From cb8f695c11b2a6e5402ca58fabcc8a17800177ee Mon Sep 17 00:00:00 2001 From: Yoshitaka Aharen Date: Fri, 24 Jun 2011 16:27:18 +0900 Subject: [PATCH 001/974] [trac1016] Modify tests of IntervalTimer not to rely on the accuracy of the timer The details are described in http://bind10.isc.org/ticket/1016 and also posted to bind10-dev at Thu, 26 May 2011 20:01:08 +0900 (Re: [bind10-dev] NetBSD timing test failure, was Re: failed unittests) --- .../asiolink/tests/interval_timer_unittest.cc | 53 ++++++------------- 1 file changed, 15 insertions(+), 38 deletions(-) diff --git a/src/lib/asiolink/tests/interval_timer_unittest.cc b/src/lib/asiolink/tests/interval_timer_unittest.cc index 8e8ef81101..51c74f26c4 100644 --- a/src/lib/asiolink/tests/interval_timer_unittest.cc +++ b/src/lib/asiolink/tests/interval_timer_unittest.cc @@ -164,24 +164,20 @@ TEST_F(IntervalTimerTest, startIntervalTimer) { itimer.setup(TimerCallBack(this), 100); EXPECT_EQ(100, itimer.getInterval()); io_service_.run(); - // reaches here after timer expired + // Control reaches here after io_service_ was stopped by TimerCallBack. + // delta: difference between elapsed time and 100 milliseconds. boost::posix_time::time_duration test_runtime = boost::posix_time::microsec_clock::universal_time() - start; EXPECT_FALSE(test_runtime.is_negative()) << "test duration " << test_runtime << " negative - clock skew?"; - boost::posix_time::time_duration delta = - test_runtime - boost::posix_time::milliseconds(100); - if (delta.is_negative()) { - delta.invert_sign(); - } - // expect TimerCallBack is called; timer_called_ is true + // Expect TimerCallBack is called; timer_called_ is true EXPECT_TRUE(timer_called_); - // expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC. - EXPECT_TRUE(delta < TIMER_MARGIN_MSEC) << - "delta " << delta.total_milliseconds() << "msec " << - ">= " << TIMER_MARGIN_MSEC.total_milliseconds(); + // Expect test_runtime is 100 milliseconds or longer. + EXPECT_TRUE(test_runtime > boost::posix_time::milliseconds(100)) << + "test runtime " << test_runtime.total_milliseconds() << + "msec " << ">= 100"; } TEST_F(IntervalTimerTest, destructIntervalTimer) { @@ -244,7 +240,7 @@ TEST_F(IntervalTimerTest, cancel) { } TEST_F(IntervalTimerTest, overwriteIntervalTimer) { - // Calling setup() multiple times updates call back function and interval. + // Call setup() multiple times to update call back function and interval. // // There are two timers: // itimer (A) @@ -266,7 +262,7 @@ TEST_F(IntervalTimerTest, overwriteIntervalTimer) { // 0 100 200 300 400 500 600 700 800 (ms) // (A) i-------------+----C----s // ^ ^stop io_service - // |change call back function + // |change call back function and interval // (B) i------------------+-------------------S // ^(stop io_service on fail) // @@ -279,30 +275,11 @@ TEST_F(IntervalTimerTest, overwriteIntervalTimer) { itimer.setup(TimerCallBackCounter(this), 300); itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400); io_service_.run(); - // reaches here after timer expired - // if interval is updated, it takes - // 400 milliseconds for TimerCallBackOverwriter - // + 100 milliseconds for TimerCallBack (stop) - // = 500 milliseconds. - // otherwise (test fails), it takes - // 400 milliseconds for TimerCallBackOverwriter - // + 400 milliseconds for TimerCallBackOverwriter (stop) - // = 800 milliseconds. - // delta: difference between elapsed time and 400 + 100 milliseconds - boost::posix_time::time_duration test_runtime = - boost::posix_time::microsec_clock::universal_time() - start; - EXPECT_FALSE(test_runtime.is_negative()) << - "test duration " << test_runtime << - " negative - clock skew?"; - boost::posix_time::time_duration delta = - test_runtime - boost::posix_time::milliseconds(400 + 100); - if (delta.is_negative()) { - delta.invert_sign(); - } - // expect callback function is updated: TimerCallBack is called + // Control reaches here after io_service_ was stopped by + // TimerCallBackCounter or TimerCallBackOverwriter. + + // Expect callback function is updated: TimerCallBack is called EXPECT_TRUE(timer_called_); - // expect interval is updated - EXPECT_TRUE(delta < TIMER_MARGIN_MSEC) << - "delta " << delta.total_milliseconds() << " msec " << - ">= " << TIMER_MARGIN_MSEC.total_milliseconds(); + // Expect interval is updated: return value of getInterval() is updated + EXPECT_EQ(itimer.getInterval(), 100); } From 421d19914af7d7a6f886c1bce084324ce9407b99 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 25 Jun 2011 16:17:57 +0200 Subject: [PATCH 002/974] [trac747] Support for logging exceptions Just that we don't need to write .arg(e.what()), .arg(e) is enough. --- src/lib/log/log_formatter.h | 8 ++++++++ src/lib/log/tests/log_formatter_unittest.cc | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h index c81d4ea21a..5f2704c0eb 100644 --- a/src/lib/log/log_formatter.h +++ b/src/lib/log/log_formatter.h @@ -180,6 +180,14 @@ public: return (*this); } + Formatter& arg(const std::exception& e) { + if (logger_) { + return (arg(e.what())); + } else { + return (*this); + } + } + }; } diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc index b91665dc80..6fda840518 100644 --- a/src/lib/log/tests/log_formatter_unittest.cc +++ b/src/lib/log/tests/log_formatter_unittest.cc @@ -123,4 +123,19 @@ TEST_F(FormatterTest, noRecurse) { EXPECT_EQ("%1 %1", outputs[0].second); } +// Test it can accept exceptions (which don't have a default conversion +// to string by themself) +TEST_F(FormatterTest, exception) { + class Ex : public std::exception { + public: + virtual const char* what() const throw() { + return "Exception test"; + } + }; + Formatter(isc::log::INFO, s("%1"), this).arg(Ex()); + ASSERT_EQ(1, outputs.size()); + EXPECT_EQ(isc::log::INFO, outputs[0].first); + EXPECT_EQ("Exception test", outputs[0].second); +} + } From a98a90d8c392ed3bd0ab51d644568cf560574112 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 25 Jun 2011 16:19:02 +0200 Subject: [PATCH 003/974] [trac747] Logging of portconfig --- src/lib/server_common/Makefile.am | 8 +++- src/lib/server_common/logger.cc | 23 ++++++++++ src/lib/server_common/logger.h | 44 +++++++++++++++++++ src/lib/server_common/portconfig.cc | 21 +++++---- .../server_common/server_common_messages.mes | 33 ++++++++++++++ src/lib/server_common/tests/run_unittests.cc | 3 ++ 6 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 src/lib/server_common/logger.cc create mode 100644 src/lib/server_common/logger.h create mode 100644 src/lib/server_common/server_common_messages.mes diff --git a/src/lib/server_common/Makefile.am b/src/lib/server_common/Makefile.am index a3063ba0f1..5e4569ff68 100644 --- a/src/lib/server_common/Makefile.am +++ b/src/lib/server_common/Makefile.am @@ -19,11 +19,17 @@ endif lib_LTLIBRARIES = libserver_common.la libserver_common_la_SOURCES = portconfig.h portconfig.cc libserver_common_la_SOURCES += keyring.h keyring.cc +libserver_common_la_SOURCES += logger.h logger.cc +nodist_libserver_common_la_SOURCES = server_common_messages.h +nodist_libserver_common_la_SOURCES += server_common_messages.cc libserver_common_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la libserver_common_la_LIBADD += $(top_builddir)/src/lib/asiolink/libasiolink.la libserver_common_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la libserver_common_la_LIBADD += $(top_builddir)/src/lib/config/libcfgclient.la libserver_common_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la libserver_common_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la +BUILT_SOURCES = server_common_messages.h server_common_messages.cc +server_common_messages.h server_common_messages.cc: server_common_messages.mes + $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/server_common/server_common_messages.mes -CLEANFILES = *.gcno *.gcda +CLEANFILES = *.gcno *.gcda messagedef.h messagedef.cc diff --git a/src/lib/server_common/logger.cc b/src/lib/server_common/logger.cc new file mode 100644 index 0000000000..0b9ab6e8ad --- /dev/null +++ b/src/lib/server_common/logger.cc @@ -0,0 +1,23 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +namespace isc { +namespace server_common { + +isc::log::Logger logger("server_common"); + +} +} diff --git a/src/lib/server_common/logger.h b/src/lib/server_common/logger.h new file mode 100644 index 0000000000..cfca1f3c45 --- /dev/null +++ b/src/lib/server_common/logger.h @@ -0,0 +1,44 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __SERVER_COMMON_LOGGER_H +#define __SERVER_COMMON_LOGGER_H + +#include +#include + +/// \file logger.h +/// \brief Server Common library global logger +/// +/// This holds the logger for the server common library. It is a private header +/// and should not be included in any publicly used header, only in local +/// cc files. + +namespace isc { +namespace server_common { + +/// \brief The logger for this library +extern isc::log::Logger logger; + +enum { + /// \brief Trace basic operations + DBG_TRACE_BASIC = 10, + /// \brief Print also values used + DBG_TRACE_VALUES = 40 +}; + +} +} + +#endif diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc index 7b2b3ddc27..83778e0e10 100644 --- a/src/lib/server_common/portconfig.cc +++ b/src/lib/server_common/portconfig.cc @@ -13,10 +13,10 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include #include #include -#include #include #include @@ -25,7 +25,6 @@ using namespace std; using namespace isc::data; using namespace isc::asiolink; using namespace isc::asiodns; -using isc::log::dlog; namespace isc { namespace server_common { @@ -43,6 +42,8 @@ parseAddresses(isc::data::ConstElementPtr addresses, ConstElementPtr addr(addrPair->get("address")); ConstElementPtr port(addrPair->get("port")); if (!addr || ! port) { + LOG_ERROR(logger, SRV_COMMON_ADDRESS_MISSING). + arg(addrPair->str()); isc_throw(BadValue, "Address must contain both the IP" "address and port"); } @@ -50,6 +51,8 @@ parseAddresses(isc::data::ConstElementPtr addresses, IOAddress(addr->stringValue()); if (port->intValue() < 0 || port->intValue() > 0xffff) { + LOG_ERROR(logger, SRV_COMMON_PORT_RANGE). + arg(port->intValue()).arg(addrPair->str()); isc_throw(BadValue, "Bad port value (" << port->intValue() << ")"); } @@ -57,11 +60,14 @@ parseAddresses(isc::data::ConstElementPtr addresses, port->intValue())); } catch (const TypeError &e) { // Better error message + LOG_ERROR(logger, SRV_COMMON_ADDRESS_TYPE). + arg(addrPair->str()); isc_throw(TypeError, "Address must be a string and port an integer"); } } } else if (addresses->getType() != Element::null) { + LOG_ERROR(logger, SRV_COMMON_ADDRESSES_NOT_LIST); isc_throw(TypeError, elemName + " config element must be a list"); } } @@ -86,10 +92,10 @@ installListenAddresses(const AddressList& newAddresses, isc::asiodns::DNSService& service) { try { - dlog("Setting listen addresses:"); + LOG_DEBUG(logger, DBG_TRACE_BASIC, SRV_COMMON_SET_LISTEN); BOOST_FOREACH(const AddressPair& addr, newAddresses) { - dlog(" " + addr.first + ":" + - boost::lexical_cast(addr.second)); + LOG_DEBUG(logger, DBG_TRACE_VALUES, SRV_COMMON_ADDRESS_VALUE). + arg(addr.first).arg(addr.second); } setAddresses(service, newAddresses); addressStore = newAddresses; @@ -108,13 +114,12 @@ installListenAddresses(const AddressList& newAddresses, * user will get error info, command control can be used to set new * address. So we just catch the exception without propagating outside */ - dlog(string("Unable to set new address: ") + e.what(), true); + LOG_ERROR(logger, SRV_COMMON_ADDRESS_FAIL).arg(e); try { setAddresses(service, addressStore); } catch (const exception& e2) { - dlog("Unable to recover from error;", true); - dlog(string("Rollback failed with: ") + e2.what(), true); + LOG_FATAL(logger, SRV_COMMON_ADDRESS_UNRECOVERABLE).arg(e2); } //Anyway the new configure has problem, we need to notify configure //manager the new configure doesn't work diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes new file mode 100644 index 0000000000..be68646a41 --- /dev/null +++ b/src/lib/server_common/server_common_messages.mes @@ -0,0 +1,33 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +$NAMESPACE isc::server_common + +# \brief Messages for the server_common library + +% SRV_COMMON_ADDRESS_MISSING Address specification is missing "address" or "port" element in %1 + +% SRV_COMMON_PORT_RANGE Port out of valid range (%1 in %2) + +% SRV_COMMON_ADDRESS_TYPE Address specification type is invalid in %1 + +% SRV_COMMON_ADDRESSES_NOT_LIST The address and port specification is not a list + +% SRV_COMMON_SET_LISTEN Setting addresses to listen to + +% SRV_COMMON_ADDRESS_VALUE Address to set: %1#%2 + +% SRV_COMMON_ADDRESS_FAIL Failed to listen on addresses (%1) + +% SRV_COMMON_ADDRESS_UNRECOVERABLE Failed to recover original addresses also (%2) diff --git a/src/lib/server_common/tests/run_unittests.cc b/src/lib/server_common/tests/run_unittests.cc index b982ef3b80..860cb77ba9 100644 --- a/src/lib/server_common/tests/run_unittests.cc +++ b/src/lib/server_common/tests/run_unittests.cc @@ -16,6 +16,7 @@ #include #include +#include #include @@ -23,5 +24,7 @@ int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + return (isc::util::unittests::run_all()); } From 6b48fd891428674ecf5eaaa083bcf5b843deabc5 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 25 Jun 2011 16:26:20 +0200 Subject: [PATCH 004/974] [trac747] Logging in keyring --- src/lib/server_common/keyring.cc | 4 ++++ .../server_common/server_common_messages.mes | 22 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/lib/server_common/keyring.cc b/src/lib/server_common/keyring.cc index b60e796f1c..791f20d427 100644 --- a/src/lib/server_common/keyring.cc +++ b/src/lib/server_common/keyring.cc @@ -13,6 +13,7 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include using namespace isc::dns; using namespace isc::data; @@ -31,6 +32,7 @@ updateKeyring(const std::string&, ConstElementPtr data, const isc::config::ConfigData&) { ConstElementPtr list(data->get("keys")); KeyringPtr load(new TSIGKeyRing); + LOG_DEBUG(logger, DBG_TRACE_BASIC, SRV_COMMON_KEYS_UPDATE); // Note that 'data' only contains explicitly configured config parameters. // So if we use the default list is NULL, rather than an empty list, and @@ -50,6 +52,7 @@ initKeyring(config::ModuleCCSession& session) { // We are already initialized return; } + LOG_DEBUG(logger, DBG_TRACE_BASIC, SRV_COMMON_KEYS_INIT); session.addRemoteConfig("tsig_keys", updateKeyring, false); } @@ -59,6 +62,7 @@ deinitKeyring(config::ModuleCCSession& session) { // Not initialized, ignore it return; } + LOG_DEBUG(logger, DBG_TRACE_BASIC, SRV_COMMON_KEYS_DEINIT); keyring.reset(); session.removeRemoteConfig("tsig_keys"); } diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes index be68646a41..507a8c0ab7 100644 --- a/src/lib/server_common/server_common_messages.mes +++ b/src/lib/server_common/server_common_messages.mes @@ -16,18 +16,24 @@ $NAMESPACE isc::server_common # \brief Messages for the server_common library -% SRV_COMMON_ADDRESS_MISSING Address specification is missing "address" or "port" element in %1 +% SRV_COMMON_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 -% SRV_COMMON_PORT_RANGE Port out of valid range (%1 in %2) +% SRV_COMMON_PORT_RANGE port out of valid range (%1 in %2) -% SRV_COMMON_ADDRESS_TYPE Address specification type is invalid in %1 +% SRV_COMMON_ADDRESS_TYPE address specification type is invalid in %1 -% SRV_COMMON_ADDRESSES_NOT_LIST The address and port specification is not a list +% SRV_COMMON_ADDRESSES_NOT_LIST the address and port specification is not a list -% SRV_COMMON_SET_LISTEN Setting addresses to listen to +% SRV_COMMON_SET_LISTEN setting addresses to listen to -% SRV_COMMON_ADDRESS_VALUE Address to set: %1#%2 +% SRV_COMMON_ADDRESS_VALUE address to set: %1#%2 -% SRV_COMMON_ADDRESS_FAIL Failed to listen on addresses (%1) +% SRV_COMMON_ADDRESS_FAIL failed to listen on addresses (%1) -% SRV_COMMON_ADDRESS_UNRECOVERABLE Failed to recover original addresses also (%2) +% SRV_COMMON_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2) + +% SRV_COMMON_KEYS_UPDATE updating TSIG keyring + +% SRV_COMMON_KEYS_INIT initializing TSIG keyring + +% SRV_COMMON_KEYS_DEINIT deinitilizing TSIG keyring From cb737e68ceac8238844fe2b8b9bc7feea23b4004 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 25 Jun 2011 16:43:59 +0200 Subject: [PATCH 005/974] [trac747] Message descriptions --- .../server_common/server_common_messages.mes | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes index 507a8c0ab7..b449475619 100644 --- a/src/lib/server_common/server_common_messages.mes +++ b/src/lib/server_common/server_common_messages.mes @@ -17,23 +17,59 @@ $NAMESPACE isc::server_common # \brief Messages for the server_common library % SRV_COMMON_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 +This points to an error in configuration. Some address specification is missing +either the address or port part and therefore can not be used. % SRV_COMMON_PORT_RANGE port out of valid range (%1 in %2) +This points to an error in configuration. The port in some address +specification is out of the valid range (0-65535). % SRV_COMMON_ADDRESS_TYPE address specification type is invalid in %1 +This points to an error in configuration. Some address specification is +malformed. The address part must be a string (and a valid IP address, either +IPv4 or IPv6) and the port must be integer (in the valid range). % SRV_COMMON_ADDRESSES_NOT_LIST the address and port specification is not a list +This points to an error in configuration. What was supposed to be a list of +IP address - port pairs isn't a list at all but something else. % SRV_COMMON_SET_LISTEN setting addresses to listen to +Debug message, noting that the server is about to start listening on a +different set of IP addresses and ports. % SRV_COMMON_ADDRESS_VALUE address to set: %1#%2 +Debug message. This lists one address and port value of the set of +addresses we are going to listen on (eg. there will be one log message +per pair). This appears only after SRV_COMMON_SET_LISTEN, but might +be hidden, as it has higher debug level. % SRV_COMMON_ADDRESS_FAIL failed to listen on addresses (%1) +The server failed to bind to one of the address/port pair it should according +to configuration, for reason listed in the message (usually because that pair +is already used by other service or missing privileges). The server will try +to recover and bind the address/port pairs it was listening before. % SRV_COMMON_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2) +The recovery of old addresses after SRV_COMMON_ADDRESS_FAIL also failed (for +the reason listed). This should not happen. + +We would really like to terminate the server right now, but considering that +the server needs reconfiguration and it is not possible to reconfigure a server +without it being run, we leave it running in inconsistent and broken state. +It probably will not work, but will allow the reconfiguration. We hope to +update the configuration system so we can really crash the server happily +instead. % SRV_COMMON_KEYS_UPDATE updating TSIG keyring +Debug message indicating that the server is initializing global TSIG keyring. +This should be seen only at server start. % SRV_COMMON_KEYS_INIT initializing TSIG keyring +Debug message indicating new keyring is being loaded from configuration (either +on startup or as a result of configuration update). % SRV_COMMON_KEYS_DEINIT deinitilizing TSIG keyring +Debug message indicating that the server is deinilizing the TSIG keyring. This +could be seen at server shutdown only, but usually not even there, as leaving +the TSIG in memory until the real shutdown and memory reclamation by OS is +harmless, so we don't usually do it. From 440524d524bde6ea17ec64b427e259f3bd08757a Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 25 Jun 2011 16:48:30 +0200 Subject: [PATCH 006/974] [trac747] Sort the message definitions --- .../server_common/server_common_messages.mes | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes index b449475619..d22946830a 100644 --- a/src/lib/server_common/server_common_messages.mes +++ b/src/lib/server_common/server_common_messages.mes @@ -16,39 +16,25 @@ $NAMESPACE isc::server_common # \brief Messages for the server_common library -% SRV_COMMON_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 -This points to an error in configuration. Some address specification is missing -either the address or port part and therefore can not be used. - -% SRV_COMMON_PORT_RANGE port out of valid range (%1 in %2) -This points to an error in configuration. The port in some address -specification is out of the valid range (0-65535). - -% SRV_COMMON_ADDRESS_TYPE address specification type is invalid in %1 -This points to an error in configuration. Some address specification is -malformed. The address part must be a string (and a valid IP address, either -IPv4 or IPv6) and the port must be integer (in the valid range). - % SRV_COMMON_ADDRESSES_NOT_LIST the address and port specification is not a list This points to an error in configuration. What was supposed to be a list of IP address - port pairs isn't a list at all but something else. -% SRV_COMMON_SET_LISTEN setting addresses to listen to -Debug message, noting that the server is about to start listening on a -different set of IP addresses and ports. - -% SRV_COMMON_ADDRESS_VALUE address to set: %1#%2 -Debug message. This lists one address and port value of the set of -addresses we are going to listen on (eg. there will be one log message -per pair). This appears only after SRV_COMMON_SET_LISTEN, but might -be hidden, as it has higher debug level. - % SRV_COMMON_ADDRESS_FAIL failed to listen on addresses (%1) The server failed to bind to one of the address/port pair it should according to configuration, for reason listed in the message (usually because that pair is already used by other service or missing privileges). The server will try to recover and bind the address/port pairs it was listening before. +% SRV_COMMON_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 +This points to an error in configuration. Some address specification is missing +either the address or port part and therefore can not be used. + +% SRV_COMMON_ADDRESS_TYPE address specification type is invalid in %1 +This points to an error in configuration. Some address specification is +malformed. The address part must be a string (and a valid IP address, either +IPv4 or IPv6) and the port must be integer (in the valid range). + % SRV_COMMON_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2) The recovery of old addresses after SRV_COMMON_ADDRESS_FAIL also failed (for the reason listed). This should not happen. @@ -60,16 +46,30 @@ It probably will not work, but will allow the reconfiguration. We hope to update the configuration system so we can really crash the server happily instead. -% SRV_COMMON_KEYS_UPDATE updating TSIG keyring -Debug message indicating that the server is initializing global TSIG keyring. -This should be seen only at server start. - -% SRV_COMMON_KEYS_INIT initializing TSIG keyring -Debug message indicating new keyring is being loaded from configuration (either -on startup or as a result of configuration update). +% SRV_COMMON_ADDRESS_VALUE address to set: %1#%2 +Debug message. This lists one address and port value of the set of +addresses we are going to listen on (eg. there will be one log message +per pair). This appears only after SRV_COMMON_SET_LISTEN, but might +be hidden, as it has higher debug level. % SRV_COMMON_KEYS_DEINIT deinitilizing TSIG keyring Debug message indicating that the server is deinilizing the TSIG keyring. This could be seen at server shutdown only, but usually not even there, as leaving the TSIG in memory until the real shutdown and memory reclamation by OS is harmless, so we don't usually do it. + +% SRV_COMMON_KEYS_INIT initializing TSIG keyring +Debug message indicating new keyring is being loaded from configuration (either +on startup or as a result of configuration update). + +% SRV_COMMON_KEYS_UPDATE updating TSIG keyring +Debug message indicating that the server is initializing global TSIG keyring. +This should be seen only at server start. + +% SRV_COMMON_PORT_RANGE port out of valid range (%1 in %2) +This points to an error in configuration. The port in some address +specification is out of the valid range (0-65535). + +% SRV_COMMON_SET_LISTEN setting addresses to listen to +Debug message, noting that the server is about to start listening on a +different set of IP addresses and ports. From d137040ad98f7203cd440ca8b449a84f048af6fd Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 26 Jun 2011 10:13:33 +0200 Subject: [PATCH 007/974] [trac747] Fix makefile --- src/lib/server_common/Makefile.am | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/server_common/Makefile.am b/src/lib/server_common/Makefile.am index 5e4569ff68..522b360a15 100644 --- a/src/lib/server_common/Makefile.am +++ b/src/lib/server_common/Makefile.am @@ -32,4 +32,6 @@ BUILT_SOURCES = server_common_messages.h server_common_messages.cc server_common_messages.h server_common_messages.cc: server_common_messages.mes $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/server_common/server_common_messages.mes -CLEANFILES = *.gcno *.gcda messagedef.h messagedef.cc +EXTRA_DIST = server_common_messages.mes + +CLEANFILES = *.gcno *.gcda server_common_messages.h server_common_messages.cc From a91e1274bc7cd044b9e6c254a100a0aff73dcc2b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 28 Jun 2011 11:11:11 +0200 Subject: [PATCH 008/974] [trac741] Logging support for cache --- src/lib/cache/Makefile.am | 11 ++++++++- src/lib/cache/cache_messages.mes | 15 ++++++++++++ src/lib/cache/logger.cc | 23 +++++++++++++++++ src/lib/cache/logger.h | 42 ++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/lib/cache/cache_messages.mes create mode 100644 src/lib/cache/logger.cc create mode 100644 src/lib/cache/logger.h diff --git a/src/lib/cache/Makefile.am b/src/lib/cache/Makefile.am index bfbe24a0b2..9871a5ed40 100644 --- a/src/lib/cache/Makefile.am +++ b/src/lib/cache/Makefile.am @@ -31,5 +31,14 @@ libcache_la_SOURCES += cache_entry_key.h cache_entry_key.cc libcache_la_SOURCES += rrset_copy.h rrset_copy.cc libcache_la_SOURCES += local_zone_data.h local_zone_data.cc libcache_la_SOURCES += message_utility.h message_utility.cc +libcache_la_SOURCES += logger.h logger.cc +nodist_libcache_la_SOURCES = cache_messages.cc cache_messages.h -CLEANFILES = *.gcno *.gcda +BUILT_SOURCES = cache_messages.cc cache_messages.h + +cache_messages.cc cache_messages.h: cache_messages.mes + $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/cache/cache_messages.mes + +CLEANFILES = *.gcno *.gcda cache_messages.cc cache_messages.h + +EXTRA_DIST = cache_messages.mes diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes new file mode 100644 index 0000000000..e1fbf368d9 --- /dev/null +++ b/src/lib/cache/cache_messages.mes @@ -0,0 +1,15 @@ +# Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +$NAMESPACE isc::cache diff --git a/src/lib/cache/logger.cc b/src/lib/cache/logger.cc new file mode 100644 index 0000000000..bcf37ecccf --- /dev/null +++ b/src/lib/cache/logger.cc @@ -0,0 +1,23 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +namespace isc { +namespace datasrc { + +isc::log::Logger logger("cache"); + +} +} diff --git a/src/lib/cache/logger.h b/src/lib/cache/logger.h new file mode 100644 index 0000000000..46a48b8e08 --- /dev/null +++ b/src/lib/cache/logger.h @@ -0,0 +1,42 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __DATASRC_LOGGER_H +#define __DATASRC_LOGGER_H + +#include +#include + +/// \file logger.h +/// \brief Cache library global logger +/// +/// This holds the logger for the cache library. It is a private header +/// and should not be included in any publicly used header, only in local +/// cc files. + +namespace isc { +namespace datasrc { + +/// \brief The logger for this library +extern isc::log::Logger logger; + +enum { + /// \brief Trace basic operations + DBG_TRACE_BASIC = 10, +}; + +} +} + +#endif From b9ad6d4babd3e10f1c13140e53d60181681a5def Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 29 Jun 2011 10:21:47 +0200 Subject: [PATCH 009/974] [trac1072] fix issue of JSON parser non-failure with input "{" --- src/lib/cc/data.cc | 3 +++ src/lib/cc/tests/data_unittests.cc | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index 6f7d4a2e3d..3f630ef0d2 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -454,6 +454,9 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line, ElementPtr map = Element::createMap(); skip_chars(in, " \t\n", line, pos); char c = in.peek(); + if (c == EOF) { + throwJSONError(std::string("Unterminated map, or } expected"), file, line, pos); + } if (c == '}') { // empty map, skip closing curly c = in.get(); diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index 2536682288..2f0a61ca0d 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -396,9 +396,19 @@ TEST(Element, to_and_from_wire) { EXPECT_EQ("1", Element::fromWire(ss, 1)->str()); // Some malformed JSON input + EXPECT_THROW(Element::fromJSON("{ "), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{ \"a\" "), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{ \"a\": "), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{ \"a\": \"b\""), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{ \"a\": {"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{ \"a\": {}"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{ \"a\": []"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{ \"a\": [ }"), isc::data::JSONError); EXPECT_THROW(Element::fromJSON("{\":"), isc::data::JSONError); EXPECT_THROW(Element::fromJSON("]"), isc::data::JSONError); EXPECT_THROW(Element::fromJSON("[ 1, 2, }"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("[ 1, 2, {}"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("[ 1, 2, { ]"), isc::data::JSONError); } ConstElementPtr From af6b421d426357550e818d6fee79dd559382ae46 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 29 Jun 2011 11:08:35 +0200 Subject: [PATCH 010/974] [trac760] add (empty) messages file --- src/bin/cmdctl/Makefile.am | 10 +++++++--- src/bin/cmdctl/cmdctl_messages.mes | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 src/bin/cmdctl/cmdctl_messages.mes diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am index 97a64ff088..fcd23f8662 100644 --- a/src/bin/cmdctl/Makefile.am +++ b/src/bin/cmdctl/Makefile.am @@ -3,6 +3,7 @@ SUBDIRS = . tests pkglibexecdir = $(libexecdir)/@PACKAGE@ pkglibexec_SCRIPTS = b10-cmdctl +pyexec_DATA = cmdctl_messages.py b10_cmdctldir = $(pkgdatadir) @@ -18,10 +19,10 @@ b10_cmdctl_DATA += cmdctl.spec EXTRA_DIST = $(CMDCTL_CONFIGURATIONS) -CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec +CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec cmdctl_messages.py cmdctl_messages.pyc man_MANS = b10-cmdctl.8 -EXTRA_DIST += $(man_MANS) b10-cmdctl.xml +EXTRA_DIST += $(man_MANS) b10-cmdctl.xml cmdctl_messages.mes if ENABLE_MAN @@ -33,8 +34,11 @@ endif cmdctl.spec: cmdctl.spec.pre $(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@ +cmdctl_messages.py: cmdctl_messages.mes + $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/cmdctl/cmdctl_messages.mes + # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix -b10-cmdctl: cmdctl.py +b10-cmdctl: cmdctl.py cmdctl_messages.py $(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@ chmod a+x $@ diff --git a/src/bin/cmdctl/cmdctl_messages.mes b/src/bin/cmdctl/cmdctl_messages.mes new file mode 100644 index 0000000000..027b106013 --- /dev/null +++ b/src/bin/cmdctl/cmdctl_messages.mes @@ -0,0 +1,16 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# No namespace declaration - these constants go in the global namespace +# of the cmdctl_messages python module. From 4dc1b4bf31b25256bac76baca6e8af71e11cc83a Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 29 Jun 2011 14:33:22 +0200 Subject: [PATCH 011/974] [trac760] replace logger calls Also did one security-related fix; if you try to log in with a bad username or password, the message sent back is now the same (it used to send a different error depending on whether the username or password was bad) --- src/bin/cmdctl/cmdctl.py.in | 59 ++++++++++++++------------ src/bin/cmdctl/cmdctl_messages.mes | 65 +++++++++++++++++++++++++++++ src/bin/cmdctl/tests/cmdctl_test.py | 4 +- 3 files changed, 100 insertions(+), 28 deletions(-) diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in index f1c1021bce..232fad6c4d 100755 --- a/src/bin/cmdctl/cmdctl.py.in +++ b/src/bin/cmdctl/cmdctl.py.in @@ -47,6 +47,18 @@ import isc.net.parse from optparse import OptionParser, OptionValueError from hashlib import sha1 from isc.util import socketserver_mixin +from cmdctl_messages import * + +# TODO: these debug-levels are hard-coded here; we are planning on +# creating a general set of debug levels, see ticket #1074. When done, +# we should remove these values and use the general ones in the +# logger.debug calls + +# Debug level for communication with BIND10 +DBG_CMDCTL_MESSAGING = 30 + +isc.log.init("b10-cmdctl") +logger = isc.log.Logger("cmdctl") try: import threading @@ -173,7 +185,8 @@ class SecureHTTPRequestHandler(http.server.BaseHTTPRequestHandler): if not user_name: return False, ["need user name"] if not self.server.get_user_info(user_name): - return False, ["user doesn't exist"] + logger.info(CMDCTL_NO_SUCH_USER, user_name) + return False, ["username or password error"] user_pwd = user_info.get('password') if not user_pwd: @@ -181,7 +194,8 @@ class SecureHTTPRequestHandler(http.server.BaseHTTPRequestHandler): local_info = self.server.get_user_info(user_name) pwd_hashval = sha1((user_pwd + local_info[1]).encode()) if pwd_hashval.hexdigest() != local_info[0]: - return False, ["password doesn't match"] + logger.info(CMDCTL_BAD_PASSWORD, user_name) + return False, ["username or password error"] return True, None @@ -238,7 +252,8 @@ class CommandControl(): self._cc = isc.cc.Session() self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, - self.command_handler) + self.command_handler, + None, True) self._module_name = self._module_cc.get_module_spec().get_module_name() self._cmdctl_config_data = self._module_cc.get_full_config() self._module_cc.start() @@ -281,7 +296,7 @@ class CommandControl(): errstr = 'unknown config item: ' + key if errstr != None: - self.log_info('Fail to apply config data, ' + errstr) + logger.error(CMDCTL_BAD_CONFIG_DATA, errstr); return ccsession.create_answer(1, errstr) return ccsession.create_answer(0) @@ -387,8 +402,8 @@ class CommandControl(): '''Send the command from bindctl to proper module. ''' errstr = 'unknown error' answer = None - if self._verbose: - self.log_info("Begin send command '%s' to module '%s'" %(command_name, module_name)) + logger.debug(DBG_CMDCTL_MESSAGING, CMDCTL_SEND_COMMAND, + command_name, module_name) if module_name == self._module_name: # Process the command sent to cmdctl directly. @@ -396,15 +411,14 @@ class CommandControl(): else: msg = ccsession.create_command(command_name, params) seq = self._cc.group_sendmsg(msg, module_name) + logger.debug(DBG_CMDCTL_MESSAGING, CMDCTL_COMMAND_SENT, + command_name, module_name) #TODO, it may be blocked, msqg need to add a new interface waiting in timeout. try: answer, env = self._cc.group_recvmsg(False, seq) except isc.cc.session.SessionTimeout: errstr = "Module '%s' not responding" % module_name - if self._verbose: - self.log_info("Finish send command '%s' to module '%s'" % (command_name, module_name)) - if answer: try: rcode, arg = ccsession.parse_answer(answer) @@ -415,16 +429,13 @@ class CommandControl(): else: return rcode, {} else: - # TODO: exception errstr = str(answer['result'][1]) except ccsession.ModuleCCSessionError as mcse: errstr = str("Error in ccsession answer:") + str(mcse) - self.log_info(errstr) + + logger.error(CMDCTL_COMMAND_ERROR, conmmand_name, module_name, errstr) return 1, {'error': errstr} - def log_info(self, msg): - sys.stdout.write("[b10-cmdctl] %s\n" % str(msg)) - def get_cmdctl_config_data(self): ''' If running in source code tree, use keyfile, certificate and user accounts file in source code. ''' @@ -481,14 +492,15 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn, for row in reader: self._user_infos[row[0]] = [row[1], row[2]] except (IOError, IndexError) as e: - self.log_info("Fail to read user database, %s" % e) + logger.error(CMDCTL_USER_DATABASE_READ_ERROR, + accounts_file, e) finally: if csvfile: csvfile.close() self._accounts_file = accounts_file if len(self._user_infos) == 0: - self.log_info("Fail to get user information, will deny any user") + logger.error(CMDCTL_NO_USER_ENTRIES_READ) def get_user_info(self, username): '''Get user's salt and hashed string. If the user @@ -520,7 +532,7 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn, ssl_version = ssl.PROTOCOL_SSLv23) return ssl_sock except (ssl.SSLError, CmdctlException) as err : - self.log_info("Deny client's connection because %s" % str(err)) + logger.info(CMDCTL_SSL_ERROR_USER_DENIED, err) self.close_request(sock) # raise socket error to finish the request raise socket.error @@ -547,9 +559,6 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn, def send_command_to_module(self, module_name, command_name, params): return self.cmdctl.send_command_with_check(module_name, command_name, params) - def log_info(self, msg): - sys.stdout.write("[b10-cmdctl] %s\n" % str(msg)) - httpd = None def signal_handler(signal, frame): @@ -607,15 +616,13 @@ if __name__ == '__main__': run(options.addr, options.port, options.idle_timeout, options.verbose) result = 0 except isc.cc.SessionError as err: - sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, " - "is the command channel daemon running?\n") + logger.fatal(CMDCTL_CC_SESSION_ERROR, err) except isc.cc.SessionTimeout: - sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, " - "is the configuration manager running?\n") + logger.fatal(CMDCTL_CC_SESSION_TIMEOUT) except KeyboardInterrupt: - sys.stderr.write("[b10-cmdctl] exit from Cmdctl\n") + logger.info(CMDCTL_STOPPED_BY_KEYBOARD) except CmdctlException as err: - sys.stderr.write("[b10-cmdctl] " + str(err) + "\n") + logger.fatal(CMDCTL_UNCAUGHT_EXCEPTION, err); if httpd: httpd.shutdown() diff --git a/src/bin/cmdctl/cmdctl_messages.mes b/src/bin/cmdctl/cmdctl_messages.mes index 027b106013..c3755bcf71 100644 --- a/src/bin/cmdctl/cmdctl_messages.mes +++ b/src/bin/cmdctl/cmdctl_messages.mes @@ -14,3 +14,68 @@ # No namespace declaration - these constants go in the global namespace # of the cmdctl_messages python module. + +% CMDCTL_BAD_CONFIG_DATA error in config data: %1 +There was an error reading the updated configuration data. The specific +error is printed. + +% CMDCTL_BAD_PASSWORD Bad password for user: %1 +A login attempt was made to b10-cmdctl, but the password was wrong. +Users can be managed with the tool b10-cmdctl-usermgr. + +% CMDCTL_CC_SESSION_ERROR error reading from cc channel: %1 +There was a problem reading from the command and control channel. The +most likely cause is that the msgq daemon is not running. + +% CMDCTL_CC_SESSION_TIMEOUT timeout on cc channel +A timeout occurred when waiting for essential data from the cc session. +This usually occurs when b10-cfgmgr is not running or not responding. +Since we are waiting for essential information, this is a fatal error, +and the cmdctl daemon will now shut down. + +% CMDCTL_COMMAND_ERROR Error in command %1 to module %2: %3 +An error was encountered sending the given command to the given module. +Either there was a communication problem with the module, or the module +was not able to process the command, and sent back an error. The +specific error is printed in the message. + +% CMDCTL_COMMAND_SENT Command '%1' to module '%2' was sent +This debug message indicates that the given command is being sent to +the given module. + +% CMDCTL_NO_SUCH_USER username not found in user database: %1 +A login attempt was made to b10-cmdctl, but the username was not known. +Users can be added with the tool b10-cmdctl-usermgr. + +% CMDCTL_NO_USER_ENTRIES_READ failed to read user information, all users will be denied +The b10-cmdctl daemon was unable to find any user data in the user +database file. Either it was unable to read the file (in which case +this message follows a message CMDCTL_USER_DATABASE_READ_ERROR +containing a specific error), or the file was empty. Users can be added +with the tool b10-cmdctl-usermgr. + +% CMDCTL_SEND_COMMAND Sending command %1 to module %2 +This debug message indicates that the given command is being sent to +the given module. + +% CMDCTL_SSL_ERROR_USER_DENIED User denied because of SSL error: %1 +The user was denied because the SSL connection could not successfully +be set up. The specific error is given in the log message. Possible +causes may be that the ssl request itself was bad, or the key or +certificate file had a problem. + +% CMDCTL_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down +There was a keyboard interrupt signal to stop the cmdctl daemon. The +daemon will now shut down. + +% CMDCTL_UNCAUGHT_EXCEPTION uncaught exception: %1 +The b10-cdmctl daemon encountered an uncaught exception and +will now shut down. This is indicative of a programming error and +should not happen under normal circumstances. The exception message +is printed. + +% CMDCTL_USER_DATABASE_READ_ERROR failed to read user database file %1: %2 +The b10-cmdctl daemon was unable to read the user database file. The +file may be unreadable for the daemon, or it may be corrupted. In the +latter case, it can be recreated with b10-cmdctl-usermgr. The specific +error is printed in the log message. diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py index 5463c36c2a..e77c529299 100644 --- a/src/bin/cmdctl/tests/cmdctl_test.py +++ b/src/bin/cmdctl/tests/cmdctl_test.py @@ -173,7 +173,7 @@ class TestSecureHTTPRequestHandler(unittest.TestCase): self.handler.server._user_infos['root'] = ['aa', 'aaa'] ret, msg = self.handler._check_user_name_and_pwd() self.assertFalse(ret) - self.assertEqual(msg, ['password doesn\'t match']) + self.assertEqual(msg, ['username or password error']) def test_check_user_name_and_pwd_2(self): user_info = {'username':'root', 'password':'abc123'} @@ -214,7 +214,7 @@ class TestSecureHTTPRequestHandler(unittest.TestCase): ret, msg = self.handler._check_user_name_and_pwd() self.assertFalse(ret) - self.assertEqual(msg, ['user doesn\'t exist']) + self.assertEqual(msg, ['username or password error']) def test_do_POST(self): self.handler.headers = {} From 839d23249ae0d1722b51d87195b92cad40e6d78c Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 29 Jun 2011 19:08:56 +0100 Subject: [PATCH 012/974] [trac1071] Add destination selection to initLogger Add support of B10_LOGGER_DESTINATION to choose logging destination for unit tests, and add tests to check that they work. Also update some documentation. --- configure.ac | 4 +- src/bin/resolver/Makefile.am | 2 + src/lib/log/README | 82 ++++++++++++++++ src/lib/log/logger_support.cc | 69 +++++++++++++- src/lib/log/logger_support.h | 19 +++- src/lib/log/tests/Makefile.am | 15 ++- src/lib/log/tests/init_logger_test.cc | 41 ++++++++ src/lib/log/tests/init_logger_test.sh.in | 115 +++++++++++++++++++++++ src/lib/log/tests/severity_test.sh.in | 2 +- 9 files changed, 340 insertions(+), 9 deletions(-) create mode 100644 src/lib/log/tests/init_logger_test.cc create mode 100755 src/lib/log/tests/init_logger_test.sh.in diff --git a/configure.ac b/configure.ac index 348708fde1..8bd37a2347 100644 --- a/configure.ac +++ b/configure.ac @@ -920,6 +920,7 @@ AC_OUTPUT([doc/version.ent 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/init_logger_test.sh src/lib/log/tests/local_file_test.sh src/lib/log/tests/severity_test.sh src/lib/log/tests/tempdir.h @@ -949,9 +950,10 @@ AC_OUTPUT([doc/version.ent chmod +x src/bin/msgq/tests/msgq_test chmod +x src/lib/dns/gen-rdatacode.py 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/init_logger_test.sh + chmod +x src/lib/log/tests/local_file_test.sh chmod +x src/lib/log/tests/severity_test.sh chmod +x src/lib/util/python/mkpywrapper.py chmod +x src/lib/python/isc/log/tests/log_console.py diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am index bce8307515..2cbf140bd6 100644 --- a/src/bin/resolver/Makefile.am +++ b/src/bin/resolver/Makefile.am @@ -59,6 +59,7 @@ nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libdns++.la b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la +b10_resolver_LDADD += $(top_builddir)/src/lib/util/libutil.la b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la @@ -67,6 +68,7 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libcache.la b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la +b10_resolver_LDADD += $(top_builddir)/src/lib/acl/libacl.la b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o b10_resolver_LDFLAGS = -pthread diff --git a/src/lib/log/README b/src/lib/log/README index d854dce0ba..cd7cd72e25 100644 --- a/src/lib/log/README +++ b/src/lib/log/README @@ -410,6 +410,44 @@ logger "pkt-auth".) As the loggers are independent and the severity levels independent, fine-tuning of what and what is not recorded can be achieved. +Logging Initialization +====================== +In all cases, if an attempt is made to use a logging method before the logging +has been initialized, the program will terminate with a LoggingNotInitialized +call. + +C++ +--- +Logging Initialization is carried out by calling initLogger(). There are two +variants to the call, one for use by production programs and one for use by +unit tests. + +Variant #1, Used by Production Programs +--------------------------------------- + void initLogger(const std::string& root, + isc::log::Severity severity = isc::log::INFO, + int dbglevel = 0, const char* file = NULL); + +This is the call that should be used by production programs: + +root +Name of the program (e.g. "b10-auth"). This is also the name of the root +logger and is used when configuring logging. + +severity +Default severity that the program will start logging with. Although this may +be overridden when the program obtains its configuration from the configuration +database, this is the severity that it used until then. (This may be set by +a command-line parameter.) + +dbglevel +The debug level used if "severity" is set to isc::log::DEBUG. + +file +The name of a local message file. This will be read and its defintitions used +to replace the compiled-in text of the messages. + + Notes ===== @@ -419,3 +457,47 @@ in both the message compiler and the server; in the server it is used when the server starts up (or when triggered by a command) to read in a message file to overwrite the internal dictionary. Writing it in C++ means there is only one piece of code that does this functionality. + + +Variant #2, Used by Unit Tests +------------------------------ + void initLogger() + +This is the call that should be used by unit tests. In this variant, all the +options are supplied by environment variables. (It should not be used for +production programs to avoid the chance that the program operation is affected +by inadvertantly-defined environment variables.) + +The environment variables are: + +B10_LOGGER_ROOT +Sets the "root" for the unit test. If not defined, the name "bind10" is used. + +B10_LOGGER_SEVERITY +The severity to set for the root logger in the unit test. Valid values are +"DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "NONE". If not defined, "INFO" +is used. + +B10_LOGGER_DBGLEVEL +If B10_LOGGER_SEVERITY is set to "DEBUG", the debug level. This can be a +number between 0 and 99, and defaults to 0. + +B10_LOGGER_LOCALMSG +If defined, points to a local message file. The default is not to use a local +message file. + +B10_LOGGER_DESTINATION +The location to which log message are written. This can be one of: + + stdout Message are written to stdout + stderr Messages are written to stderr + syslog[:facility] Messages are written to syslog. If the optional + "facility" is used, the messages are written using + that facility. (This defaults to "local7" if not + specified) + Anything else Interpreted as the name of a file to which output + is appended. If the file does not exist, a new one + is opened. + +In the case of "stdout", "stderr" and "syslog", they must be written exactly +as is - no leading or trailing spaces, and in lower-case. diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc index 73323a03f7..eac2fbbe8b 100644 --- a/src/lib/log/logger_support.cc +++ b/src/lib/log/logger_support.cc @@ -31,7 +31,9 @@ #include #include +#include #include +#include using namespace std; @@ -40,6 +42,68 @@ namespace { // Flag to hold logging initialization state. bool logging_init_state = false; + +// Set logging destination according to the setting of B10_LOGGER_DESTINATION. +// (See header for initLogger() for more details.) +void +setDestination(const char* root, const isc::log::Severity severity, + const int dbglevel) { + + using namespace isc::log; + + // Constants: + static const string STDOUT = "stdout"; + static const string STDERR = "stderr"; + static const string SYSLOG = "syslog"; + static const string SYSLOG_COLON = "syslog:"; + + + const char* destination = getenv("B10_LOGGER_DESTINATION"); + if (destination != NULL) { + + // Destination is present, adjust root logger destination to it. + LoggerSpecification spec(root, severity, dbglevel); + OutputOption option; + + const string dest = destination; + if (dest == STDOUT) { + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDOUT; + + } else if (dest == STDERR) { + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDERR; + + } else if (dest == SYSLOG) { + option.destination = OutputOption::DEST_SYSLOG; + option.facility = "local0"; + + } else if (dest.find(SYSLOG_COLON) == 0) { + option.destination = OutputOption::DEST_SYSLOG; + // Must take account of the string actually being "syslog:". + if (dest == SYSLOG_COLON) { + cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " << + SYSLOG_COLON << " is invalid, " << SYSLOG << + " will be used instead\n"; + option.facility = "local0"; + } else { + // Everything else is the facility name + option.facility = dest.substr(SYSLOG_COLON.size()); + } + + } else { + // Not a recognised destination, assume a file. + option.destination = OutputOption::DEST_FILE; + option.filename = dest; + } + + // ... and set it. + spec.addOutputOption(option); + LoggerManager manager; + manager.process(spec); + } +} + } // Anonymous namespace namespace isc { @@ -115,11 +179,14 @@ void initLogger(isc::log::Severity severity, int dbglevel) { } } - /// Set the local message file + // Set the local message file const char* localfile = getenv("B10_LOGGER_LOCALMSG"); // Initialize logging initLogger(root, severity, dbglevel, localfile); + + // Now set the destination + setDestination(root, severity, dbglevel); } } // namespace log diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h index 4bc8acc195..cf83abcb32 100644 --- a/src/lib/log/logger_support.h +++ b/src/lib/log/logger_support.h @@ -68,26 +68,37 @@ void initLogger(const std::string& root, /// Performs run-time initialization of the logger via the setting of /// environment variables. These are: /// -/// B10_LOGGER_ROOT +/// - B10_LOGGER_ROOT\n /// Name of the root logger. If not given, the string "bind10" will be used. /// -/// B10_LOGGER_SEVERITY +/// - B10_LOGGER_SEVERITY\n /// Severity of messages that will be logged. This must be one of the strings /// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case /// and must not contain leading or trailing spaces.) If not specified (or if /// specified but incorrect), the default passed as argument to this function /// (currently INFO) will be used. /// -/// B10_LOGGER_DBGLEVEL +/// - B10_LOGGER_DBGLEVEL\n /// Ignored if the level is not DEBUG, this should be a number between 0 and /// 99 indicating the logging severity. The default is 0. If outside these /// limits or if not a number, The value passed to this function (default /// of 0) is used. /// -/// B10_LOGGER_LOCALMSG +/// - B10_LOGGER_LOCALMSG\n /// If defined, the path specification of a file that contains message /// definitions replacing ones in the default dictionary. /// +/// - B10_LOGGER_DESTINATION\n +/// If defined, the destination of the logging output. This can be one of: +/// - \c stdout Send output to stdout. +/// - \c stderr Send output to stderr +/// - \c syslog Send output to syslog using the facility local0. +/// - \c syslog:xxx Send output to syslog, using the facility xxx. ("xxx" +/// should be one of the syslog facilities such as "local0".) There must +/// be a colon between "syslog" and "xxx +/// - \c other Anything else is interpreted as the name of a file to which +/// output is appended. If the file does not exist, it is created. +/// /// Any errors in the settings cause messages to be output to stderr. /// /// This function is aimed at test programs, allowing the default settings to diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am index cd2ae2920f..6159d6303a 100644 --- a/src/lib/log/tests/Makefile.am +++ b/src/lib/log/tests/Makefile.am @@ -52,12 +52,23 @@ logger_example_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS) logger_example_LDADD = $(top_builddir)/src/lib/log/liblog.la logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la +check_PROGRAMS += init_logger_test +init_logger_test_SOURCES = init_logger_test.cc +init_logger_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +init_logger_test_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS) +init_logger_test_LDADD = $(top_builddir)/src/lib/log/liblog.la +init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la + noinst_PROGRAMS = $(TESTS) -# Additional test using the shell -PYTESTS = console_test.sh local_file_test.sh severity_test.sh +# Additional test using the shell. These are principally tests +# where the global logging environment is affected, and where the +# output needs to be compared with stored output (where "cut" and +# "diff" are useful utilities). + check-local: $(SHELL) $(abs_builddir)/console_test.sh $(SHELL) $(abs_builddir)/destination_test.sh + $(SHELL) $(abs_builddir)/init_logger_test.sh $(SHELL) $(abs_builddir)/local_file_test.sh $(SHELL) $(abs_builddir)/severity_test.sh diff --git a/src/lib/log/tests/init_logger_test.cc b/src/lib/log/tests/init_logger_test.cc new file mode 100644 index 0000000000..3cf52024fc --- /dev/null +++ b/src/lib/log/tests/init_logger_test.cc @@ -0,0 +1,41 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include +#include + +using namespace isc::log; + +/// \brief Test InitLogger +/// +/// A test program that initializes using initLogger(), then outputs several +/// messages at different severities. An external script sets the environment +/// variables and checks that they have the desired effect. + +int +main(int, char**) { + initLogger(); + Logger logger("log"); + + LOG_DEBUG(logger, 0, LOG_BAD_DESTINATION).arg("debug-0"); + LOG_DEBUG(logger, 50, LOG_BAD_DESTINATION).arg("debug-50"); + LOG_DEBUG(logger, 99, LOG_BAD_DESTINATION).arg("debug-99"); + LOG_INFO(logger, LOG_BAD_SEVERITY).arg("info"); + LOG_WARN(logger, LOG_BAD_STREAM).arg("warn"); + LOG_ERROR(logger, LOG_DUPLICATE_MESSAGE_ID).arg("error"); + LOG_FATAL(logger, LOG_NO_MESSAGE_ID).arg("fatal"); + + return (0); +} diff --git a/src/lib/log/tests/init_logger_test.sh.in b/src/lib/log/tests/init_logger_test.sh.in new file mode 100755 index 0000000000..6e199a43f7 --- /dev/null +++ b/src/lib/log/tests/init_logger_test.sh.in @@ -0,0 +1,115 @@ +#!/bin/sh +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# \brief Severity test +# +# Checks that the logger will limit the output of messages less severy than +# the severity/debug setting. + +testname="initLogger test" +echo $testname + +failcount=0 +tempfile=@abs_builddir@/init_logger_test_tempfile_$$ +destfile=@abs_builddir@/init_logger_test_destfile_$$ + +passfail() { + if [ $1 -eq 0 ]; then + echo " pass" + else + echo " FAIL" + failcount=`expr $failcount + $1` + fi +} + +echo "1. Checking that B10_LOGGER_SEVERITY/B10_LOGGER_DBGLEVEL work" + +echo -n " - severity=DEBUG, dbglevel=99: " +cat > $tempfile << . +DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-0 +DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-50 +DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-99 +INFO [bind10.log] LOG_BAD_SEVERITY unrecognized log severity: info +WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn +ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code +FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID +. +rm -f $destfile +B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test 2> $destfile +cut -d' ' -f3- $destfile | diff $tempfile - +passfail $? + +echo -n " - severity=DEBUG, dbglevel=50: " +cat > $tempfile << . +DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-0 +DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-50 +INFO [bind10.log] LOG_BAD_SEVERITY unrecognized log severity: info +WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn +ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code +FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID +. +rm -f $destfile +B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test 2> $destfile +cut -d' ' -f3- $destfile | diff $tempfile - +passfail $? + +echo -n " - severity=WARN: " +cat > $tempfile << . +WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn +ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code +FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID +. +rm -f $destfile +B10_LOGGER_SEVERITY=WARN ./init_logger_test 2> $destfile +cut -d' ' -f3- $destfile | diff $tempfile - +passfail $? + +echo "2. Checking that B10_LOGGER_DESTINATION works" + +echo -n " - stdout: " +cat > $tempfile << . +FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID +. +rm -f $destfile +B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=stdout ./init_logger_test 1> $destfile +cut -d' ' -f3- $destfile | diff $tempfile - +passfail $? + +echo -n " - stderr: " +rm -f $destfile +B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=stderr ./init_logger_test 2> $destfile +cut -d' ' -f3- $destfile | diff $tempfile - +passfail $? + +echo -n " - file: " +rm -f $destfile +B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=$destfile ./init_logger_test +cut -d' ' -f3- $destfile | diff $tempfile - +passfail $? + +# Note: can't automatically test syslog output. + +if [ $failcount -eq 0 ]; then + echo "PASS: $testname" +elif [ $failcount -eq 1 ]; then + echo "FAIL: $testname - 1 test failed" +else + echo "FAIL: $testname - $failcount tests failed" +fi + +# Tidy up. +rm -f $tempfile $destfile + +exit $failcount diff --git a/src/lib/log/tests/severity_test.sh.in b/src/lib/log/tests/severity_test.sh.in index 124f36af5d..a5827ded98 100755 --- a/src/lib/log/tests/severity_test.sh.in +++ b/src/lib/log/tests/severity_test.sh.in @@ -33,7 +33,7 @@ passfail() { fi } -echo -n "1. runInitTest default parameters:" +echo -n "1. Default parameters:" cat > $tempfile << . FATAL [example] LOG_WRITE_ERROR error writing to test1: 42 ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file From 4b83a53a37e3fa53a01ca0a6b4c9f7846a64bc5e Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 29 Jun 2011 19:11:15 +0100 Subject: [PATCH 013/974] [trac1071] Fix typo in README file. --- src/lib/log/README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/log/README b/src/lib/log/README index cd7cd72e25..e09691ad78 100644 --- a/src/lib/log/README +++ b/src/lib/log/README @@ -493,8 +493,8 @@ The location to which log message are written. This can be one of: stderr Messages are written to stderr syslog[:facility] Messages are written to syslog. If the optional "facility" is used, the messages are written using - that facility. (This defaults to "local7" if not - specified) + that facility. (This defaults to "local0" if not + specified.) Anything else Interpreted as the name of a file to which output is appended. If the file does not exist, a new one is opened. From 7e41b9c3e2e1ca809ed4ea6de67c843a1a0d7680 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 29 Jun 2011 21:46:05 +0200 Subject: [PATCH 014/974] [trac714] handle bad data on the cc session in cfgmgr --- src/lib/python/isc/cc/session.py | 37 ++++++++++++++----- src/lib/python/isc/cc/tests/session_test.py | 10 +++++ src/lib/python/isc/config/cfgmgr.py | 3 ++ src/lib/python/isc/config/cfgmgr_messages.mes | 6 +++ 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/lib/python/isc/cc/session.py b/src/lib/python/isc/cc/session.py index fb7dd06ff0..f6b62653be 100644 --- a/src/lib/python/isc/cc/session.py +++ b/src/lib/python/isc/cc/session.py @@ -93,6 +93,19 @@ class Session: self._socket.send(msg) def recvmsg(self, nonblock = True, seq = None): + """Reads a message. If nonblock is true, and there is no + message to read, it returns (None, None). + If seq is not None, it should be a value as returned by + group_sendmsg(), in which case only the response to + that message is returned, and others will be queued until + the next call to this method. + If seq is None, only messages that are *not* responses + will be returned, and responses will be queued. + The queue is checked for relevant messages before data + is read from the socket. + Raises a SessionError if there is a JSON decode problem in + the message that is read, or if the session has been closed + prior to the call of recvmsg()""" with self._lock: if len(self._queue) > 0: i = 0; @@ -109,16 +122,22 @@ class Session: if data and len(data) > 2: header_length = struct.unpack('>H', data[0:2])[0] data_length = len(data) - 2 - header_length - if data_length > 0: - env = isc.cc.message.from_wire(data[2:header_length+2]) - msg = isc.cc.message.from_wire(data[header_length + 2:]) - if (seq == None and "reply" not in env) or (seq != None and "reply" in env and seq == env["reply"]): - return env, msg + try: + if data_length > 0: + env = isc.cc.message.from_wire(data[2:header_length+2]) + msg = isc.cc.message.from_wire(data[header_length + 2:]) + if (seq == None and "reply" not in env) or (seq != None and "reply" in env and seq == env["reply"]): + return env, msg + else: + self._queue.append((env,msg)) + return self.recvmsg(nonblock, seq) else: - self._queue.append((env,msg)) - return self.recvmsg(nonblock, seq) - else: - return isc.cc.message.from_wire(data[2:header_length+2]), None + return isc.cc.message.from_wire(data[2:header_length+2]), None + except ValueError as ve: + # TODO: when we have logging here, add a debug + # message printing the data that we were unable + # to parse as JSON + raise SessionError(ve) return None, None def _receive_bytes(self, size): diff --git a/src/lib/python/isc/cc/tests/session_test.py b/src/lib/python/isc/cc/tests/session_test.py index fe35a6cbfa..772ed0c961 100644 --- a/src/lib/python/isc/cc/tests/session_test.py +++ b/src/lib/python/isc/cc/tests/session_test.py @@ -274,6 +274,16 @@ class testSession(unittest.TestCase): self.assertEqual({"hello": "b"}, msg) self.assertFalse(sess.has_queued_msgs()) + def test_recv_bad_msg(self): + sess = MySession() + self.assertFalse(sess.has_queued_msgs()) + sess._socket.addrecv({'to': 'someone' }, {'hello': 'b'}) + sess._socket.addrecv({'to': 'someone', 'reply': 1}, {'hello': 'a'}) + # mangle the bytes a bit + sess._socket.recvqueue[5] = sess._socket.recvqueue[5] - 2 + sess._socket.recvqueue = sess._socket.recvqueue[:-2] + self.assertRaises(SessionError, sess.recvmsg, True, 1) + def test_next_sequence(self): sess = MySession() self.assertEqual(sess._sequence, 1) diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py index 83db159794..18e001c306 100644 --- a/src/lib/python/isc/config/cfgmgr.py +++ b/src/lib/python/isc/config/cfgmgr.py @@ -380,6 +380,9 @@ class ConfigManager: answer, env = self.cc.group_recvmsg(False, seq) except isc.cc.SessionTimeout: answer = ccsession.create_answer(1, "Timeout waiting for answer from " + module_name) + except isc.cc.SessionError as se: + logger.error(CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE, module_name, se) + answer = ccsession.create_answer(1, "Unable to parse response from " + module_name + ": " + str(se)) if answer: rcode, val = ccsession.parse_answer(answer) if rcode == 0: diff --git a/src/lib/python/isc/config/cfgmgr_messages.mes b/src/lib/python/isc/config/cfgmgr_messages.mes index 9355e4d976..eb09ab4692 100644 --- a/src/lib/python/isc/config/cfgmgr_messages.mes +++ b/src/lib/python/isc/config/cfgmgr_messages.mes @@ -20,6 +20,12 @@ An older version of the configuration database has been found, from which there was an automatic upgrade path to the current version. These changes are now applied, and no action from the administrator is necessary. +% CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2 +The configuration manager sent a configuration update to a module, but +the module responded with an answer that could not be parsed. The answer +message appears to be invalid JSON data, or not decodable to a string. +This is likely to be a problem in the module in question. + % CFGMGR_CC_SESSION_ERROR Error connecting to command channel: %1 The configuration manager daemon was unable to connect to the messaging system. The most likely cause is that msgq is not running. From 27bb28f8bbde1dfc79030b0129a1c0405a8ffc38 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 29 Jun 2011 15:50:46 -0700 Subject: [PATCH 015/974] [trac1069] introduce "RequestCheckCreator", the factory for the default check of the DNS ACLs. match logic is not implemented yet. --- src/lib/acl/dns.cc | 81 ++++++++++++++++++++++++++--- src/lib/acl/dns.h | 45 ++++++++++++++-- src/lib/acl/tests/dns_test.cc | 98 +++++++++++++++++++++++++++++++++-- 3 files changed, 206 insertions(+), 18 deletions(-) diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index 16f1bf5dcb..05aea82779 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -12,20 +12,85 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include "dns.h" +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include + +using namespace std; +using boost::shared_ptr; +using namespace isc::data; namespace isc { namespace acl { +template <> +bool +IPCheck::matches(const dns::RequestContext&) const { + return (false); +} + namespace dns { -Loader& -getLoader() { - static Loader* loader(NULL); - if (loader == NULL) { - loader = new Loader(REJECT); - // TODO: This is the place where we register default check creators - // like IP check, etc, once we have them. +vector +internal::RequestCheckCreator::names() const { + // Probably we should eventually build this vector in a more + // sophisticated way. For now, it's simple enough to hardcode + // everything. + vector supported_names; + supported_names.push_back("from"); + return (supported_names); +} + +shared_ptr +internal::RequestCheckCreator::create(const string& name, + ConstElementPtr definition, + // unused: + const acl::Loader&) +{ + if (!definition) { + isc_throw(LoaderError, + "NULL pointer is passed to RequestCheckCreator"); } + + if (name == "from") { + return (shared_ptr( + new internal::RequestIPCheck(definition->stringValue()))); + } else { + // This case shouldn't happen (normally) as it should have been + // rejected at the loader level. But we explicitly catch the case + // and throw an exception for that. + isc_throw(LoaderError, "Invalid check name for RequestCheck: " << + name); + } +} + +RequestLoader& +getLoader() { + static RequestLoader* loader(NULL); + if (loader == NULL) { + // Creator registration may throw, so we first store the new loader + // in an auto pointer in order to provide the strong exception + // guarantee. + auto_ptr loader_ptr = + auto_ptr(new RequestLoader(REJECT)); + + // Register default check creator(s) + loader_ptr->registerCreator(shared_ptr( + new internal::RequestCheckCreator())); + + // From this point there shouldn't be any exception thrown + loader = loader_ptr.release(); + } + return (*loader); } diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h index 6f36e51893..d7cd445b4e 100644 --- a/src/lib/acl/dns.h +++ b/src/lib/acl/dns.h @@ -15,7 +15,15 @@ #ifndef ACL_DNS_H #define ACL_DNS_H -#include "loader.h" +#include +#include + +#include + +#include + +#include +#include #include #include @@ -64,13 +72,13 @@ struct RequestContext { }; /// \brief DNS based check. -typedef acl::Check Check; +typedef acl::Check RequestCheck; /// \brief DNS based compound check. typedef acl::CompoundCheck CompoundCheck; /// \brief DNS based ACL. -typedef acl::ACL ACL; +typedef acl::ACL RequestACL; /// \brief DNS based ACL loader. -typedef acl::Loader Loader; +typedef acl::Loader RequestLoader; /** * \brief Loader singleton access function. @@ -80,10 +88,37 @@ typedef acl::Loader Loader; * one is enough, this one will have registered default checks and it * is known one, so any plugins can registrer additional checks as well. */ -Loader& getLoader(); +RequestLoader& getLoader(); + +// The following is essentially private to the implementation and could +// be hidden in the implementation file. But it's visible via this header +// file for testing purposes. They are not supposed to be used by normal +// applications directly, and to signal the intent, they are given inside +// a separate namespace. +namespace internal { +// Shortcut typedef +typedef isc::acl::IPCheck RequestIPCheck; + +class RequestCheckCreator : public acl::Loader::CheckCreator { +public: + virtual std::vector names() const; + + virtual boost::shared_ptr + create(const std::string& name, isc::data::ConstElementPtr definition, + const acl::Loader& loader); + + /// Until we are sure how the various rules work for this case, we won't + /// allow unexpected special interpretation for list definitions. + virtual bool allowListAbbreviation() const { return (false); } +}; +} } } } #endif + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc index e5e0f3a18a..6fccffc289 100644 --- a/src/lib/acl/tests/dns_test.cc +++ b/src/lib/acl/tests/dns_test.cc @@ -12,10 +12,25 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + +#include +#include + +#include + +#include #include +#include +#include +#include + #include +using namespace std; +using namespace isc::data; using namespace isc::acl::dns; +using isc::acl::LoaderError; namespace { @@ -23,13 +38,86 @@ namespace { // time and the returned value can be used to anything. It is not much of a // test, but the getLoader is not much of a function. TEST(DNSACL, getLoader) { - Loader* l(&getLoader()); + RequestLoader* l(&getLoader()); ASSERT_TRUE(l != NULL); EXPECT_EQ(l, &getLoader()); - EXPECT_NO_THROW(l->load(isc::data::Element::fromJSON( - "[{\"action\": \"DROP\"}]"))); - // TODO Test that the things we should register by default, like IP based - // check, are loaded. + EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\"}]"))); + + // Test check rules registered by default, i.e. RequestCheck + EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\"," + " \"from\": \"192.0.2.1\"}]"))); +} + +class RequestCheckCreatorTest : public ::testing::Test { +protected: + internal::RequestCheckCreator creator_; + + typedef boost::shared_ptr ConstRequestCheckPtr; + ConstRequestCheckPtr check_; +}; + +TEST_F(RequestCheckCreatorTest, names) { + ASSERT_EQ(1, creator_.names().size()); + EXPECT_EQ("from", creator_.names()[0]); +} + +TEST_F(RequestCheckCreatorTest, allowListAbbreviation) { + EXPECT_FALSE(creator_.allowListAbbreviation()); +} + +// The following two tests check the creator for the form of +// 'from: "IP prefix"'. We don't test many variants of prefixes, which +// are done in the tests for IPCheck. +TEST_F(RequestCheckCreatorTest, createIPv4Check) { + check_ = creator_.create("from", Element::fromJSON("\"192.0.2.1\""), + getLoader()); + const internal::RequestIPCheck& ipcheck_ = + dynamic_cast(*check_); + EXPECT_EQ(AF_INET, ipcheck_.getFamily()); + EXPECT_EQ(32, ipcheck_.getPrefixlen()); + const vector check_address(ipcheck_.getAddress()); + ASSERT_EQ(4, check_address.size()); + const uint8_t expected_address[] = { 192, 0, 2, 1 }; + EXPECT_TRUE(equal(check_address.begin(), check_address.end(), + expected_address)); +} + +TEST_F(RequestCheckCreatorTest, createIPv6Check) { + check_ = creator_.create("from", + Element::fromJSON("\"2001:db8::5300/120\""), + getLoader()); + const internal::RequestIPCheck& ipcheck_ = + dynamic_cast(*check_); + EXPECT_EQ(AF_INET6, ipcheck_.getFamily()); + EXPECT_EQ(120, ipcheck_.getPrefixlen()); + const vector check_address(ipcheck_.getAddress()); + ASSERT_EQ(16, check_address.size()); + const uint8_t expected_address[] = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x53, 0x00 }; + EXPECT_TRUE(equal(check_address.begin(), check_address.end(), + expected_address)); +} + +TEST_F(RequestCheckCreatorTest, badCreate) { + // Invalid name + EXPECT_THROW(creator_.create("bad", Element::fromJSON("\"192.0.2.1\""), + getLoader()), LoaderError); + + // Invalid type of parameter + EXPECT_THROW(creator_.create("from", Element::fromJSON("4"), getLoader()), + isc::data::TypeError); + EXPECT_THROW(creator_.create("from", Element::fromJSON("[]"), getLoader()), + isc::data::TypeError); + + // Syntax error for IPCheck + EXPECT_THROW(creator_.create("from", Element::fromJSON("\"bad\""), + getLoader()), + isc::InvalidParameter); + + // NULL pointer + EXPECT_THROW(creator_.create("from", ConstElementPtr(), getLoader()), + LoaderError); } } From e3b0557e225ad3e7a6b7d192b8820666d7b81d0a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 29 Jun 2011 17:15:33 -0700 Subject: [PATCH 016/974] [trac1069] added specialization for IPCheck with tests based on the server_common/Client counterparts. Also move the helper getSockAddr() function for the test program as it's shared by multiple test implementations. --- src/lib/acl/dns.cc | 16 ++++- src/lib/acl/dns.h | 22 ++++-- src/lib/acl/tests/dns_test.cc | 96 +++++++++++++++++++++----- src/lib/acl/tests/ip_check_unittest.cc | 31 ++------- src/lib/acl/tests/sockaddr.h | 66 ++++++++++++++++++ 5 files changed, 182 insertions(+), 49 deletions(-) create mode 100644 src/lib/acl/tests/sockaddr.h diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index 05aea82779..93b6297c58 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -32,10 +32,22 @@ using namespace isc::data; namespace isc { namespace acl { + +/// The specialization of \c IPCheck for access control with \c RequestContext. +/// +/// It returns \c true if the remote (source) IP address of the request +/// matches the expression encapsulated in the \c IPCheck, and returns +/// \c false if not. +/// +/// \note The match logic is expected to be extended as we add +/// more match parameters (at least there's a plan for TSIG key). template <> bool -IPCheck::matches(const dns::RequestContext&) const { - return (false); +IPCheck::matches( + const dns::RequestContext& request) const +{ + return (compare(request.remote_address.getData(), + request.remote_address.getFamily())); } namespace dns { diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h index d7cd445b4e..ce65c36559 100644 --- a/src/lib/acl/dns.h +++ b/src/lib/acl/dns.h @@ -46,16 +46,28 @@ namespace dns { * call the ACLs directly? */ struct RequestContext { + explicit RequestContext(const IPAddress& remote_address_param) : + remote_address(remote_address_param) + {} + + const IPAddress& remote_address; + +#ifdef notyet /// \brief The DNS message (payload). isc::dns::ConstMessagePtr message; + /// \brief The remote IP address (eg. the client). asiolink::IOAddress remote_address; + /// \brief The local IP address (ours, of the interface where we received). asiolink::IOAddress local_address; + /// \brief The remote port. uint16_t remote_port; + /// \brief The local port. uint16_t local_port; + /** * \brief Name of the TSIG key the message is signed with. * @@ -69,6 +81,7 @@ struct RequestContext { * ACL. */ std::string tsig_key_name; +#endif }; /// \brief DNS based check. @@ -96,6 +109,7 @@ RequestLoader& getLoader(); // applications directly, and to signal the intent, they are given inside // a separate namespace. namespace internal { + // Shortcut typedef typedef isc::acl::IPCheck RequestIPCheck; @@ -111,11 +125,11 @@ public: /// allow unexpected special interpretation for list definitions. virtual bool allowListAbbreviation() const { return (false); } }; -} +} // end of namespace "internal" -} -} -} +} // end of namespace "dns" +} // end of namespace "acl" +} // end of namespace "isc" #endif diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc index 6fccffc289..aa2d342869 100644 --- a/src/lib/acl/tests/dns_test.cc +++ b/src/lib/acl/tests/dns_test.cc @@ -16,6 +16,10 @@ #include #include +#include + +#include +#include #include @@ -25,11 +29,14 @@ #include #include +#include "sockaddr.h" + #include using namespace std; +using boost::scoped_ptr; using namespace isc::data; -using namespace isc::acl::dns; +using namespace isc::acl; using isc::acl::LoaderError; namespace { @@ -38,9 +45,9 @@ namespace { // time and the returned value can be used to anything. It is not much of a // test, but the getLoader is not much of a function. TEST(DNSACL, getLoader) { - RequestLoader* l(&getLoader()); + dns::RequestLoader* l(&dns::getLoader()); ASSERT_TRUE(l != NULL); - EXPECT_EQ(l, &getLoader()); + EXPECT_EQ(l, &dns::getLoader()); EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\"}]"))); // Test check rules registered by default, i.e. RequestCheck @@ -50,9 +57,9 @@ TEST(DNSACL, getLoader) { class RequestCheckCreatorTest : public ::testing::Test { protected: - internal::RequestCheckCreator creator_; + dns::internal::RequestCheckCreator creator_; - typedef boost::shared_ptr ConstRequestCheckPtr; + typedef boost::shared_ptr ConstRequestCheckPtr; ConstRequestCheckPtr check_; }; @@ -70,9 +77,9 @@ TEST_F(RequestCheckCreatorTest, allowListAbbreviation) { // are done in the tests for IPCheck. TEST_F(RequestCheckCreatorTest, createIPv4Check) { check_ = creator_.create("from", Element::fromJSON("\"192.0.2.1\""), - getLoader()); - const internal::RequestIPCheck& ipcheck_ = - dynamic_cast(*check_); + dns::getLoader()); + const dns::internal::RequestIPCheck& ipcheck_ = + dynamic_cast(*check_); EXPECT_EQ(AF_INET, ipcheck_.getFamily()); EXPECT_EQ(32, ipcheck_.getPrefixlen()); const vector check_address(ipcheck_.getAddress()); @@ -85,9 +92,9 @@ TEST_F(RequestCheckCreatorTest, createIPv4Check) { TEST_F(RequestCheckCreatorTest, createIPv6Check) { check_ = creator_.create("from", Element::fromJSON("\"2001:db8::5300/120\""), - getLoader()); - const internal::RequestIPCheck& ipcheck_ = - dynamic_cast(*check_); + dns::getLoader()); + const dns::internal::RequestIPCheck& ipcheck_ = + dynamic_cast(*check_); EXPECT_EQ(AF_INET6, ipcheck_.getFamily()); EXPECT_EQ(120, ipcheck_.getPrefixlen()); const vector check_address(ipcheck_.getAddress()); @@ -102,22 +109,79 @@ TEST_F(RequestCheckCreatorTest, createIPv6Check) { TEST_F(RequestCheckCreatorTest, badCreate) { // Invalid name EXPECT_THROW(creator_.create("bad", Element::fromJSON("\"192.0.2.1\""), - getLoader()), LoaderError); + dns::getLoader()), LoaderError); // Invalid type of parameter - EXPECT_THROW(creator_.create("from", Element::fromJSON("4"), getLoader()), + EXPECT_THROW(creator_.create("from", Element::fromJSON("4"), + dns::getLoader()), isc::data::TypeError); - EXPECT_THROW(creator_.create("from", Element::fromJSON("[]"), getLoader()), + EXPECT_THROW(creator_.create("from", Element::fromJSON("[]"), + dns::getLoader()), isc::data::TypeError); // Syntax error for IPCheck EXPECT_THROW(creator_.create("from", Element::fromJSON("\"bad\""), - getLoader()), + dns::getLoader()), isc::InvalidParameter); // NULL pointer - EXPECT_THROW(creator_.create("from", ConstElementPtr(), getLoader()), + EXPECT_THROW(creator_.create("from", ConstElementPtr(), dns::getLoader()), LoaderError); } +class RequestCheckTest : public ::testing::Test { +protected: + typedef boost::shared_ptr ConstRequestCheckPtr; + + // A helper shortcut to create a single IP check for the given prefix. + ConstRequestCheckPtr createIPCheck(const string& prefix) { + return (creator_.create("from", Element::fromJSON( + string("\"") + prefix + string("\"")), + dns::getLoader())); + } + + // create a one time request context for a specific test. Note that + // getSockaddr() uses a static storage, so it cannot be called more than + // once in a single test. + const dns::RequestContext& getRequest4() { + ipaddr.reset(new IPAddress(tests::getSockAddr("192.0.2.1"))); + request.reset(new dns::RequestContext(*ipaddr)); + return (*request); + } + const dns::RequestContext& getRequest6() { + ipaddr.reset(new IPAddress(tests::getSockAddr("2001:db8::1"))); + request.reset(new dns::RequestContext(*ipaddr)); + return (*request); + } + +private: + scoped_ptr ipaddr; + scoped_ptr request; + dns::internal::RequestCheckCreator creator_; +}; + +TEST_F(RequestCheckTest, checkIPv4) { + // Exact match + EXPECT_TRUE(createIPCheck("192.0.2.1")->matches(getRequest4())); + // Exact match (negative) + EXPECT_FALSE(createIPCheck("192.0.2.53")->matches(getRequest4())); + // Prefix match + EXPECT_TRUE(createIPCheck("192.0.2.0/24")->matches(getRequest4())); + // Prefix match (negative) + EXPECT_FALSE(createIPCheck("192.0.1.0/24")->matches(getRequest4())); + // Address family mismatch (the first 4 bytes of the IPv6 address has the + // same binary representation as the client's IPv4 address, which + // shouldn't confuse the match logic) + EXPECT_FALSE(createIPCheck("c000:0201::")->matches(getRequest4())); +} + +TEST_F(RequestCheckTest, checkIPv6) { + // The following are a set of tests of the same concept as checkIPv4 + EXPECT_TRUE(createIPCheck("2001:db8::1")->matches(getRequest6())); + EXPECT_FALSE(createIPCheck("2001:db8::53")->matches(getRequest6())); + EXPECT_TRUE(createIPCheck("2001:db8::/64")->matches(getRequest6())); + EXPECT_FALSE(createIPCheck("2001:db8:1::/64")->matches(getRequest6())); + EXPECT_FALSE(createIPCheck("32.1.13.184")->matches(getRequest6())); +} + } diff --git a/src/lib/acl/tests/ip_check_unittest.cc b/src/lib/acl/tests/ip_check_unittest.cc index fb249788f5..8b8c49808c 100644 --- a/src/lib/acl/tests/ip_check_unittest.cc +++ b/src/lib/acl/tests/ip_check_unittest.cc @@ -14,12 +14,13 @@ #include #include -#include #include #include #include +#include "sockaddr.h" + using namespace isc::acl; using namespace isc::acl::internal; using namespace std; @@ -159,32 +160,8 @@ TEST(IPFunctionCheck, SplitIPAddress) { EXPECT_THROW(splitIPAddress(" 1/ "), isc::InvalidParameter); } -const struct sockaddr& -getSockAddr(const char* const addr) { - struct addrinfo hints, *res; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICHOST; - - if (getaddrinfo(addr, NULL, &hints, &res) == 0) { - static struct sockaddr_storage ss; - void* ss_ptr = &ss; - memcpy(ss_ptr, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - return (*static_cast(ss_ptr)); - } - - // We don't expect getaddrinfo to fail for our tests. But if that - // ever happens we return a dummy value that would make subsequent test - // fail. - static struct sockaddr sa_dummy; - sa_dummy.sa_family = AF_UNSPEC; - return (sa_dummy); -} - TEST(IPAddress, constructIPv4) { - IPAddress ipaddr(getSockAddr("192.0.2.1")); + IPAddress ipaddr(tests::getSockAddr("192.0.2.1")); const char expected_data[4] = { 192, 0, 2, 1 }; EXPECT_EQ(AF_INET, ipaddr.getFamily()); EXPECT_EQ(4, ipaddr.getLength()); @@ -192,7 +169,7 @@ TEST(IPAddress, constructIPv4) { } TEST(IPAddress, constructIPv6) { - IPAddress ipaddr(getSockAddr("2001:db8:1234:abcd::53")); + IPAddress ipaddr(tests::getSockAddr("2001:db8:1234:abcd::53")); const char expected_data[16] = { 0x20, 0x01, 0x0d, 0xb8, 0x12, 0x34, 0xab, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53 }; diff --git a/src/lib/acl/tests/sockaddr.h b/src/lib/acl/tests/sockaddr.h new file mode 100644 index 0000000000..11d63872df --- /dev/null +++ b/src/lib/acl/tests/sockaddr.h @@ -0,0 +1,66 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __ACL_TEST_SOCKADDR_H +#define __ACL_TEST_SOCKADDR_H 1 + +#include +#include +#include + +namespace isc { +namespace acl { +namespace tests { + +// This is a helper function that returns a sockaddr for the given textual +// IP address. Note that "inline" is crucial because this function is defined +// in a header file included in multiple .cc files. Without inline it would +// produce an external linkage and cause troubles at link time. +// +// Note that this function uses a static storage for the return value. +// So if it's called more than once in a singe context (e.g., in the same +// EXPECT_xx()), it's unlikely to work as expected. +inline const struct sockaddr& +getSockAddr(const char* const addr) { + struct addrinfo hints, *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(addr, NULL, &hints, &res) == 0) { + static struct sockaddr_storage ss; + void* ss_ptr = &ss; + memcpy(ss_ptr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return (*static_cast(ss_ptr)); + } + + // We don't expect getaddrinfo to fail for our tests. But if that + // ever happens we return a dummy value that would make subsequent test + // fail. + static struct sockaddr sa_dummy; + sa_dummy.sa_family = AF_UNSPEC; + return (sa_dummy); +} + +} // end of namespace "tests" +} // end of namespace "acl" +} // end of namespace "isc" + +#endif // __ACL_TEST_SOCKADDR_H + +// Local Variables: +// mode: c++ +// End: From 0caa1d89d3e60a80ab7517d3691a149093e32be6 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 29 Jun 2011 17:19:52 -0700 Subject: [PATCH 017/974] [trac1069] cleanup: removed ACL related definitions and tests from Client. they were moved to acl::dns and merged with RequestContext. --- src/lib/server_common/client.cc | 7 ------ src/lib/server_common/client.h | 11 --------- .../server_common/tests/client_unittest.cc | 24 ------------------- 3 files changed, 42 deletions(-) diff --git a/src/lib/server_common/client.cc b/src/lib/server_common/client.cc index 31dee88481..e6383d6352 100644 --- a/src/lib/server_common/client.cc +++ b/src/lib/server_common/client.cc @@ -66,10 +66,3 @@ std::ostream& isc::server_common::operator<<(std::ostream& os, const Client& client) { return (os << client.toText()); } - -template <> -bool -IPCheck::matches(const Client& client) const { - const IPAddress& request_src(client.getRequestSourceIPAddress()); - return (compare(request_src.getData(), request_src.getFamily())); -} diff --git a/src/lib/server_common/client.h b/src/lib/server_common/client.h index 148e0696e6..1c5928aff6 100644 --- a/src/lib/server_common/client.h +++ b/src/lib/server_common/client.h @@ -145,17 +145,6 @@ private: /// parameter \c os after the insertion operation. std::ostream& operator<<(std::ostream& os, const Client& client); } - -namespace acl { -/// The specialization of \c IPCheck for access control with \c Client. -/// -/// It returns \c true if the source IP address of the client's request -/// matches the expression encapsulated in the \c IPCheck, and returns -/// \c false if not. -template <> -bool IPCheck::matches( - const server_common::Client& client) const; -} } #endif // __CLIENT_H diff --git a/src/lib/server_common/tests/client_unittest.cc b/src/lib/server_common/tests/client_unittest.cc index 34a90a2866..287a92660e 100644 --- a/src/lib/server_common/tests/client_unittest.cc +++ b/src/lib/server_common/tests/client_unittest.cc @@ -89,30 +89,6 @@ TEST_F(ClientTest, constructIPv6) { client6->getRequestSourceIPAddress().getData(), 16)); } -TEST_F(ClientTest, ACLCheckIPv4) { - // Exact match - EXPECT_TRUE(IPCheck("192.0.2.1").matches(*client4)); - // Exact match (negative) - EXPECT_FALSE(IPCheck("192.0.2.53").matches(*client4)); - // Prefix match - EXPECT_TRUE(IPCheck("192.0.2.0/24").matches(*client4)); - // Prefix match (negative) - EXPECT_FALSE(IPCheck("192.0.1.0/24").matches(*client4)); - // Address family mismatch (the first 4 bytes of the IPv6 address has the - // same binary representation as the client's IPv4 address, which - // shouldn't confuse the match logic) - EXPECT_FALSE(IPCheck("c000:0201::").matches(*client4)); -} - -TEST_F(ClientTest, ACLCheckIPv6) { - // The following are a set of tests of the same concept as ACLCheckIPv4 - EXPECT_TRUE(IPCheck("2001:db8::1").matches(*client6)); - EXPECT_FALSE(IPCheck("2001:db8::53").matches(*client6)); - EXPECT_TRUE(IPCheck("2001:db8::/64").matches(*client6)); - EXPECT_FALSE(IPCheck("2001:db8:1::/64").matches(*client6)); - EXPECT_FALSE(IPCheck("32.1.13.184").matches(*client6)); -} - TEST_F(ClientTest, toText) { EXPECT_EQ("192.0.2.1#53214", client4->toText()); EXPECT_EQ("2001:db8::1#53216", client6->toText()); From c136060da6a43da5db7e45b6a32da83f0f7d0820 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 29 Jun 2011 21:43:57 -0500 Subject: [PATCH 018/974] [jreed-docs-2] remove some spaces at ends of lines in guide --- doc/guide/bind10-guide.xml | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 7d1a006545..3e03ed2e5c 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -129,7 +129,7 @@ The processes started by the bind10 command have names starting with "b10-", including: - + @@ -224,7 +224,7 @@
Managing BIND 10 - + Once BIND 10 is running, a few commands are used to interact directly with the system: @@ -263,7 +263,7 @@ In addition, manual pages are also provided in the default installation. - + - + Starting BIND10 with <command>bind10</command> - BIND 10 provides the bind10 command which + BIND 10 provides the bind10 command which starts up the required processes. bind10 will also restart processes that exit unexpectedly. @@ -694,7 +694,7 @@ Debian and Ubuntu: After starting the b10-msgq communications channel, - bind10 connects to it, + bind10 connects to it, runs the configuration manager, and reads its own configuration. Then it starts the other modules. @@ -752,7 +752,7 @@ Debian and Ubuntu: b10-msgq service. It listens on 127.0.0.1. - + The configuration data item is: - + database_file - + This is an optional string to define the path to find the SQLite3 database file. @@ -1103,7 +1103,7 @@ This may be a temporary setting until then. shutdown - + Stop the authoritative DNS server. @@ -1159,7 +1159,7 @@ This may be a temporary setting until then. $INCLUDE - + Loads an additional zone file. This may be recursive. @@ -1167,7 +1167,7 @@ This may be a temporary setting until then. $ORIGIN - + Defines the relative domain name. @@ -1175,7 +1175,7 @@ This may be a temporary setting until then. $TTL - + Defines the time-to-live value used for following records that don't include a TTL. @@ -1240,7 +1240,7 @@ TODO The current development release of BIND 10 only supports - AXFR. (IXFR is not supported.) + AXFR. (IXFR is not supported.) @@ -1287,7 +1287,7 @@ what if a NOTIFY is sent? The current development release of BIND 10 only supports - AXFR. (IXFR is not supported.) + AXFR. (IXFR is not supported.) Access control is not yet provided. From 688d0a641d4fa7a018fb4f9e131ed1454c68dd15 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 29 Jun 2011 21:45:12 -0500 Subject: [PATCH 019/974] [jreed-docs-2] add start of access control section and some comments todo wrote about access control for resolver added many comments for things to document. --- doc/guide/bind10-guide.xml | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 3e03ed2e5c..c894f9cf9b 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1374,6 +1374,67 @@ what is XfroutClient xfr_client?? +
+ Access Control + + + The b10-resolver daemon only accepts + DNS queries from the localhost (127.0.0.1 and ::1). + The configuration may + be used to reject, drop, or allow specific IPs or networks. + This configuration list is first match. + + + + The configuration's item may be + set to ACCEPT to allow the incoming query, + REJECT to respond with a DNS REFUSED return + code, or DROP to ignore the query without + any response (such as a blackhole). For more information, + see the respective debugging messages: RESOLVER_QUERY_ACCEPTED, + RESOLVER_QUERY_REJECTED, + and RESOLVER_QUERY_DROPPED. + + + + The required configuration's item is set + to an IPv4 or IPv6 address, addresses with an network mask, or to + the special lowercase keywords any6 (for + any IPv6 address) or any4 (for any IPv4 + address). + + + + + + For example to allow the 192.168.1.0/24 + network to use your recursive name server, at the + bindctl prompt run: + + + +> config add Resolver/query_acl +> config set Resolver/query_acl[2]/action "ACCEPT" +> config set Resolver/query_acl[2]/from "192.168.1.0/24" +> config commit + + + (Replace the 2 + as needed; run config show + Resolver/query_acl if needed.) + + + This prototype access control configuration + syntax may be changed. + +
+
Forwarding @@ -1533,6 +1594,29 @@ then change those defaults with config set Resolver/forward_addresses[0]/address + + From ca03a1f5156b0a68a2179e287d9af444c64aee91 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 30 Jun 2011 10:39:49 +0200 Subject: [PATCH 020/974] [trac760] address review comments --- src/bin/cmdctl/cmdctl_messages.mes | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bin/cmdctl/cmdctl_messages.mes b/src/bin/cmdctl/cmdctl_messages.mes index c3755bcf71..9ba2a9b075 100644 --- a/src/bin/cmdctl/cmdctl_messages.mes +++ b/src/bin/cmdctl/cmdctl_messages.mes @@ -19,13 +19,13 @@ There was an error reading the updated configuration data. The specific error is printed. -% CMDCTL_BAD_PASSWORD Bad password for user: %1 +% CMDCTL_BAD_PASSWORD bad password for user: %1 A login attempt was made to b10-cmdctl, but the password was wrong. Users can be managed with the tool b10-cmdctl-usermgr. % CMDCTL_CC_SESSION_ERROR error reading from cc channel: %1 There was a problem reading from the command and control channel. The -most likely cause is that the msgq daemon is not running. +most likely cause is that the message bus daemon is not running. % CMDCTL_CC_SESSION_TIMEOUT timeout on cc channel A timeout occurred when waiting for essential data from the cc session. @@ -33,14 +33,14 @@ This usually occurs when b10-cfgmgr is not running or not responding. Since we are waiting for essential information, this is a fatal error, and the cmdctl daemon will now shut down. -% CMDCTL_COMMAND_ERROR Error in command %1 to module %2: %3 +% CMDCTL_COMMAND_ERROR error in command %1 to module %2: %3 An error was encountered sending the given command to the given module. Either there was a communication problem with the module, or the module was not able to process the command, and sent back an error. The specific error is printed in the message. -% CMDCTL_COMMAND_SENT Command '%1' to module '%2' was sent -This debug message indicates that the given command is being sent to +% CMDCTL_COMMAND_SENT command '%1' to module '%2' was sent +This debug message indicates that the given command has been sent to the given module. % CMDCTL_NO_SUCH_USER username not found in user database: %1 @@ -54,15 +54,15 @@ this message follows a message CMDCTL_USER_DATABASE_READ_ERROR containing a specific error), or the file was empty. Users can be added with the tool b10-cmdctl-usermgr. -% CMDCTL_SEND_COMMAND Sending command %1 to module %2 +% CMDCTL_SEND_COMMAND sending command %1 to module %2 This debug message indicates that the given command is being sent to the given module. -% CMDCTL_SSL_ERROR_USER_DENIED User denied because of SSL error: %1 +% CMDCTL_SSL_ERROR_USER_DENIED user denied because of SSL error: %1 The user was denied because the SSL connection could not successfully be set up. The specific error is given in the log message. Possible -causes may be that the ssl request itself was bad, or the key or -certificate file had a problem. +causes may be that the ssl request itself was bad, or the local key or +certificate file could not be read. % CMDCTL_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down There was a keyboard interrupt signal to stop the cmdctl daemon. The From ae4e7f10136bd182db6d4801ace410e72574abf2 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 30 Jun 2011 10:56:12 +0200 Subject: [PATCH 021/974] [master] short message about why u no log in asiolink readme since this is just a short description, i'm pushing this straight to master --- src/lib/asiolink/README | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/asiolink/README b/src/lib/asiolink/README index 66091b1c2b..b9e38f98b4 100644 --- a/src/lib/asiolink/README +++ b/src/lib/asiolink/README @@ -20,3 +20,10 @@ Some of the classes defined here--for example, IOSocket, IOEndpoint, and IOAddress--are to be used by BIND 10 modules as wrappers around ASIO-specific classes. + +Logging +------- + +At this point, nothing is logged by this low-level library. We may +revisit that in the future, if we find suitable messages to log, but +right now there are also no loggers initialized or called. From dfd7a01376d7b871cf7dfe631f5c96b4b2b7767b Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 30 Jun 2011 11:12:45 +0200 Subject: [PATCH 022/974] [trac760] typo --- src/bin/cmdctl/cmdctl.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in index 232fad6c4d..304f62d97f 100755 --- a/src/bin/cmdctl/cmdctl.py.in +++ b/src/bin/cmdctl/cmdctl.py.in @@ -433,7 +433,7 @@ class CommandControl(): except ccsession.ModuleCCSessionError as mcse: errstr = str("Error in ccsession answer:") + str(mcse) - logger.error(CMDCTL_COMMAND_ERROR, conmmand_name, module_name, errstr) + logger.error(CMDCTL_COMMAND_ERROR, command_name, module_name, errstr) return 1, {'error': errstr} def get_cmdctl_config_data(self): From 4355e75c9f82ea797d9353e82fd4d7c445c9e5c2 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 30 Jun 2011 10:20:09 +0100 Subject: [PATCH 023/974] [trac1071] Update documentation and final tidy-up --- src/lib/log/README | 181 ++++++++++++++------------ src/lib/log/logger_support.cc | 41 +++--- src/lib/log/tests/init_logger_test.cc | 7 +- 3 files changed, 124 insertions(+), 105 deletions(-) diff --git a/src/lib/log/README b/src/lib/log/README index e09691ad78..298004da9d 100644 --- a/src/lib/log/README +++ b/src/lib/log/README @@ -142,6 +142,12 @@ Points to note: the error originated from the logging library and the "WRITE_ERROR" indicates that there was a problem in a write operation. + * The rest of the line - from the first non-space character to the last non- + space character - is taken exactly as-is for the text of the message. There + are no restrictions on what characters may be in this text, other than they + be printable. (This means that both single-quote (') and double-quote (") + characters are allowed.) + * The replacement tokens are the strings "%1", "%2" etc. When a message is logged, these are replaced with the arguments passed to the logging call: %1 refers to the first argument, %2 to the second etc. Within the @@ -233,7 +239,7 @@ Using the Logging - C++ 1. Build message header file and source file as describe above. 2. The main program unit should include a call to isc::log::initLogger() - (defined in logger_support.h) to set the logging severity, debug log + (described in more detail below) to set the logging severity, debug log level, and external message file: a) The logging severity is one of the enum defined in logger.h, i.e. @@ -279,9 +285,9 @@ Using the Logging - Python ========================== 1. Build message module as describe above. -2. The main program unit should include a call to isc.log.init() to - set the to set the logging severity, debug log level, and external - message file: +2. The main program unit should include a call to isc.log.init() + (described in more detail below) to set the to set the logging + severity, debug log level, and external message file: a) The logging severity is one of the strings: @@ -316,6 +322,91 @@ Using the Logging - Python logger.error(LOG_WRITE_ERROR, "output.txt"); +Logging Initialization +====================== +In all cases, if an attempt is made to use a logging method before the logging +has been initialized, the program will terminate with a LoggingNotInitialized +call. + +C++ +--- +Logging Initialization is carried out by calling initLogger(). There are two +variants to the call, one for use by production programs and one for use by +unit tests. + +Variant #1, Used by Production Programs +--------------------------------------- + void initLogger(const std::string& root, + isc::log::Severity severity = isc::log::INFO, + int dbglevel = 0, const char* file = NULL); + +This is the call that should be used by production programs: + +root +Name of the program (e.g. "b10-auth"). This is also the name of the root +logger and is used when configuring logging. + +severity +Default severity that the program will start logging with. Although this may +be overridden when the program obtains its configuration from the configuration +database, this is the severity that it used until then. (This may be set by +a command-line parameter.) + +dbglevel +The debug level used if "severity" is set to isc::log::DEBUG. + +file +The name of a local message file. This will be read and its defintitions used +to replace the compiled-in text of the messages. + + +Variant #2, Used by Unit Tests +------------------------------ + void initLogger() + +This is the call that should be used by unit tests. In this variant, all the +options are supplied by environment variables. (It should not be used for +production programs to avoid the chance that the program operation is affected +by inadvertantly-defined environment variables.) + +The environment variables are: + +B10_LOGGER_ROOT +Sets the "root" for the unit test. If not defined, the name "bind10" is used. + +B10_LOGGER_SEVERITY +The severity to set for the root logger in the unit test. Valid values are +"DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "NONE". If not defined, "INFO" +is used. + +B10_LOGGER_DBGLEVEL +If B10_LOGGER_SEVERITY is set to "DEBUG", the debug level. This can be a +number between 0 and 99, and defaults to 0. + +B10_LOGGER_LOCALMSG +If defined, points to a local message file. The default is not to use a local +message file. + +B10_LOGGER_DESTINATION +The location to which log message are written. This can be one of: + + stdout Message are written to stdout + stderr Messages are written to stderr + syslog[:facility] Messages are written to syslog. If the optional + "facility" is used, the messages are written using + that facility. (This defaults to "local0" if not + specified.) + Anything else Interpreted as the name of a file to which output + is appended. If the file does not exist, a new one + is opened. + +In the case of "stdout", "stderr" and "syslog", they must be written exactly +as is - no leading or trailing spaces, and in lower-case. + +Python +------ +To be supplied + Severity Guidelines =================== @@ -410,44 +501,6 @@ logger "pkt-auth".) As the loggers are independent and the severity levels independent, fine-tuning of what and what is not recorded can be achieved. -Logging Initialization -====================== -In all cases, if an attempt is made to use a logging method before the logging -has been initialized, the program will terminate with a LoggingNotInitialized -call. - -C++ ---- -Logging Initialization is carried out by calling initLogger(). There are two -variants to the call, one for use by production programs and one for use by -unit tests. - -Variant #1, Used by Production Programs ---------------------------------------- - void initLogger(const std::string& root, - isc::log::Severity severity = isc::log::INFO, - int dbglevel = 0, const char* file = NULL); - -This is the call that should be used by production programs: - -root -Name of the program (e.g. "b10-auth"). This is also the name of the root -logger and is used when configuring logging. - -severity -Default severity that the program will start logging with. Although this may -be overridden when the program obtains its configuration from the configuration -database, this is the severity that it used until then. (This may be set by -a command-line parameter.) - -dbglevel -The debug level used if "severity" is set to isc::log::DEBUG. - -file -The name of a local message file. This will be read and its defintitions used -to replace the compiled-in text of the messages. - - Notes ===== @@ -457,47 +510,3 @@ in both the message compiler and the server; in the server it is used when the server starts up (or when triggered by a command) to read in a message file to overwrite the internal dictionary. Writing it in C++ means there is only one piece of code that does this functionality. - - -Variant #2, Used by Unit Tests ------------------------------- - void initLogger() - -This is the call that should be used by unit tests. In this variant, all the -options are supplied by environment variables. (It should not be used for -production programs to avoid the chance that the program operation is affected -by inadvertantly-defined environment variables.) - -The environment variables are: - -B10_LOGGER_ROOT -Sets the "root" for the unit test. If not defined, the name "bind10" is used. - -B10_LOGGER_SEVERITY -The severity to set for the root logger in the unit test. Valid values are -"DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "NONE". If not defined, "INFO" -is used. - -B10_LOGGER_DBGLEVEL -If B10_LOGGER_SEVERITY is set to "DEBUG", the debug level. This can be a -number between 0 and 99, and defaults to 0. - -B10_LOGGER_LOCALMSG -If defined, points to a local message file. The default is not to use a local -message file. - -B10_LOGGER_DESTINATION -The location to which log message are written. This can be one of: - - stdout Message are written to stdout - stderr Messages are written to stderr - syslog[:facility] Messages are written to syslog. If the optional - "facility" is used, the messages are written using - that facility. (This defaults to "local0" if not - specified.) - Anything else Interpreted as the name of a file to which output - is appended. If the file does not exist, a new one - is opened. - -In the case of "stdout", "stderr" and "syslog", they must be written exactly -as is - no leading or trailing spaces, and in lower-case. diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc index eac2fbbe8b..2c5ab455e2 100644 --- a/src/lib/log/logger_support.cc +++ b/src/lib/log/logger_support.cc @@ -44,27 +44,34 @@ bool logging_init_state = false; // Set logging destination according to the setting of B10_LOGGER_DESTINATION. -// (See header for initLogger() for more details.) +// (See header for initLogger() for more details.) This is a no-op if the +// environment variable is not defined. +// +// \param root Name of the root logger +// \param severity Severity level to be assigned to the root logger +// \param dbglevel Debug level + void setDestination(const char* root, const isc::log::Severity severity, const int dbglevel) { using namespace isc::log; - // Constants: - static const string STDOUT = "stdout"; - static const string STDERR = "stderr"; - static const string SYSLOG = "syslog"; - static const string SYSLOG_COLON = "syslog:"; - - const char* destination = getenv("B10_LOGGER_DESTINATION"); if (destination != NULL) { - // Destination is present, adjust root logger destination to it. + // Constants: not declared static as this is function is expected to be + // called once only + const string STDOUT = "stdout"; + const string STDERR = "stderr"; + const string SYSLOG = "syslog"; + const string SYSLOG_COLON = "syslog:"; + + // Prepare the objects to define the logging specification LoggerSpecification spec(root, severity, dbglevel); OutputOption option; + // Set up output option according to destination specification const string dest = destination; if (dest == STDOUT) { option.destination = OutputOption::DEST_CONSOLE; @@ -76,28 +83,30 @@ setDestination(const char* root, const isc::log::Severity severity, } else if (dest == SYSLOG) { option.destination = OutputOption::DEST_SYSLOG; - option.facility = "local0"; + // Use default specified in OutputOption constructor for the + // syslog destination } else if (dest.find(SYSLOG_COLON) == 0) { option.destination = OutputOption::DEST_SYSLOG; - // Must take account of the string actually being "syslog:". + // Must take account of the string actually being "syslog:" if (dest == SYSLOG_COLON) { cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " << SYSLOG_COLON << " is invalid, " << SYSLOG << " will be used instead\n"; - option.facility = "local0"; + // Use default for logging facility + } else { - // Everything else is the facility name + // Everything else in the string is the facility name option.facility = dest.substr(SYSLOG_COLON.size()); } } else { - // Not a recognised destination, assume a file. + // Not a recognised destination, assume a file option.destination = OutputOption::DEST_FILE; option.filename = dest; } - // ... and set it. + // ... and set the destination spec.addOutputOption(option); LoggerManager manager; manager.process(spec); @@ -185,7 +194,7 @@ void initLogger(isc::log::Severity severity, int dbglevel) { // Initialize logging initLogger(root, severity, dbglevel, localfile); - // Now set the destination + // Now set the destination for logging output setDestination(root, severity, dbglevel); } diff --git a/src/lib/log/tests/init_logger_test.cc b/src/lib/log/tests/init_logger_test.cc index 3cf52024fc..104c0780f3 100644 --- a/src/lib/log/tests/init_logger_test.cc +++ b/src/lib/log/tests/init_logger_test.cc @@ -20,9 +20,10 @@ using namespace isc::log; /// \brief Test InitLogger /// -/// A test program that initializes using initLogger(), then outputs several -/// messages at different severities. An external script sets the environment -/// variables and checks that they have the desired effect. +/// A program used in testing the logger that initializes logging using +/// initLogger(), then outputs several messages at different severities and +/// debug levels. An external script sets the environment variables and checks +/// that they have the desired effect. int main(int, char**) { From 9e72b16bcbfcdc819cbdc437feb10f73b1694107 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 30 Jun 2011 11:51:13 +0200 Subject: [PATCH 024/974] [trac714] allow non-strict json decoding i.e. allow control-chars in strings --- src/lib/python/isc/cc/message.py | 2 +- src/lib/python/isc/cc/tests/message_test.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/cc/message.py b/src/lib/python/isc/cc/message.py index 3601c41f5e..3ebcc438c8 100644 --- a/src/lib/python/isc/cc/message.py +++ b/src/lib/python/isc/cc/message.py @@ -35,7 +35,7 @@ def from_wire(data): Raises an AttributeError if the given object has no decode() method (which should return a string). ''' - return json.loads(data.decode('utf8')) + return json.loads(data.decode('utf8'), strict=False) if __name__ == "__main__": import doctest diff --git a/src/lib/python/isc/cc/tests/message_test.py b/src/lib/python/isc/cc/tests/message_test.py index 20242018e4..c417068120 100644 --- a/src/lib/python/isc/cc/tests/message_test.py +++ b/src/lib/python/isc/cc/tests/message_test.py @@ -31,6 +31,10 @@ class MessageTest(unittest.TestCase): self.msg2_str = "{\"aaa\": [1, 1.1, true, false, null]}"; self.msg2_wire = self.msg2_str.encode() + self.msg3 = { "aaa": [ 1, 1.1, True, False, "string\n" ] } + self.msg3_str = "{\"aaa\": [1, 1.1, true, false, \"string\n\" ]}"; + self.msg3_wire = self.msg3_str.encode() + def test_encode_json(self): self.assertEqual(self.msg1_wire, isc.cc.message.to_wire(self.msg1)) self.assertEqual(self.msg2_wire, isc.cc.message.to_wire(self.msg2)) @@ -40,6 +44,7 @@ class MessageTest(unittest.TestCase): def test_decode_json(self): self.assertEqual(self.msg1, isc.cc.message.from_wire(self.msg1_wire)) self.assertEqual(self.msg2, isc.cc.message.from_wire(self.msg2_wire)) + self.assertEqual(self.msg3, isc.cc.message.from_wire(self.msg3_wire)) self.assertRaises(AttributeError, isc.cc.message.from_wire, 1) self.assertRaises(ValueError, isc.cc.message.from_wire, b'\x001') From ebc5206327363f747822e7344037d9c2b76b8cd9 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 30 Jun 2011 11:35:07 +0100 Subject: [PATCH 025/974] [trac1029] Add query type to the debug message --- src/lib/datasrc/data_source.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc index 4e1fcde202..b57a967b13 100644 --- a/src/lib/datasrc/data_source.cc +++ b/src/lib/datasrc/data_source.cc @@ -945,7 +945,7 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) { void DataSrc::doQuery(Query& q) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_QUERY_PROCESS).arg(q.qname()). - arg(q.qclass()); + arg(q.qtype()).arg(q.qclass()); Message& m = q.message(); vector additional; From 99c025349129904b864806049ea8761940ba0ecc Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 30 Jun 2011 12:05:36 +0100 Subject: [PATCH 026/974] [trac1024] Change default logging severity and debug level --- src/lib/log/logger_support.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h index 4bc8acc195..7dc0c28e76 100644 --- a/src/lib/log/logger_support.h +++ b/src/lib/log/logger_support.h @@ -76,13 +76,13 @@ void initLogger(const std::string& root, /// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case /// and must not contain leading or trailing spaces.) If not specified (or if /// specified but incorrect), the default passed as argument to this function -/// (currently INFO) will be used. +/// (currently DEBUG) will be used. /// /// B10_LOGGER_DBGLEVEL /// Ignored if the level is not DEBUG, this should be a number between 0 and /// 99 indicating the logging severity. The default is 0. If outside these /// limits or if not a number, The value passed to this function (default -/// of 0) is used. +/// of MAX_DEBUG_LEVEL) is used. /// /// B10_LOGGER_LOCALMSG /// If defined, the path specification of a file that contains message @@ -94,8 +94,8 @@ void initLogger(const std::string& root, /// be overridden by the tester. It is not intended for use in production /// code. -void initLogger(isc::log::Severity severity = isc::log::INFO, - int dbglevel = 0); +void initLogger(isc::log::Severity severity = isc::log::DEBUG, + int dbglevel = isc::log::MAX_DEBUG_LEVEL); } // namespace log } // namespace isc From 4191945aad5aaf0873b15727716d0a988b1c978d Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 30 Jun 2011 13:37:19 +0100 Subject: [PATCH 027/974] [trac1084] Reorder statements in MemoryZone::add Only log details of the zone being added after input validation, when it is known the the zone pointer is non-null. (Validation detects null pointers and throws an exception.) --- src/lib/datasrc/memory_datasrc.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 3c57d1b087..b8019a24b4 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -225,10 +225,13 @@ struct MemoryZone::MemoryZoneImpl { */ // Implementation of MemoryZone::add result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) { + // Sanitize input. This will cause an exception to be thrown + // if the input RRset is empty. + addValidation(rrset); + + // OK, can add the RRset. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET). arg(rrset->getName()).arg(rrset->getType()).arg(origin_); - // Sanitize input - addValidation(rrset); // Add wildcards possibly contained in the owner name to the domain // tree. From 8b21629d234228ff9fbb7a3c5ad5ebeca4b981c1 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 09:13:30 -0500 Subject: [PATCH 028/974] [master] replace wrong references with real content I could have fixed the cross-reference spelling, but instead I just replaced it with real explanation of the format. This is very minor and mentioned on jabber. --- src/bin/resolver/resolver_messages.mes | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes index 6c5be642d6..05b9a8b548 100644 --- a/src/bin/resolver/resolver_messages.mes +++ b/src/bin/resolver/resolver_messages.mes @@ -210,10 +210,14 @@ query in the form of #. % RESOLVER_QUERY_REJECTED query rejected: '%1/%2/%3' from %4 An informational message that indicates an incoming query is rejected in terms of the query ACL. This results in a response with an RCODE of -REFUSED. See QUERYACCEPTED for the information given in the message. +REFUSED. The log message shows the query in the form of //, and the client that sends the +query in the form of #. % RESOLVER_QUERY_DROPPED query dropped: '%1/%2/%3' from %4 An informational message that indicates an incoming query is dropped -in terms of the query ACL. Unlike the QUERYREJECTED case, the server does -not return any response. See QUERYACCEPTED for the information given in -the message. +in terms of the query ACL. Unlike the RESOLVER_QUERY_REJECTED +case, the server does not return any response. The log message +shows the query in the form of //, and the client that sends the query in the form of #. From 666d6e49e1cd46fd293b3fdce239e34588666ed6 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 09:15:41 -0500 Subject: [PATCH 029/974] [master] replace DENY with DROP in developer documentation --- src/lib/acl/loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/acl/loader.h b/src/lib/acl/loader.h index 6b024c9b3b..a2aa2f8efa 100644 --- a/src/lib/acl/loader.h +++ b/src/lib/acl/loader.h @@ -81,7 +81,7 @@ public: * or if it doesn't contain one of the accepted values. * * \param action The JSON representation of the action. It must be a string - * and contain one of "ACCEPT", "REJECT" or "DENY". + * and contain one of "ACCEPT", "REJECT" or "DROP. * \note We could define different names or add aliases if needed. */ BasicAction defaultActionLoader(data::ConstElementPtr action); From cae4ced00386d042535ec9b53b20e9bbc2cdaa20 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 30 Jun 2011 17:03:10 +0200 Subject: [PATCH 030/974] [trac758] logging conversion for bob --- src/bin/bind10/Makefile.am | 8 +- src/bin/bind10/bind10.py.in | 131 +++++++++++------------ src/bin/bind10/bind10_messages.mes | 161 +++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 73 deletions(-) create mode 100644 src/bin/bind10/bind10_messages.mes diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am index cca4a53797..126c429e44 100644 --- a/src/bin/bind10/Makefile.am +++ b/src/bin/bind10/Makefile.am @@ -1,16 +1,17 @@ SUBDIRS = . tests sbin_SCRIPTS = bind10 -CLEANFILES = bind10 bind10.pyc +CLEANFILES = bind10 bind10.pyc bind10_messages.py bind10_messages.pyc pkglibexecdir = $(libexecdir)/@PACKAGE@ +pyexec_DATA = bind10_messages.py bind10dir = $(pkgdatadir) bind10_DATA = bob.spec EXTRA_DIST = bob.spec man_MANS = bind10.8 -EXTRA_DIST += $(man_MANS) bind10.xml +EXTRA_DIST += $(man_MANS) bind10.xml bind10_messages.mes if ENABLE_MAN @@ -19,6 +20,9 @@ bind10.8: bind10.xml endif +bind10_messages.py: bind10_messages.mes + $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/bind10/bind10_messages.mes + # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix bind10: bind10.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in index 48d641d282..3aea1185c4 100755 --- a/src/bin/bind10/bind10.py.in +++ b/src/bin/bind10/bind10.py.in @@ -65,6 +65,16 @@ import posix import isc.cc import isc.util.process import isc.net.parse +import isc.log +from bind10_messages import * + +isc.log.init("b10-bind10") +logger = isc.log.Logger("bind10") + +# Pending system-wide debug level definitions, the ones we +# use here are hardcoded for now +DBG_PROCESS = 10 +DBG_COMMANDS = 30 # Assign this process some longer name isc.util.process.rename(sys.argv[0]) @@ -252,8 +262,7 @@ class BoB: if new_config['start_' + name]: if not started: if self.uid is not None: - sys.stderr.write("[bind10] Starting " + name + " as " + - "a user, not root. This might fail.\n") + logger.warn(BIND10_START_AS_NON_ROOT, name) start() else: stop() @@ -279,9 +288,8 @@ class BoB: self.started_auth_family = False # The real code of the config handler function follows here - if self.verbose: - sys.stdout.write("[bind10] Handling new configuration: " + - str(new_config) + "\n") + logger.debug(DBG_COMMANDS, BIND10_RECEIVED_NEW_CONFIGURATION, + new_config) start_stop('resolver', self.started_resolver_family, resolver_on, resolver_off) start_stop('auth', self.started_auth_family, auth_on, auth_off) @@ -298,8 +306,7 @@ class BoB: return process_list def command_handler(self, command, args): - if self.verbose: - sys.stdout.write("[bind10] Boss got command: " + str(command) + "\n") + logger.debug(DBG_COMMANDS, BIND10_RECEIVED_COMMAND, command) answer = isc.config.ccsession.create_answer(1, "command not implemented") if type(command) != str: answer = isc.config.ccsession.create_answer(1, "bad command") @@ -332,12 +339,10 @@ class BoB: start, this runs through the list of started processes, killing each one. It then clears that list. """ - if self.verbose: - sys.stdout.write("[bind10] killing started processes:\n") + logger.info(BIND10_KILLING_ALL_PROCESSES) for pid in self.processes: - if self.verbose: - sys.stdout.write("[bind10] - %s\n" % self.processes[pid].name) + logger.info(BIND10_KILL_PROCESS, self.processes[pid].name) self.processes[pid].process.kill() self.processes = {} @@ -351,18 +356,14 @@ class BoB: xfrin/xfrout and zone manager as we don't need to start those if we are not running the authoritative server.) """ - if self.verbose: - sys.stdout.write("[bind10] Reading Boss configuration:\n") + logger.info(BIND10_READING_BOSS_CONFIGURATION) config_data = self.ccs.get_full_config() self.cfg_start_auth = config_data.get("start_auth") self.cfg_start_resolver = config_data.get("start_resolver") - if self.verbose: - sys.stdout.write("[bind10] - start_auth: %s\n" % - str(self.cfg_start_auth)) - sys.stdout.write("[bind10] - start_resolver: %s\n" % - str(self.cfg_start_resolver)) + logger.info(BIND10_CONFIGURATION_START_AUTH, self.cfg_start_auth) + logger.info(BIND10_CONFIGURATION_START_RESOLVER, self.cfg_start_resolver) def log_starting(self, process, port = None, address = None): """ @@ -377,13 +378,14 @@ class BoB: appended to the message (if present). """ self.curproc = process - if self.verbose: - sys.stdout.write("[bind10] Starting %s" % self.curproc) - if port is not None: - sys.stdout.write(" on port %d" % port) - if address is not None: - sys.stdout.write(" (address %s)" % str(address)) - sys.stdout.write("\n") + if port is None and address is None: + logger.debug(DBG_PROCESS, BIND10_STARTING_PROCESS, self.curproc) + elif address is None: + logger.debug(DBG_PROCESS, BIND10_STARTING_PROCESS_PORT, self.curproc, + port) + else: + logger.debug(DBG_PROCESS, BIND10_STARTING_PROCESS_PORT_ADDRESS, + self.curproc, address, port) def log_started(self, pid = None): """ @@ -391,11 +393,10 @@ class BoB: message. As with starting_message(), this ensures a consistent format. """ - if self.verbose: - sys.stdout.write("[bind10] Started %s" % self.curproc) - if pid is not None: - sys.stdout.write(" (PID %d)" % pid) - sys.stdout.write("\n") + if pid is None: + logger.debug(DBG_PROCESS, BIND10_STARTED_PROCESS, self.curproc) + else: + logger.debug(DBG_PROCESS, BIND10_STARTED_PROCESS_PID, self.curproc, pid) # The next few methods start the individual processes of BIND-10. They # are called via start_all_processes(). If any fail, an exception is @@ -459,7 +460,9 @@ class BoB: """ self.log_starting("ccsession") self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, - self.config_handler, self.command_handler) + self.config_handler, + self.command_handler, + None, True) self.ccs.start() self.log_started() @@ -620,12 +623,12 @@ class BoB: # running c_channel_env = {} if self.msgq_socket_file is not None: - c_channel_env["BIND10_MSGQ_SOCKET_FILE"] = self.msgq_socket_file - if self.verbose: - sys.stdout.write("[bind10] Checking for already running b10-msgq\n") + c_channel_env["BIND10_MSGQ_SOCKET_FILE"] = self.msgq_socket_file + logger.debug(DBG_PROCESS, BIND10_CHECK_MSGQ_ALREADY_RUNNING) # try to connect, and if we can't wait a short while try: self.cc_session = isc.cc.Session(self.msgq_socket_file) + logger.fatal(BIND10_MSGQ_ALREADY_RUNNING); return "b10-msgq already running, or socket file not cleaned , cannot start" except isc.cc.session.SessionError: # this is the case we want, where the msgq is not running @@ -663,8 +666,7 @@ class BoB: Stop the given process, friendly-like. The process is the name it has (in logs, etc), the recipient is the address on msgq. """ - if self.verbose: - sys.stdout.write("[bind10] Asking %s to terminate\n" % process) + logger.info(BIND10_STOP_PROCESS, process) # TODO: Some timeout to solve processes that don't want to die would # help. We can even store it in the dict, it is used only as a set self.expected_shutdowns[process] = 1 @@ -690,8 +692,7 @@ class BoB: def shutdown(self): """Stop the BoB instance.""" - if self.verbose: - sys.stdout.write("[bind10] Stopping the server.\n") + logger.info(BIND10_SHUTDOWN) # first try using the BIND 10 request to stop try: self.stop_all_processes() @@ -705,9 +706,8 @@ class BoB: # next try sending a SIGTERM processes_to_stop = list(self.processes.values()) for proc_info in processes_to_stop: - if self.verbose: - sys.stdout.write("[bind10] Sending SIGTERM to %s (PID %d).\n" % - (proc_info.name, proc_info.pid)) + logger.info(BIND10_SEND_SIGTERM, proc_info.name, + proc_info.pid) try: proc_info.process.terminate() except OSError: @@ -721,17 +721,15 @@ class BoB: self.reap_children() processes_to_stop = list(self.processes.values()) for proc_info in processes_to_stop: - if self.verbose: - sys.stdout.write("[bind10] Sending SIGKILL to %s (PID %d).\n" % - (proc_info.name, proc_info.pid)) + logger.info(BIND10_SEND_SIGKILL, proc_info.name, + proc_info.pid) try: proc_info.process.kill() except OSError: # ignore these (usually ESRCH because the child # finally exited) pass - if self.verbose: - sys.stdout.write("[bind10] All processes ended, server done.\n") + logger.info(BIND10_SHUTDOWN_COMPLETE) def _get_process_exit_status(self): return os.waitpid(-1, os.WNOHANG) @@ -759,18 +757,16 @@ class BoB: # elsewhere. if self.runnable: if exit_status is None: - sys.stdout.write( - "[bind10] Process %s (PID %d) died: exit status not available" % - (proc_info.name, proc_info.pid)) + logger.info(BIND10_PROCESS_ENDED_NO_EXIT_STATUS, + proc_info.name, proc_info.pid) else: - sys.stdout.write( - "[bind10] Process %s (PID %d) terminated, exit status = %d\n" % - (proc_info.name, proc_info.pid, exit_status)) + logger.info(BIND10_PROCESS_ENDED_WITH_EXIT_STATUS, + proc_info.name, proc_info.pid, + exit_status) # Was it a special process? if proc_info.name == "b10-msgq": - sys.stdout.write( - "[bind10] The b10-msgq process died, shutting down.\n") + logger.fatal(BIND10_MSGQ_DAEMON_ENDED) self.runnable = False # If we're in 'brittle' mode, we want to shutdown after @@ -778,7 +774,7 @@ class BoB: if self.brittle: self.runnable = False else: - sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid) + logger.info(BIND10_UNKNOWN_CHILD_PROCESS_ENDED, pid) def restart_processes(self): """ @@ -809,14 +805,11 @@ class BoB: next_restart = restart_time still_dead[proc_info.pid] = proc_info else: - if self.verbose: - sys.stdout.write("[bind10] Resurrecting dead %s process...\n" % - proc_info.name) + logger.info(BIND10_RESURRECTING_PROCESS, proc_info.name) try: proc_info.respawn() self.processes[proc_info.pid] = proc_info - sys.stdout.write("[bind10] Resurrected %s (PID %d)\n" % - (proc_info.name, proc_info.pid)) + logger.info(BIND10_RESURRECTED_PROCESS, proc_info.name, proc_info.pid) except: still_dead[proc_info.pid] = proc_info # remember any processes that refuse to be resurrected @@ -848,8 +841,7 @@ def fatal_signal(signal_number, stack_frame): """We need to exit (SIGINT or SIGTERM received).""" global options global boss_of_bind - if options.verbose: - sys.stdout.write("[bind10] Received %s.\n" % get_signame(signal_number)) + logger.info(BIND10_RECEIVED_SIGNAL, get_signame(signal_number)) signal.signal(signal.SIGCHLD, signal.SIG_DFL) boss_of_bind.runnable = False @@ -967,12 +959,11 @@ def main(): pass if setuid is None: - sys.stderr.write("bind10: invalid user: '%s'\n" % options.user) + logger.fatal(BIND10_INVALID_USER, options.user) sys.exit(1) # Announce startup. - if options.verbose: - sys.stdout.write("%s\n" % VERSION) + logger.info(BIND10_STARTING, VERSION) # Create wakeup pipe for signal handlers wakeup_pipe = os.pipe() @@ -994,9 +985,9 @@ def main(): setuid, username, options.cmdctl_port, options.brittle) startup_result = boss_of_bind.startup() if startup_result: - sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result) + logger.fatal(BIND10_STARTUP_ERROR, startup_result) sys.exit(1) - sys.stdout.write("[bind10] BIND 10 started\n") + logger.info(BIND10_STARTUP_COMPLETE) dump_pid(options.pid_file) # In our main loop, we check for dead processes or messages @@ -1022,7 +1013,7 @@ def main(): if err.args[0] == errno.EINTR: (rlist, wlist, xlist) = ([], [], []) else: - sys.stderr.write("[bind10] Error with select(); %s\n" % err) + logger.fatal(BIND10_SELECT_ERROR, err) break for fd in rlist + xlist: @@ -1030,8 +1021,7 @@ def main(): try: boss_of_bind.ccs.check_command() except isc.cc.session.ProtocolError: - if options.verbose: - sys.stderr.write("[bind10] msgq channel disappeared.\n") + logger.fatal(BIND10_MSGQ_DISAPPEARED) break elif fd == wakeup_fd: os.read(wakeup_fd, 32) @@ -1039,7 +1029,6 @@ def main(): # shutdown signal.signal(signal.SIGCHLD, signal.SIG_DFL) boss_of_bind.shutdown() - sys.stdout.write("[bind10] BIND 10 exiting\n"); unlink_pid_file(options.pid_file) sys.exit(0) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes new file mode 100644 index 0000000000..33d72fdceb --- /dev/null +++ b/src/bin/bind10/bind10_messages.mes @@ -0,0 +1,161 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# No namespace declaration - these constants go in the global namespace +# of the xfrin messages python module. + +% BIND10_CHECK_MSGQ_ALREADY_RUNNING checking if msgq is already running +The boss process is starting up and will now check if the message bus +daemon is already running. If so, it will not be able to start, as it +needs a dedicated message bus. + +% BIND10_CONFIGURATION_START_AUTH start authoritative server: %1 +This message shows whether or not the authoritative server should be +started according to the configuration. + +% BIND10_CONFIGURATION_START_RESOLVER start resolver: %1 +This message shows whether or not the resolver should be +started according to the configuration. + +% BIND10_INVALID_USER invalid user: '%1' +The boss process was started with the -u option, to drop root priveleges +and continue running as the specified user, but the user is unknown. + +% BIND10_KILL_PROCESS killing process %1 +The boss module is sending a kill signal to the given process, as part +of the process of killing all started processes during a failed startup, +as described for BIND10_KILLING_ALL_PROCESSES + +% BIND10_KILLING_ALL_PROCESSES killing all started processes +The boss module was not able to start every process it needed to start +during startup, and will now kill the processes that did get started. + +% BIND10_MSGQ_ALREADY_RUNNING msgq daemon already running, cannot start +There already appears to me a message bus daemon running. Either an +old process was not shut down correctly, and needs to be killed, or +another instance of BIND10, with the same msgq domain socket, is +running, which needs to be stopped. + +% BIND10_MSGQ_DAEMON_ENDED b10-msgq process died, shutting down +The message bus daemon has died. This is a fatal error, since it may +leave the system in an inconsistent state. BIND10 will now shut down + +% BIND10_MSGQ_DISAPPEARED msgq channel disappeared +While listening on the message bus channel for messages, it suddenly +disappeared. The msgq daemon may have died. This might lead to an +inconsistend state of the system, and BIND 10 will now shut down. + +% BIND10_PROCESS_ENDED_NO_EXIT_STATUS Process %1 (PID %2) died: exit status not available +The given process ended unexpectedly, but no exit status is +available. Depending on which module it was, it may simply be +restarted, or it may be a problem that will cause the boss module to +shut down too. The latter happens if it was the message bus daemon, +which, if it has died suddenly, may leave the system in an +inconsistent state. BIND10 will also shut down now if it has been run +with --brittle. + +% BIND10_PROCESS_ENDED_WITH_EXIT_STATUS Process %1 (PID %2) terminated, exit status = %3 +The given process ended unexpectedly with the given exit status. +Depending on which module it was, it may simply be restarted, or it +may be a problem that will cause the boss module to shut down too. +The latter happens if it was the message bus daemon, which, if it has +died suddenly, may leave the system in an inconsistent state. BIND10 +will also shut down now if it has been run with --brittle. + +% BIND10_READING_BOSS_CONFIGURATION reading boss configuration +The boss process is starting up, and will now process the initial +configuration, as received from the configuration manager. + +% BIND10_RECEIVED_COMMAND received command: %1 +The boss module received a command and shall now process it. The command +is printed. + +% BIND10_RECEIVED_NEW_CONFIGURATION received new configuration: %1 +The boss module received a configuration update and is going to apply +it now. The new configuration is printed. + +% BIND10_RECEIVED_SIGNAL received signal %1 +The boss module received the given signal. + +% BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2) +The given process has been restarted succesfully, and is now running +with the given process id. + +% BIND10_RESURRECTING_PROCESS resurrecting dead %1 process... +The given process has ended unexpectedly, and is now restarted. + +% BIND10_SELECT_ERROR error in select() call: %1 +There was a fatal error in the call to select(), used to see if a child +process has ended or if there is a message on the message bus. This +should not happen under normal circumstances and is considered fatal, +so BIND 10 will now shut down. The specific error is printed. + +% BIND10_SEND_SIGKILL sending SIGKILL to %1 (PID %2) +The boss module is sending a SIGKILL signal to the given process. + +% BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2) +The boss module is sending a SIGTERM signal to the given process. + +% BIND10_SHUTDOWN stopping the server +The boss process received a command or signal telling it to shut down. +It will send shutdown commands to each process. The processes that do +not shut down will then receive a SIGTERM signal. If that doesn't work, +it shall send SIGKILL signals to the processes still alive. + +% BIND10_SHUTDOWN_COMPLETE all processes ended, shutdown complete +All child processes have been stopped, and the boss process will now +stop itself. + +% BIND10_START_AS_NON_ROOT starting %1 as a user, not root. This might fail. +The given module is being started or restarted without root privileges. +If the module needs these privileges, it may have problems starting. +Note that this issue should be resolved by the pending 'socket-creator' +process; once that has been implemented, modules should not need root +privileges anymore. + +% BIND10_STARTED_PROCESS started %1 +The given process has successfully been started + +% BIND10_STARTED_PROCESS_PID started %1 (PID %2) +The given process has successfully been started, and has the given PID. + +% BIND10_STARTING starting BIND10: %1 +Informational message on startup that show the full version. + +% BIND10_STARTING_PROCESS starting process %1 +The boss module is starting the given process. + +% BIND10_STARTING_PROCESS_PORT starting process %1 (to listen on port %2) +The boss module is starting the given process, which will listen on the +given port number. + +% BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2:%3) +The boss module is starting the given process, which will listen on the +given address and port number. + +% BIND10_STARTUP_COMPLETE BIND 10 started +All modules have been successfully started, and BIND 10 is now running. + +% BIND10_STARTUP_ERROR error during startup: %1 +There was a fatal error when BIND10 was trying to start. The error is +shown, and BIND10 will now shut down. + +% BIND10_STOP_PROCESS asking %1 to shut down +The boss module is sending a shutdown command to the given module over +the message channel. + +% BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited +An unknown child process has exited. The PID is printed, but no further +action will be taken by the boss process. + From 348387b8fa68c25873b4ee50881738c9c0e83670 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 30 Jun 2011 17:06:25 +0200 Subject: [PATCH 031/974] [trac758] lowercase two messages --- src/bin/bind10/bind10_messages.mes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 33d72fdceb..8bf8269310 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -56,7 +56,7 @@ While listening on the message bus channel for messages, it suddenly disappeared. The msgq daemon may have died. This might lead to an inconsistend state of the system, and BIND 10 will now shut down. -% BIND10_PROCESS_ENDED_NO_EXIT_STATUS Process %1 (PID %2) died: exit status not available +% BIND10_PROCESS_ENDED_NO_EXIT_STATUS process %1 (PID %2) died: exit status not available The given process ended unexpectedly, but no exit status is available. Depending on which module it was, it may simply be restarted, or it may be a problem that will cause the boss module to @@ -65,7 +65,7 @@ which, if it has died suddenly, may leave the system in an inconsistent state. BIND10 will also shut down now if it has been run with --brittle. -% BIND10_PROCESS_ENDED_WITH_EXIT_STATUS Process %1 (PID %2) terminated, exit status = %3 +% BIND10_PROCESS_ENDED_WITH_EXIT_STATUS process %1 (PID %2) terminated, exit status = %3 The given process ended unexpectedly with the given exit status. Depending on which module it was, it may simply be restarted, or it may be a problem that will cause the boss module to shut down too. From 3f599b883384e9f180f12b06d704ef098e948c8e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 30 Jun 2011 17:08:22 +0200 Subject: [PATCH 032/974] [trac474] Added missing doxygen comment --- src/lib/log/log_formatter.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h index 5f2704c0eb..4cecee2925 100644 --- a/src/lib/log/log_formatter.h +++ b/src/lib/log/log_formatter.h @@ -180,6 +180,9 @@ public: return (*this); } + /// \brief Exception version of arg. + /// + /// Simply for convenience, so the .what() doesn't have to be typed. Formatter& arg(const std::exception& e) { if (logger_) { return (arg(e.what())); From ebe5d465d2995899aa3f95c944e0d32d09ec2034 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 30 Jun 2011 17:14:04 +0200 Subject: [PATCH 033/974] [trac747] Shorten the message prefix So the messages are shorter and the prefix itself doesn't contain an underscore. --- src/lib/server_common/keyring.cc | 6 ++--- src/lib/server_common/portconfig.cc | 16 ++++++------ .../server_common/server_common_messages.mes | 26 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/lib/server_common/keyring.cc b/src/lib/server_common/keyring.cc index 791f20d427..501dfd9a08 100644 --- a/src/lib/server_common/keyring.cc +++ b/src/lib/server_common/keyring.cc @@ -32,7 +32,7 @@ updateKeyring(const std::string&, ConstElementPtr data, const isc::config::ConfigData&) { ConstElementPtr list(data->get("keys")); KeyringPtr load(new TSIGKeyRing); - LOG_DEBUG(logger, DBG_TRACE_BASIC, SRV_COMMON_KEYS_UPDATE); + LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_KEYS_UPDATE); // Note that 'data' only contains explicitly configured config parameters. // So if we use the default list is NULL, rather than an empty list, and @@ -52,7 +52,7 @@ initKeyring(config::ModuleCCSession& session) { // We are already initialized return; } - LOG_DEBUG(logger, DBG_TRACE_BASIC, SRV_COMMON_KEYS_INIT); + LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_KEYS_INIT); session.addRemoteConfig("tsig_keys", updateKeyring, false); } @@ -62,7 +62,7 @@ deinitKeyring(config::ModuleCCSession& session) { // Not initialized, ignore it return; } - LOG_DEBUG(logger, DBG_TRACE_BASIC, SRV_COMMON_KEYS_DEINIT); + LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_KEYS_DEINIT); keyring.reset(); session.removeRemoteConfig("tsig_keys"); } diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc index 83778e0e10..281dd0c0b2 100644 --- a/src/lib/server_common/portconfig.cc +++ b/src/lib/server_common/portconfig.cc @@ -42,7 +42,7 @@ parseAddresses(isc::data::ConstElementPtr addresses, ConstElementPtr addr(addrPair->get("address")); ConstElementPtr port(addrPair->get("port")); if (!addr || ! port) { - LOG_ERROR(logger, SRV_COMMON_ADDRESS_MISSING). + LOG_ERROR(logger, SRVCOMM_ADDRESS_MISSING). arg(addrPair->str()); isc_throw(BadValue, "Address must contain both the IP" "address and port"); @@ -51,7 +51,7 @@ parseAddresses(isc::data::ConstElementPtr addresses, IOAddress(addr->stringValue()); if (port->intValue() < 0 || port->intValue() > 0xffff) { - LOG_ERROR(logger, SRV_COMMON_PORT_RANGE). + LOG_ERROR(logger, SRVCOMM_PORT_RANGE). arg(port->intValue()).arg(addrPair->str()); isc_throw(BadValue, "Bad port value (" << port->intValue() << ")"); @@ -60,14 +60,14 @@ parseAddresses(isc::data::ConstElementPtr addresses, port->intValue())); } catch (const TypeError &e) { // Better error message - LOG_ERROR(logger, SRV_COMMON_ADDRESS_TYPE). + LOG_ERROR(logger, SRVCOMM_ADDRESS_TYPE). arg(addrPair->str()); isc_throw(TypeError, "Address must be a string and port an integer"); } } } else if (addresses->getType() != Element::null) { - LOG_ERROR(logger, SRV_COMMON_ADDRESSES_NOT_LIST); + LOG_ERROR(logger, SRVCOMM_ADDRESSES_NOT_LIST); isc_throw(TypeError, elemName + " config element must be a list"); } } @@ -92,9 +92,9 @@ installListenAddresses(const AddressList& newAddresses, isc::asiodns::DNSService& service) { try { - LOG_DEBUG(logger, DBG_TRACE_BASIC, SRV_COMMON_SET_LISTEN); + LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_SET_LISTEN); BOOST_FOREACH(const AddressPair& addr, newAddresses) { - LOG_DEBUG(logger, DBG_TRACE_VALUES, SRV_COMMON_ADDRESS_VALUE). + LOG_DEBUG(logger, DBG_TRACE_VALUES, SRVCOMM_ADDRESS_VALUE). arg(addr.first).arg(addr.second); } setAddresses(service, newAddresses); @@ -114,12 +114,12 @@ installListenAddresses(const AddressList& newAddresses, * user will get error info, command control can be used to set new * address. So we just catch the exception without propagating outside */ - LOG_ERROR(logger, SRV_COMMON_ADDRESS_FAIL).arg(e); + LOG_ERROR(logger, SRVCOMM_ADDRESS_FAIL).arg(e); try { setAddresses(service, addressStore); } catch (const exception& e2) { - LOG_FATAL(logger, SRV_COMMON_ADDRESS_UNRECOVERABLE).arg(e2); + LOG_FATAL(logger, SRVCOMM_ADDRESS_UNRECOVERABLE).arg(e2); } //Anyway the new configure has problem, we need to notify configure //manager the new configure doesn't work diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes index d22946830a..f5b076c3d9 100644 --- a/src/lib/server_common/server_common_messages.mes +++ b/src/lib/server_common/server_common_messages.mes @@ -16,27 +16,27 @@ $NAMESPACE isc::server_common # \brief Messages for the server_common library -% SRV_COMMON_ADDRESSES_NOT_LIST the address and port specification is not a list +% SRVCOMM_ADDRESSES_NOT_LIST the address and port specification is not a list This points to an error in configuration. What was supposed to be a list of IP address - port pairs isn't a list at all but something else. -% SRV_COMMON_ADDRESS_FAIL failed to listen on addresses (%1) +% SRVCOMM_ADDRESS_FAIL failed to listen on addresses (%1) The server failed to bind to one of the address/port pair it should according to configuration, for reason listed in the message (usually because that pair is already used by other service or missing privileges). The server will try to recover and bind the address/port pairs it was listening before. -% SRV_COMMON_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 +% SRVCOMM_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 This points to an error in configuration. Some address specification is missing either the address or port part and therefore can not be used. -% SRV_COMMON_ADDRESS_TYPE address specification type is invalid in %1 +% SRVCOMM_ADDRESS_TYPE address specification type is invalid in %1 This points to an error in configuration. Some address specification is malformed. The address part must be a string (and a valid IP address, either IPv4 or IPv6) and the port must be integer (in the valid range). -% SRV_COMMON_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2) -The recovery of old addresses after SRV_COMMON_ADDRESS_FAIL also failed (for +% SRVCOMM_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2) +The recovery of old addresses after SRVCOMM_ADDRESS_FAIL also failed (for the reason listed). This should not happen. We would really like to terminate the server right now, but considering that @@ -46,30 +46,30 @@ It probably will not work, but will allow the reconfiguration. We hope to update the configuration system so we can really crash the server happily instead. -% SRV_COMMON_ADDRESS_VALUE address to set: %1#%2 +% SRVCOMM_ADDRESS_VALUE address to set: %1#%2 Debug message. This lists one address and port value of the set of addresses we are going to listen on (eg. there will be one log message -per pair). This appears only after SRV_COMMON_SET_LISTEN, but might +per pair). This appears only after SRVCOMM_SET_LISTEN, but might be hidden, as it has higher debug level. -% SRV_COMMON_KEYS_DEINIT deinitilizing TSIG keyring +% SRVCOMM_KEYS_DEINIT deinitilizing TSIG keyring Debug message indicating that the server is deinilizing the TSIG keyring. This could be seen at server shutdown only, but usually not even there, as leaving the TSIG in memory until the real shutdown and memory reclamation by OS is harmless, so we don't usually do it. -% SRV_COMMON_KEYS_INIT initializing TSIG keyring +% SRVCOMM_KEYS_INIT initializing TSIG keyring Debug message indicating new keyring is being loaded from configuration (either on startup or as a result of configuration update). -% SRV_COMMON_KEYS_UPDATE updating TSIG keyring +% SRVCOMM_KEYS_UPDATE updating TSIG keyring Debug message indicating that the server is initializing global TSIG keyring. This should be seen only at server start. -% SRV_COMMON_PORT_RANGE port out of valid range (%1 in %2) +% SRVCOMM_PORT_RANGE port out of valid range (%1 in %2) This points to an error in configuration. The port in some address specification is out of the valid range (0-65535). -% SRV_COMMON_SET_LISTEN setting addresses to listen to +% SRVCOMM_SET_LISTEN setting addresses to listen to Debug message, noting that the server is about to start listening on a different set of IP addresses and ports. From 534acaf92fd8ba43488be7057d7a35623dcab0a9 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 30 Jun 2011 17:15:39 +0200 Subject: [PATCH 034/974] [trac760] tweak ssl error message --- src/bin/cmdctl/cmdctl.py.in | 2 +- src/bin/cmdctl/cmdctl_messages.mes | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in index 304f62d97f..778d38f368 100755 --- a/src/bin/cmdctl/cmdctl.py.in +++ b/src/bin/cmdctl/cmdctl.py.in @@ -532,7 +532,7 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn, ssl_version = ssl.PROTOCOL_SSLv23) return ssl_sock except (ssl.SSLError, CmdctlException) as err : - logger.info(CMDCTL_SSL_ERROR_USER_DENIED, err) + logger.info(CMDCTL_SSL_SETUP_FAILURE_USER_DENIED, err) self.close_request(sock) # raise socket error to finish the request raise socket.error diff --git a/src/bin/cmdctl/cmdctl_messages.mes b/src/bin/cmdctl/cmdctl_messages.mes index 9ba2a9b075..55b941f1a1 100644 --- a/src/bin/cmdctl/cmdctl_messages.mes +++ b/src/bin/cmdctl/cmdctl_messages.mes @@ -58,7 +58,7 @@ with the tool b10-cmdctl-usermgr. This debug message indicates that the given command is being sent to the given module. -% CMDCTL_SSL_ERROR_USER_DENIED user denied because of SSL error: %1 +% CMDCTL_SSL_SETUP_FAILURE_USER_DENIED failed to create an SSL connection (user denied): %1 The user was denied because the SSL connection could not successfully be set up. The specific error is given in the log message. Possible causes may be that the ssl request itself was bad, or the local key or From 22f3ad26d4bb70a03858d42122b7a648211911c7 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 10:21:35 -0500 Subject: [PATCH 035/974] [master] add copyright / license to the guide XML file. --- doc/guide/bind10-guide.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 7d1a006545..6a4218207a 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -5,6 +5,23 @@ %version; ]> + + + From b4007e4b25d21ba3b693674ca19ead7d202b7de0 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 10:26:10 -0500 Subject: [PATCH 036/974] [bind10-20110705-release] update version to 20110705 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 348708fde1..dcedf95b83 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.59]) -AC_INIT(bind10-devel, 20110519, bind10-dev@isc.org) +AC_INIT(bind10-devel, 20110705, bind10-dev@isc.org) AC_CONFIG_SRCDIR(README) AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([config.h]) From f7af58ec51254d0586ee20ebfae4bd0f8977ed48 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 30 Jun 2011 17:27:32 +0200 Subject: [PATCH 037/974] [trac747] Some language fixes --- src/lib/server_common/portconfig.cc | 2 +- .../server_common/server_common_messages.mes | 47 +++++++++---------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc index 281dd0c0b2..c60e18d2da 100644 --- a/src/lib/server_common/portconfig.cc +++ b/src/lib/server_common/portconfig.cc @@ -67,7 +67,7 @@ parseAddresses(isc::data::ConstElementPtr addresses, } } } else if (addresses->getType() != Element::null) { - LOG_ERROR(logger, SRVCOMM_ADDRESSES_NOT_LIST); + LOG_ERROR(logger, SRVCOMM_ADDRESSES_NOT_LIST).arg(elemName); isc_throw(TypeError, elemName + " config element must be a list"); } } diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes index f5b076c3d9..84d502b570 100644 --- a/src/lib/server_common/server_common_messages.mes +++ b/src/lib/server_common/server_common_messages.mes @@ -16,7 +16,7 @@ $NAMESPACE isc::server_common # \brief Messages for the server_common library -% SRVCOMM_ADDRESSES_NOT_LIST the address and port specification is not a list +% SRVCOMM_ADDRESSES_NOT_LIST the address and port specification is not a list in %1 This points to an error in configuration. What was supposed to be a list of IP address - port pairs isn't a list at all but something else. @@ -24,27 +24,27 @@ IP address - port pairs isn't a list at all but something else. The server failed to bind to one of the address/port pair it should according to configuration, for reason listed in the message (usually because that pair is already used by other service or missing privileges). The server will try -to recover and bind the address/port pairs it was listening before. +to recover and bind the address/port pairs it was listening before, if any. % SRVCOMM_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 -This points to an error in configuration. Some address specification is missing -either the address or port part and therefore can not be used. +This points to an error in configuration. An address specification in the +configuration is missing either an address or port and so cannot be used. The +specification causing the error is given in the message. % SRVCOMM_ADDRESS_TYPE address specification type is invalid in %1 -This points to an error in configuration. Some address specification is -malformed. The address part must be a string (and a valid IP address, either -IPv4 or IPv6) and the port must be integer (in the valid range). +This points to an error in configuration. An address specification in the +configuration malformed. The specification causing the error is given in the +message. A valid specification contains an address part (which must be a string +and must represent a valid IPv4 or IPv6 address) and port (which must be an +integer in the range valid for TCP/UDP ports on your system). % SRVCOMM_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2) -The recovery of old addresses after SRVCOMM_ADDRESS_FAIL also failed (for -the reason listed). This should not happen. +The recovery of old addresses after SRVCOMM_ADDRESS_FAIL also failed for +the reason listed. -We would really like to terminate the server right now, but considering that -the server needs reconfiguration and it is not possible to reconfigure a server -without it being run, we leave it running in inconsistent and broken state. -It probably will not work, but will allow the reconfiguration. We hope to -update the configuration system so we can really crash the server happily -instead. +The condition indicates problems with the server and/or the system on +which it is running. The server will continue running with any other +configured addresses although the service may be severely degraded. % SRVCOMM_ADDRESS_VALUE address to set: %1#%2 Debug message. This lists one address and port value of the set of @@ -52,24 +52,21 @@ addresses we are going to listen on (eg. there will be one log message per pair). This appears only after SRVCOMM_SET_LISTEN, but might be hidden, as it has higher debug level. -% SRVCOMM_KEYS_DEINIT deinitilizing TSIG keyring -Debug message indicating that the server is deinilizing the TSIG keyring. This -could be seen at server shutdown only, but usually not even there, as leaving -the TSIG in memory until the real shutdown and memory reclamation by OS is -harmless, so we don't usually do it. +% SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring +Debug message indicating that the server is deinilizing the TSIG keyring. % SRVCOMM_KEYS_INIT initializing TSIG keyring -Debug message indicating new keyring is being loaded from configuration (either -on startup or as a result of configuration update). - -% SRVCOMM_KEYS_UPDATE updating TSIG keyring Debug message indicating that the server is initializing global TSIG keyring. This should be seen only at server start. +% SRVCOMM_KEYS_UPDATE updating TSIG keyring +Debug message indicating new keyring is being loaded from configuration (either +on startup or as a result of configuration update). + % SRVCOMM_PORT_RANGE port out of valid range (%1 in %2) This points to an error in configuration. The port in some address specification is out of the valid range (0-65535). % SRVCOMM_SET_LISTEN setting addresses to listen to Debug message, noting that the server is about to start listening on a -different set of IP addresses and ports. +different set of IP addresses and ports than before. From 0a836aa297d08b3c375d245f50971cf4cf2760e7 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 10:37:50 -0500 Subject: [PATCH 038/974] [master] add a comment to not edit this xml file. As briefly mentioned in jabber. --- tools/system_messages.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/system_messages.py b/tools/system_messages.py index 6cf3ce9411..7b0d60cc5a 100644 --- a/tools/system_messages.py +++ b/tools/system_messages.py @@ -58,6 +58,12 @@ SEC_HEADER=""" %version; ]> + From 07708b4325680c4731f0d3dc24bca9da3c962d80 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 10:37:50 -0500 Subject: [PATCH 039/974] [bind10-20110705-release][master] add a comment to not edit this xml file. As briefly mentioned in jabber. --- tools/system_messages.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/system_messages.py b/tools/system_messages.py index 6cf3ce9411..7b0d60cc5a 100644 --- a/tools/system_messages.py +++ b/tools/system_messages.py @@ -58,6 +58,12 @@ SEC_HEADER=""" %version; ]> + From f5ae2264a57664aa6ab307865db72f1f740b80c7 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 30 Jun 2011 17:41:24 +0200 Subject: [PATCH 040/974] [trac747] Further language modifications --- src/lib/server_common/server_common_messages.mes | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes index 84d502b570..b85fa1304d 100644 --- a/src/lib/server_common/server_common_messages.mes +++ b/src/lib/server_common/server_common_messages.mes @@ -43,8 +43,9 @@ The recovery of old addresses after SRVCOMM_ADDRESS_FAIL also failed for the reason listed. The condition indicates problems with the server and/or the system on -which it is running. The server will continue running with any other -configured addresses although the service may be severely degraded. +which it is running. The server will continue running to allow +reconfiguration, but will not be listening on any address or port until +an administrator does so. % SRVCOMM_ADDRESS_VALUE address to set: %1#%2 Debug message. This lists one address and port value of the set of @@ -56,8 +57,8 @@ be hidden, as it has higher debug level. Debug message indicating that the server is deinilizing the TSIG keyring. % SRVCOMM_KEYS_INIT initializing TSIG keyring -Debug message indicating that the server is initializing global TSIG keyring. -This should be seen only at server start. +Debug message indicating that the server is initializing the global TSIG +keyring. This should be seen only at server start. % SRVCOMM_KEYS_UPDATE updating TSIG keyring Debug message indicating new keyring is being loaded from configuration (either From 3bbdd1e7d3f89b3a281900c75ceb0830d0cfd7d3 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 11:16:08 -0500 Subject: [PATCH 041/974] [master] cleanup changelog use a tab before the keyword type. use two tabs before the committer username. --- ChangeLog | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4616678cf2..451a1c0a11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -52,7 +52,7 @@ Now builds and runs with Python 3.2 (Trac #710, git dae1d2e24f993e1eef9ab429326652f40a006dfb) -257. [bug] y-aharen +257. [bug] y-aharen Fixed a bug an instance of IntervalTimerImpl may be destructed while deadline_timer is holding the handler. This fix addresses occasional failure of IntervalTimerTest.destructIntervalTimer. @@ -72,12 +72,12 @@ b10-xfrout: failed to send notifies over IPv6 correctly. (Trac964, git 3255c92714737bb461fb67012376788530f16e40) -253. [func] jelte +253. [func] jelte Add configuration options for logging through the virtual module Logging. (Trac 736, git 9fa2a95177265905408c51d13c96e752b14a0824) -252. [func] stephen +252. [func] stephen Add syslog as destination for logging. (Trac976, git 31a30f5485859fd3df2839fc309d836e3206546e) @@ -90,36 +90,36 @@ their permissions must be adjusted by hand (if necessary). (Trac870, git 461fc3cb6ebabc9f3fa5213749956467a14ebfd4) -250. [bug] ocean +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 +249. [func] jerry xfrout: add support for TSIG verification. (Trac816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217) -248. [func] stephen +248. [func] stephen Add file and stderr as destinations for logging. (Trac555, git 38b3546867425bd64dbc5920111a843a3330646b) -247. [func] jelte +247. [func] jelte Upstream queries from the resolver now set EDNS0 buffer size. (Trac834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d) -246. [func] stephen +246. [func] stephen Implement logging using log4cplus (http://log4cplus.sourceforge.net) (Trac899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10) -245. [func] vorner +245. [func] vorner Authoritative server can now sign the answers using TSIG (configured in tsig_keys/keys, list of strings like "name::sha1-hmac"). It doesn't use them for ACL yet, only verifies them and signs if the request is signed. (Trac875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d) -244. [func] stephen +244. [func] stephen In unit tests, allow the choice of whether unhandled exceptions are caught in the unit test program (and details printed) or allowed to propagate to the default exception handler. See the bind10-dev thread From 3caca9f8debed45019acb731b5ef2f55a3479ee4 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 11:29:34 -0500 Subject: [PATCH 042/974] [master] use a space and # hash mark before the Trac number in the ChangeLog --- ChangeLog | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 451a1c0a11..0054c24171 100644 --- a/ChangeLog +++ b/ChangeLog @@ -61,25 +61,25 @@ 256. [bug] jerry src/bin/xfrin: update xfrin to check TSIG before other part of incoming message. - (Trac955, git 261450e93af0b0406178e9ef121f81e721e0855c) + (Trac #955, git 261450e93af0b0406178e9ef121f81e721e0855c) 255. [func] zhang likun src/lib/cache: remove empty code in lib/cache and the corresponding suppression rule in src/cppcheck-suppress.lst. - (Trac639, git 4f714bac4547d0a025afd314c309ca5cb603e212) + (Trac #639, git 4f714bac4547d0a025afd314c309ca5cb603e212) 254. [bug] jinmei b10-xfrout: failed to send notifies over IPv6 correctly. - (Trac964, git 3255c92714737bb461fb67012376788530f16e40) + (Trac #964, git 3255c92714737bb461fb67012376788530f16e40) 253. [func] jelte Add configuration options for logging through the virtual module Logging. - (Trac 736, git 9fa2a95177265905408c51d13c96e752b14a0824) + (Trac #736, git 9fa2a95177265905408c51d13c96e752b14a0824) 252. [func] stephen Add syslog as destination for logging. - (Trac976, git 31a30f5485859fd3df2839fc309d836e3206546e) + (Trac #976, git 31a30f5485859fd3df2839fc309d836e3206546e) 251. [bug]* jinmei Make sure bindctl private files are non readable to anyone except @@ -88,36 +88,36 @@ 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) + (Trac #870, 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) + (Trac #838, git 83e33ec80c0c6485d8b116b13045b3488071770f) 249. [func] jerry xfrout: add support for TSIG verification. - (Trac816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217) + (Trac #816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217) 248. [func] stephen Add file and stderr as destinations for logging. - (Trac555, git 38b3546867425bd64dbc5920111a843a3330646b) + (Trac #555, git 38b3546867425bd64dbc5920111a843a3330646b) 247. [func] jelte Upstream queries from the resolver now set EDNS0 buffer size. - (Trac834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d) + (Trac #834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d) 246. [func] stephen Implement logging using log4cplus (http://log4cplus.sourceforge.net) - (Trac899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10) + (Trac #899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10) 245. [func] vorner Authoritative server can now sign the answers using TSIG (configured in tsig_keys/keys, list of strings like "name::sha1-hmac"). It doesn't use them for ACL yet, only verifies them and signs if the request is signed. - (Trac875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d) + (Trac #875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d) 244. [func] stephen In unit tests, allow the choice of whether unhandled exceptions are @@ -129,7 +129,7 @@ 243. [func]* feng Add optional hmac algorithm SHA224/384/812. - (Trac#782, git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1) + (Trac #782, git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1) bind10-devel-20110519 released on May 19, 2011 @@ -176,7 +176,7 @@ bind10-devel-20110519 released on May 19, 2011 stats module and stats-httpd module, and maybe with other statistical modules in future. "stats.spec" has own configuration and commands of stats module, if it requires. - (Trac#719, git a234b20dc6617392deb8a1e00eb0eed0ff353c0a) + (Trac #719, git a234b20dc6617392deb8a1e00eb0eed0ff353c0a) 236. [func] jelte C++ client side of configuration now uses BIND10 logging system. @@ -219,13 +219,13 @@ bind10-devel-20110519 released on May 19, 2011 instead of '%s,%d', which allows us to cope better with mismatched placeholders and allows reordering of them in case of translation. - (Trac901, git 4903410e45670b30d7283f5d69dc28c2069237d6) + (Trac #901, git 4903410e45670b30d7283f5d69dc28c2069237d6) 230. [bug] naokikambe Removed too repeated verbose messages in two cases of: - when auth sends statistics data to stats - when stats receives statistics data from other modules - (Trac#620, git 0ecb807011196eac01f281d40bc7c9d44565b364) + (Trac #620, git 0ecb807011196eac01f281d40bc7c9d44565b364) 229. [doc] jreed Add manual page for b10-host. From dadf18e8c050ad6a5977fa32d563f31de99d3ac7 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 11:31:57 -0500 Subject: [PATCH 043/974] [master] add a changelog entry for the many trac tickets for log conversions --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 0054c24171..90db8148b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +266. [func] Multiple developers + Convert various error messages, debugging and other output + to the new logging interface, including for b10-resolver, + the resolver library, the CC library, b10-auth, b10-cfgmgr, + b10-xfrin, and b10-xfrout. This includes a lot of new + documentation describing the new log messages. + (Trac #738, #739, #742, #746, #759, #761, #762) + 265. [func]* jinmei b10-resolver: Introduced ACL on incoming queries. By default the resolver accepts queries from ::1 and 127.0.0.1 and rejects all From 734cae300ccd13aacec1f32b283d4d21b5de8fb5 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 11:16:08 -0500 Subject: [PATCH 044/974] [bind10-20110705-release][master] cleanup changelog use a tab before the keyword type. use two tabs before the committer username. --- ChangeLog | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4616678cf2..451a1c0a11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -52,7 +52,7 @@ Now builds and runs with Python 3.2 (Trac #710, git dae1d2e24f993e1eef9ab429326652f40a006dfb) -257. [bug] y-aharen +257. [bug] y-aharen Fixed a bug an instance of IntervalTimerImpl may be destructed while deadline_timer is holding the handler. This fix addresses occasional failure of IntervalTimerTest.destructIntervalTimer. @@ -72,12 +72,12 @@ b10-xfrout: failed to send notifies over IPv6 correctly. (Trac964, git 3255c92714737bb461fb67012376788530f16e40) -253. [func] jelte +253. [func] jelte Add configuration options for logging through the virtual module Logging. (Trac 736, git 9fa2a95177265905408c51d13c96e752b14a0824) -252. [func] stephen +252. [func] stephen Add syslog as destination for logging. (Trac976, git 31a30f5485859fd3df2839fc309d836e3206546e) @@ -90,36 +90,36 @@ their permissions must be adjusted by hand (if necessary). (Trac870, git 461fc3cb6ebabc9f3fa5213749956467a14ebfd4) -250. [bug] ocean +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 +249. [func] jerry xfrout: add support for TSIG verification. (Trac816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217) -248. [func] stephen +248. [func] stephen Add file and stderr as destinations for logging. (Trac555, git 38b3546867425bd64dbc5920111a843a3330646b) -247. [func] jelte +247. [func] jelte Upstream queries from the resolver now set EDNS0 buffer size. (Trac834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d) -246. [func] stephen +246. [func] stephen Implement logging using log4cplus (http://log4cplus.sourceforge.net) (Trac899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10) -245. [func] vorner +245. [func] vorner Authoritative server can now sign the answers using TSIG (configured in tsig_keys/keys, list of strings like "name::sha1-hmac"). It doesn't use them for ACL yet, only verifies them and signs if the request is signed. (Trac875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d) -244. [func] stephen +244. [func] stephen In unit tests, allow the choice of whether unhandled exceptions are caught in the unit test program (and details printed) or allowed to propagate to the default exception handler. See the bind10-dev thread From a5cf5c7b3a6ac9be60a8737f0e36a61897d32acd Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 11:29:34 -0500 Subject: [PATCH 045/974] [bind10-20110705-release][master] use a space and # hash mark before the Trac number in the ChangeLog --- ChangeLog | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 451a1c0a11..0054c24171 100644 --- a/ChangeLog +++ b/ChangeLog @@ -61,25 +61,25 @@ 256. [bug] jerry src/bin/xfrin: update xfrin to check TSIG before other part of incoming message. - (Trac955, git 261450e93af0b0406178e9ef121f81e721e0855c) + (Trac #955, git 261450e93af0b0406178e9ef121f81e721e0855c) 255. [func] zhang likun src/lib/cache: remove empty code in lib/cache and the corresponding suppression rule in src/cppcheck-suppress.lst. - (Trac639, git 4f714bac4547d0a025afd314c309ca5cb603e212) + (Trac #639, git 4f714bac4547d0a025afd314c309ca5cb603e212) 254. [bug] jinmei b10-xfrout: failed to send notifies over IPv6 correctly. - (Trac964, git 3255c92714737bb461fb67012376788530f16e40) + (Trac #964, git 3255c92714737bb461fb67012376788530f16e40) 253. [func] jelte Add configuration options for logging through the virtual module Logging. - (Trac 736, git 9fa2a95177265905408c51d13c96e752b14a0824) + (Trac #736, git 9fa2a95177265905408c51d13c96e752b14a0824) 252. [func] stephen Add syslog as destination for logging. - (Trac976, git 31a30f5485859fd3df2839fc309d836e3206546e) + (Trac #976, git 31a30f5485859fd3df2839fc309d836e3206546e) 251. [bug]* jinmei Make sure bindctl private files are non readable to anyone except @@ -88,36 +88,36 @@ 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) + (Trac #870, 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) + (Trac #838, git 83e33ec80c0c6485d8b116b13045b3488071770f) 249. [func] jerry xfrout: add support for TSIG verification. - (Trac816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217) + (Trac #816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217) 248. [func] stephen Add file and stderr as destinations for logging. - (Trac555, git 38b3546867425bd64dbc5920111a843a3330646b) + (Trac #555, git 38b3546867425bd64dbc5920111a843a3330646b) 247. [func] jelte Upstream queries from the resolver now set EDNS0 buffer size. - (Trac834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d) + (Trac #834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d) 246. [func] stephen Implement logging using log4cplus (http://log4cplus.sourceforge.net) - (Trac899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10) + (Trac #899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10) 245. [func] vorner Authoritative server can now sign the answers using TSIG (configured in tsig_keys/keys, list of strings like "name::sha1-hmac"). It doesn't use them for ACL yet, only verifies them and signs if the request is signed. - (Trac875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d) + (Trac #875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d) 244. [func] stephen In unit tests, allow the choice of whether unhandled exceptions are @@ -129,7 +129,7 @@ 243. [func]* feng Add optional hmac algorithm SHA224/384/812. - (Trac#782, git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1) + (Trac #782, git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1) bind10-devel-20110519 released on May 19, 2011 @@ -176,7 +176,7 @@ bind10-devel-20110519 released on May 19, 2011 stats module and stats-httpd module, and maybe with other statistical modules in future. "stats.spec" has own configuration and commands of stats module, if it requires. - (Trac#719, git a234b20dc6617392deb8a1e00eb0eed0ff353c0a) + (Trac #719, git a234b20dc6617392deb8a1e00eb0eed0ff353c0a) 236. [func] jelte C++ client side of configuration now uses BIND10 logging system. @@ -219,13 +219,13 @@ bind10-devel-20110519 released on May 19, 2011 instead of '%s,%d', which allows us to cope better with mismatched placeholders and allows reordering of them in case of translation. - (Trac901, git 4903410e45670b30d7283f5d69dc28c2069237d6) + (Trac #901, git 4903410e45670b30d7283f5d69dc28c2069237d6) 230. [bug] naokikambe Removed too repeated verbose messages in two cases of: - when auth sends statistics data to stats - when stats receives statistics data from other modules - (Trac#620, git 0ecb807011196eac01f281d40bc7c9d44565b364) + (Trac #620, git 0ecb807011196eac01f281d40bc7c9d44565b364) 229. [doc] jreed Add manual page for b10-host. From 6c3401b4a9fb79bdee7484e1e3c05758d1b0c0ca Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 30 Jun 2011 11:31:57 -0500 Subject: [PATCH 046/974] [bind10-20110705-release][master] add a changelog entry for the many trac tickets for log conversions --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 0054c24171..90db8148b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +266. [func] Multiple developers + Convert various error messages, debugging and other output + to the new logging interface, including for b10-resolver, + the resolver library, the CC library, b10-auth, b10-cfgmgr, + b10-xfrin, and b10-xfrout. This includes a lot of new + documentation describing the new log messages. + (Trac #738, #739, #742, #746, #759, #761, #762) + 265. [func]* jinmei b10-resolver: Introduced ACL on incoming queries. By default the resolver accepts queries from ::1 and 127.0.0.1 and rejects all From ae5aa5618516d4f894bdf5d2aefed76742069644 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 30 Jun 2011 14:06:49 -0700 Subject: [PATCH 047/974] [trac1069] (not directly relevant) cleanup: constify some variables --- .../tests/resolver_config_unittest.cc | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/bin/resolver/tests/resolver_config_unittest.cc b/src/bin/resolver/tests/resolver_config_unittest.cc index 90063016e6..b674d032d8 100644 --- a/src/bin/resolver/tests/resolver_config_unittest.cc +++ b/src/bin/resolver/tests/resolver_config_unittest.cc @@ -100,14 +100,14 @@ TEST_F(ResolverConfig, forwardAddresses) { TEST_F(ResolverConfig, forwardAddressConfig) { // Try putting there some address - ElementPtr config(Element::fromJSON("{" - "\"forward_addresses\": [" - " {" - " \"address\": \"192.0.2.1\"," - " \"port\": 53" - " }" - "]" - "}")); + ConstElementPtr config(Element::fromJSON("{" + "\"forward_addresses\": [" + " {" + " \"address\": \"192.0.2.1\"," + " \"port\": 53" + " }" + "]" + "}")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); EXPECT_TRUE(server.isForwarding()); @@ -127,14 +127,14 @@ TEST_F(ResolverConfig, forwardAddressConfig) { TEST_F(ResolverConfig, rootAddressConfig) { // Try putting there some address - ElementPtr config(Element::fromJSON("{" - "\"root_addresses\": [" - " {" - " \"address\": \"192.0.2.1\"," - " \"port\": 53" - " }" - "]" - "}")); + ConstElementPtr config(Element::fromJSON("{" + "\"root_addresses\": [" + " {" + " \"address\": \"192.0.2.1\"," + " \"port\": 53" + " }" + "]" + "}")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); ASSERT_EQ(1, server.getRootAddresses().size()); @@ -210,12 +210,12 @@ TEST_F(ResolverConfig, timeouts) { } TEST_F(ResolverConfig, timeoutsConfig) { - ElementPtr config = Element::fromJSON("{" - "\"timeout_query\": 1000," - "\"timeout_client\": 2000," - "\"timeout_lookup\": 3000," - "\"retries\": 4" - "}"); + ConstElementPtr config = Element::fromJSON("{" + "\"timeout_query\": 1000," + "\"timeout_client\": 2000," + "\"timeout_lookup\": 3000," + "\"retries\": 4" + "}"); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); EXPECT_EQ(1000, server.getQueryTimeout()); @@ -266,7 +266,7 @@ TEST_F(ResolverConfig, defaultQueryACL) { TEST_F(ResolverConfig, emptyQueryACL) { // Explicitly configured empty ACL should have the same effect. - ElementPtr config(Element::fromJSON("{ \"query_acl\": [] }")); + ConstElementPtr config(Element::fromJSON("{ \"query_acl\": [] }")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.1"))); @@ -276,10 +276,10 @@ TEST_F(ResolverConfig, emptyQueryACL) { TEST_F(ResolverConfig, queryACLIPv4) { // A simple "accept" query for a specific IPv4 address - ElementPtr config(Element::fromJSON( - "{ \"query_acl\": " - " [ {\"action\": \"ACCEPT\"," - " \"from\": \"192.0.2.1\"} ] }")); + ConstElementPtr config(Element::fromJSON( + "{ \"query_acl\": " + " [ {\"action\": \"ACCEPT\"," + " \"from\": \"192.0.2.1\"} ] }")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createClient("192.0.2.1"))); @@ -289,10 +289,10 @@ TEST_F(ResolverConfig, queryACLIPv4) { TEST_F(ResolverConfig, queryACLIPv6) { // same for IPv6 - ElementPtr config(Element::fromJSON( - "{ \"query_acl\": " - " [ {\"action\": \"ACCEPT\"," - " \"from\": \"2001:db8::1\"} ] }")); + ConstElementPtr config(Element::fromJSON( + "{ \"query_acl\": " + " [ {\"action\": \"ACCEPT\"," + " \"from\": \"2001:db8::1\"} ] }")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.1"))); @@ -306,15 +306,15 @@ TEST_F(ResolverConfig, multiEntryACL) { // as it should have been tested in the underlying ACL module. All we // have to do to check is a reasonably complicated ACL configuration is // loaded as expected. - ElementPtr config(Element::fromJSON( - "{ \"query_acl\": " - " [ {\"action\": \"ACCEPT\"," - " \"from\": \"192.0.2.1\"}," - " {\"action\": \"REJECT\"," - " \"from\": \"192.0.2.0/24\"}," - " {\"action\": \"DROP\"," - " \"from\": \"2001:db8::1\"}," - "] }")); + ConstElementPtr config(Element::fromJSON( + "{ \"query_acl\": " + " [ {\"action\": \"ACCEPT\"," + " \"from\": \"192.0.2.1\"}," + " {\"action\": \"REJECT\"," + " \"from\": \"192.0.2.0/24\"}," + " {\"action\": \"DROP\"," + " \"from\": \"2001:db8::1\"}," + "] }")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createClient("192.0.2.1"))); From aedaf51b32a4b31d697b18ecb914af3889d13c2c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 30 Jun 2011 15:15:04 -0700 Subject: [PATCH 048/974] [trac1069] adapted resolver ACL config and processing to the generalized framework. it now accepts "action only" rule, so the test test in that case was updated accordingly. --- src/bin/resolver/Makefile.am | 1 + src/bin/resolver/resolver.cc | 54 +++++---------- src/bin/resolver/resolver.h | 15 ++--- src/bin/resolver/tests/Makefile.am | 1 + .../tests/resolver_config_unittest.cc | 66 ++++++++++++------- src/bin/resolver/tests/resolver_unittest.cc | 2 +- 6 files changed, 65 insertions(+), 74 deletions(-) diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am index bce8307515..4d6f5c36c2 100644 --- a/src/bin/resolver/Makefile.am +++ b/src/bin/resolver/Makefile.am @@ -59,6 +59,7 @@ nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libdns++.la b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la +b10_resolver_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc index be254b7332..1a481c2adb 100644 --- a/src/bin/resolver/resolver.cc +++ b/src/bin/resolver/resolver.cc @@ -26,7 +26,7 @@ #include -#include +#include #include #include @@ -82,7 +82,9 @@ public: client_timeout_(4000), lookup_timeout_(30000), retries_(3), - query_acl_(new Resolver::ClientACL(REJECT)), + // we apply "reject all" (implicit default of the loader) ACL by + // default: + query_acl_(acl::dns::getLoader().load(Element::fromJSON("[]"))), rec_query_(NULL) {} @@ -160,11 +162,11 @@ public: OutputBufferPtr buffer, DNSServer* server); - const Resolver::ClientACL& getQueryACL() const { + const Resolver::QueryACL& getQueryACL() const { return (*query_acl_); } - void setQueryACL(shared_ptr new_acl) { + void setQueryACL(shared_ptr new_acl) { query_acl_ = new_acl; } @@ -192,7 +194,7 @@ public: private: /// ACL on incoming queries - shared_ptr query_acl_; + shared_ptr query_acl_; /// Object to handle upstream queries RecursiveQuery* rec_query_; @@ -514,8 +516,10 @@ ResolverImpl::processNormalQuery(const IOMessage& io_message, const RRClass qclass = question->getClass(); // Apply query ACL - Client client(io_message); - const BasicAction query_action(getQueryACL().execute(client)); + const Client client(io_message); + const BasicAction query_action( + getQueryACL().execute(acl::dns::RequestContext( + client.getRequestSourceIPAddress()))); if (query_action == isc::acl::REJECT) { LOG_INFO(resolver_logger, RESOLVER_QUERY_REJECTED) .arg(question->getName()).arg(qtype).arg(qclass).arg(client); @@ -574,32 +578,6 @@ ResolverImpl::processNormalQuery(const IOMessage& io_message, return (RECURSION); } -namespace { -// This is a simplified ACL parser for the initial implementation with minimal -// external dependency. For a longer term we'll switch to a more generic -// loader with allowing more complicated ACL syntax. -shared_ptr -createQueryACL(isc::data::ConstElementPtr acl_config) { - if (!acl_config) { - return (shared_ptr()); - } - - shared_ptr new_acl( - new Resolver::ClientACL(REJECT)); - BOOST_FOREACH(ConstElementPtr rule, acl_config->listValue()) { - ConstElementPtr action = rule->get("action"); - ConstElementPtr from = rule->get("from"); - if (!action || !from) { - isc_throw(BadValue, "query ACL misses mandatory parameter"); - } - new_acl->append(shared_ptr >( - new IPCheck(from->stringValue())), - defaultActionLoader(action)); - } - return (new_acl); -} -} - ConstElementPtr Resolver::updateConfig(ConstElementPtr config) { LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_CONFIG_UPDATED) @@ -616,8 +594,10 @@ Resolver::updateConfig(ConstElementPtr config) { ConstElementPtr listenAddressesE(config->get("listen_on")); AddressList listenAddresses(parseAddresses(listenAddressesE, "listen_on")); - shared_ptr query_acl(createQueryACL( - config->get("query_acl"))); + const ConstElementPtr query_acl_cfg(config->get("query_acl")); + const shared_ptr query_acl = + query_acl_cfg ? acl::dns::getLoader().load(query_acl_cfg) : + shared_ptr(); bool set_timeouts(false); int qtimeout = impl_->query_timeout_; int ctimeout = impl_->client_timeout_; @@ -777,13 +757,13 @@ Resolver::getListenAddresses() const { return (impl_->listen_); } -const Resolver::ClientACL& +const Resolver::QueryACL& Resolver::getQueryACL() const { return (impl_->getQueryACL()); } void -Resolver::setQueryACL(shared_ptr new_acl) { +Resolver::setQueryACL(shared_ptr new_acl) { if (!new_acl) { isc_throw(InvalidParameter, "NULL pointer is passed to setQueryACL"); } diff --git a/src/bin/resolver/resolver.h b/src/bin/resolver/resolver.h index 9c7812682c..656723d316 100644 --- a/src/bin/resolver/resolver.h +++ b/src/bin/resolver/resolver.h @@ -21,10 +21,9 @@ #include -#include - #include #include +#include #include #include @@ -41,12 +40,6 @@ #include -namespace isc { -namespace server_common { -class Client; -} -} - class ResolverImpl; /** @@ -247,12 +240,12 @@ public: int getRetries() const; // Shortcut typedef used for query ACL. - typedef isc::acl::ACL ClientACL; + typedef isc::acl::ACL QueryACL; /// Get the query ACL. /// /// \exception None - const ClientACL& getQueryACL() const; + const QueryACL& getQueryACL() const; /// Set the new query ACL. /// @@ -265,7 +258,7 @@ public: /// \exception InvalidParameter The given pointer is NULL /// /// \param new_acl The new ACL to replace the existing one. - void setQueryACL(boost::shared_ptr new_acl); + void setQueryACL(boost::shared_ptr new_acl); private: ResolverImpl* impl_; diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am index c5196176f9..97a2ba6509 100644 --- a/src/bin/resolver/tests/Makefile.am +++ b/src/bin/resolver/tests/Makefile.am @@ -39,6 +39,7 @@ 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 run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la +run_unittests_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la diff --git a/src/bin/resolver/tests/resolver_config_unittest.cc b/src/bin/resolver/tests/resolver_config_unittest.cc index b674d032d8..698e535417 100644 --- a/src/bin/resolver/tests/resolver_config_unittest.cc +++ b/src/bin/resolver/tests/resolver_config_unittest.cc @@ -43,6 +43,7 @@ using namespace std; using boost::scoped_ptr; using namespace isc::acl; +using isc::acl::dns::RequestContext; using namespace isc::data; using namespace isc::testutils; using namespace isc::asiodns; @@ -57,19 +58,22 @@ protected: DNSService dnss; Resolver server; scoped_ptr endpoint; - scoped_ptr request; + scoped_ptr query_message; scoped_ptr client; + scoped_ptr request; ResolverConfig() : dnss(ios, NULL, NULL, NULL) { server.setDNSService(dnss); server.setConfigured(); } - const Client& createClient(const string& source_addr) { + const RequestContext& createRequest(const string& source_addr) { endpoint.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress(source_addr), 53210)); - request.reset(new IOMessage(NULL, 0, IOSocket::getDummyUDPSocket(), - *endpoint)); - client.reset(new Client(*request)); - return (*client); + query_message.reset(new IOMessage(NULL, 0, + IOSocket::getDummyUDPSocket(), + *endpoint)); + client.reset(new Client(*query_message)); + request.reset(new RequestContext(client->getRequestSourceIPAddress())); + return (*request); } void invalidTest(const string &JSON, const string& name); }; @@ -253,15 +257,15 @@ TEST_F(ResolverConfig, invalidTimeoutsConfig) { TEST_F(ResolverConfig, defaultQueryACL) { // If no configuration is loaded, the default ACL should reject everything. - EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.1"))); + EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1"))); EXPECT_EQ(REJECT, server.getQueryACL().execute( - createClient("2001:db8::1"))); + createRequest("2001:db8::1"))); // The following would be allowed if the server had loaded the default // configuration from the spec file. In this context it should not have // happened, and they should be rejected just like the above cases. - EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("127.0.0.1"))); - EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("::1"))); + EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("127.0.0.1"))); + EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("::1"))); } TEST_F(ResolverConfig, emptyQueryACL) { @@ -269,9 +273,9 @@ TEST_F(ResolverConfig, emptyQueryACL) { ConstElementPtr config(Element::fromJSON("{ \"query_acl\": [] }")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); - EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.1"))); + EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1"))); EXPECT_EQ(REJECT, server.getQueryACL().execute( - createClient("2001:db8::1"))); + createRequest("2001:db8::1"))); } TEST_F(ResolverConfig, queryACLIPv4) { @@ -282,9 +286,9 @@ TEST_F(ResolverConfig, queryACLIPv4) { " \"from\": \"192.0.2.1\"} ] }")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); - EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createClient("192.0.2.1"))); + EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1"))); EXPECT_EQ(REJECT, server.getQueryACL().execute( - createClient("2001:db8::1"))); + createRequest("2001:db8::1"))); } TEST_F(ResolverConfig, queryACLIPv6) { @@ -295,9 +299,9 @@ TEST_F(ResolverConfig, queryACLIPv6) { " \"from\": \"2001:db8::1\"} ] }")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); - EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.1"))); + EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1"))); EXPECT_EQ(ACCEPT, server.getQueryACL().execute( - createClient("2001:db8::1"))); + createRequest("2001:db8::1"))); } TEST_F(ResolverConfig, multiEntryACL) { @@ -317,14 +321,15 @@ TEST_F(ResolverConfig, multiEntryACL) { "] }")); ConstElementPtr result(server.updateConfig(config)); EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire()); - EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createClient("192.0.2.1"))); - EXPECT_EQ(REJECT, server.getQueryACL().execute(createClient("192.0.2.2"))); + EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1"))); + EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.2"))); EXPECT_EQ(DROP, server.getQueryACL().execute( - createClient("2001:db8::1"))); + createRequest("2001:db8::1"))); EXPECT_EQ(REJECT, server.getQueryACL().execute( - createClient("2001:db8::2"))); // match the default rule + createRequest("2001:db8::2"))); // match the default rule } + int getResultCode(ConstElementPtr result) { int rcode; @@ -332,6 +337,22 @@ getResultCode(ConstElementPtr result) { return (rcode); } +TEST_F(ResolverConfig, queryACLActionOnly) { + // "action only" rule will be accepted by the loader, which can + // effectively change the default action. + ConstElementPtr config(Element::fromJSON( + "{ \"query_acl\": " + " [ {\"action\": \"ACCEPT\"," + " \"from\": \"192.0.2.1\"}," + " {\"action\": \"DROP\"} ] }")); + EXPECT_EQ(0, getResultCode(server.updateConfig(config))); + EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1"))); + + // We reject non matching queries by default, but the last resort + // rule should have changed the action in that case to "DROP". + EXPECT_EQ(DROP, server.getQueryACL().execute(createRequest("192.0.2.2"))); +} + TEST_F(ResolverConfig, badQueryACL) { // Most of these cases shouldn't happen in practice because the syntax // check should be performed before updateConfig(). But we check at @@ -346,10 +367,6 @@ TEST_F(ResolverConfig, badQueryACL) { server.updateConfig( Element::fromJSON("{ \"query_acl\":" " [ {\"from\": \"192.0.2.1\"} ] }")))); - EXPECT_EQ(1, getResultCode( - server.updateConfig( - Element::fromJSON("{ \"query_acl\":" - " [ {\"action\": \"DROP\"} ] }")))); // invalid "action" EXPECT_EQ(1, getResultCode( server.updateConfig( @@ -361,7 +378,6 @@ TEST_F(ResolverConfig, badQueryACL) { Element::fromJSON("{ \"query_acl\":" " [ {\"action\": \"BADACTION\"," " \"from\": \"192.0.2.1\"}]}")))); - // invalid "from" EXPECT_EQ(1, getResultCode( server.updateConfig( diff --git a/src/bin/resolver/tests/resolver_unittest.cc b/src/bin/resolver/tests/resolver_unittest.cc index 9bcc261f73..6eda44268c 100644 --- a/src/bin/resolver/tests/resolver_unittest.cc +++ b/src/bin/resolver/tests/resolver_unittest.cc @@ -157,7 +157,7 @@ TEST_F(ResolverTest, setQueryACL) { // valid cases are tested through other tests. We only explicitly check // an invalid case: passing a NULL shared pointer. EXPECT_THROW(server.setQueryACL( - boost::shared_ptr()), + boost::shared_ptr()), isc::InvalidParameter); } From f0445f392c1e2c99acfe9117ad36eef0811bd68b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 30 Jun 2011 15:29:36 -0700 Subject: [PATCH 049/974] [trac1069] explicitly catch a null pointer in Loader::load() (resulting in an exception). not directly related to the subject of this branch, but the case was encountered during the resolver test. IMO, the library shouldn't cause crash due to broken input even from a buggy implementation, and even if the validation is normally expected to be done at a higher level. --- src/lib/acl/loader.h | 27 ++++++++++++++++++++++----- src/lib/acl/tests/loader_test.cc | 4 ++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/lib/acl/loader.h b/src/lib/acl/loader.h index 6b024c9b3b..ff9827bab5 100644 --- a/src/lib/acl/loader.h +++ b/src/lib/acl/loader.h @@ -15,7 +15,8 @@ #ifndef ACL_LOADER_H #define ACL_LOADER_H -#include "acl.h" +#include +#include #include #include #include @@ -297,16 +298,28 @@ public: * \brief Load an ACL. * * This parses an ACL list, creates the checks and actions of each element - * and returns it. It may throw LoaderError if it isn't a list or the - * "action" key is missing in some element. Also, no exceptions from - * loadCheck (therefore from whatever creator is used) and from the - * actionLoader passed to constructor are not caught. + * and returns it. + * + * No exceptions from \c loadCheck (therefore from whatever creator is + * used) and from the actionLoader passed to constructor are not caught. + * + * \exception InvalidParameter The given element is NULL (most likely a + * caller's bug) + * \exception LoaderError The given element isn't a list or the + * "action" key is missing in some element * * \param description The JSON list of ACL. + * + * \return The newly created ACL object */ boost::shared_ptr > load(const data::ConstElementPtr& description) const { + if (!description) { + isc_throw(isc::InvalidParameter, + "Null description is passed to ACL loader"); + } + // We first check it's a list, so we can use the list reference // (the list may be huge) if (description->getType() != data::Element::list) { @@ -460,3 +473,7 @@ private: #include "logic_check.h" #endif + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/acl/tests/loader_test.cc b/src/lib/acl/tests/loader_test.cc index 4415081f7f..1705c0a6f8 100644 --- a/src/lib/acl/tests/loader_test.cc +++ b/src/lib/acl/tests/loader_test.cc @@ -13,6 +13,7 @@ // PERFORMANCE OF THIS SOFTWARE. #include "creators.h" +#include #include #include #include @@ -373,7 +374,10 @@ TEST_F(LoaderTest, ACLPropagate) { Element::fromJSON( "[{\"action\": \"ACCEPT\", \"throw\": 1}]")), TestCreatorError); +} +TEST_F(LoaderTest, nullDescription) { + EXPECT_THROW(loader_.load(ConstElementPtr()), isc::InvalidParameter); } } From d2dc7a3ef911a5ab83527753f351bc99440d60fe Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 30 Jun 2011 16:09:01 -0700 Subject: [PATCH 050/974] [trac1069] documentation update, mainly for RequestContext. note that other members than the remote IP address were removed to avoid defining things that "you ain't gonna need (it)". But added comments about possibility of these extensions. --- src/lib/acl/dns.h | 82 ++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h index ce65c36559..bb0834d7ee 100644 --- a/src/lib/acl/dns.h +++ b/src/lib/acl/dns.h @@ -13,7 +13,7 @@ // PERFORMANCE OF THIS SOFTWARE. #ifndef ACL_DNS_H -#define ACL_DNS_H +#define ACL_DNS_H 1 #include #include @@ -25,9 +25,6 @@ #include #include -#include -#include - namespace isc { namespace acl { namespace dns { @@ -38,50 +35,55 @@ namespace dns { * This plays the role of Context of the generic template ACLs (in namespace * isc::acl). * - * It is simple structure holding just the bunch of information. Therefore - * the names don't end up with a slash, there are no methods so they can't be - * confused with local variables. + * It is a simple structure holding just the bunch of information. Therefore + * the names don't end up with an underscore; there are no methods so they + * can't be confused with local variables. * - * \todo Do we want a constructor to set this in a shorter manner? So we can - * call the ACLs directly? + * This structure is generally expected to be ephemeral and read-only: It + * would be constructed immediately before a particular ACL is checked + * and used only for the ACL match purposes. Due to this nature, and since + * ACL processing is often performance sensitive (typically it's performed + * against all incoming packets), the construction is designed to be + * lightweight: it tries to avoid expensive data copies or dynamic memory + * allocation as much as possible. Specifically, the constructor can + * take a pointer or reference to an object and keeps it as a reference + * (not making a local copy). This also means the caller is responsible for + * keeping the passed parameters valid while this structure is used. + * This should generally be reasonable as this structure is expected to be + * used only for a very short period as stated above. + * + * Based on the minimalist philosophy, the initial implementation only + * maintains the remote (source) IP address of the request. The plan is + * to add more parameters of the request. A scheduled next step is to + * support the TSIG key (if it's included in the request). Other possibilities + * are the local (destination) IP address, the remote and local port numbers, + * various fields of the DNS request (e.g. a particular header flag value). */ struct RequestContext { + /// The constructor + /// + /// This is a trivial constructor that perform straightforward + /// initialization of the member variables from the given parameters. + /// + /// \exception None + /// + /// \parameter remote_address_param The remote IP address explicit RequestContext(const IPAddress& remote_address_param) : remote_address(remote_address_param) {} + /// + /// \name Parameter variables + /// + /// These member variables must be immutable so that the integrity of + /// the structure is kept throughout its lifetime. The easiest way is + /// to declare the variable as const. If it's not possible for a + /// particular variable, it must be defined as private and accessible + /// only via an accessor method. + //@{ + /// \brief The remote IP address (eg. the client's IP address). const IPAddress& remote_address; - -#ifdef notyet - /// \brief The DNS message (payload). - isc::dns::ConstMessagePtr message; - - /// \brief The remote IP address (eg. the client). - asiolink::IOAddress remote_address; - - /// \brief The local IP address (ours, of the interface where we received). - asiolink::IOAddress local_address; - - /// \brief The remote port. - uint16_t remote_port; - - /// \brief The local port. - uint16_t local_port; - - /** - * \brief Name of the TSIG key the message is signed with. - * - * This will be either the name of the TSIG key the message is signed with, - * or empty string, if the message is not signed. It is true we could get - * the information from the message itself, but because at the time when - * the ACL is checked, the signature has been verified already, so passing - * it around is probably cheaper. - * - * It is expected that messages with invalid signatures are handled before - * ACL. - */ - std::string tsig_key_name; -#endif + //@} }; /// \brief DNS based check. From e3ef2529a0582f0b146ea7326cf2d52312149cf9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 30 Jun 2011 17:43:51 -0700 Subject: [PATCH 051/974] [trac758] fixed misspellings --- src/bin/bind10/bind10_messages.mes | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 8bf8269310..b6604a09ff 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -29,7 +29,7 @@ This message shows whether or not the resolver should be started according to the configuration. % BIND10_INVALID_USER invalid user: '%1' -The boss process was started with the -u option, to drop root priveleges +The boss process was started with the -u option, to drop root privileges and continue running as the specified user, but the user is unknown. % BIND10_KILL_PROCESS killing process %1 @@ -54,7 +54,7 @@ leave the system in an inconsistent state. BIND10 will now shut down % BIND10_MSGQ_DISAPPEARED msgq channel disappeared While listening on the message bus channel for messages, it suddenly disappeared. The msgq daemon may have died. This might lead to an -inconsistend state of the system, and BIND 10 will now shut down. +inconsistent state of the system, and BIND 10 will now shut down. % BIND10_PROCESS_ENDED_NO_EXIT_STATUS process %1 (PID %2) died: exit status not available The given process ended unexpectedly, but no exit status is @@ -89,7 +89,7 @@ it now. The new configuration is printed. The boss module received the given signal. % BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2) -The given process has been restarted succesfully, and is now running +The given process has been restarted successfully, and is now running with the given process id. % BIND10_RESURRECTING_PROCESS resurrecting dead %1 process... From 1b11baa7c10783eb9d53c24c7f1deb1c0a424105 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 30 Jun 2011 18:54:36 -0700 Subject: [PATCH 052/974] [trac758] trivial editorial fixes. --- src/bin/bind10/bind10_messages.mes | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index b6604a09ff..e2cc17f20d 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -49,7 +49,7 @@ running, which needs to be stopped. % BIND10_MSGQ_DAEMON_ENDED b10-msgq process died, shutting down The message bus daemon has died. This is a fatal error, since it may -leave the system in an inconsistent state. BIND10 will now shut down +leave the system in an inconsistent state. BIND10 will now shut down. % BIND10_MSGQ_DISAPPEARED msgq channel disappeared While listening on the message bus channel for messages, it suddenly @@ -125,13 +125,13 @@ process; once that has been implemented, modules should not need root privileges anymore. % BIND10_STARTED_PROCESS started %1 -The given process has successfully been started +The given process has successfully been started. % BIND10_STARTED_PROCESS_PID started %1 (PID %2) The given process has successfully been started, and has the given PID. % BIND10_STARTING starting BIND10: %1 -Informational message on startup that show the full version. +Informational message on startup that shows the full version. % BIND10_STARTING_PROCESS starting process %1 The boss module is starting the given process. From 690a04dcd63dce08a69e648223320e922f82b3d6 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 30 Jun 2011 18:55:24 -0700 Subject: [PATCH 053/974] [trac758] cleanup: removed an unnecessary semicolon --- src/bin/bind10/bind10.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in index 3aea1185c4..e9e23a96f8 100755 --- a/src/bin/bind10/bind10.py.in +++ b/src/bin/bind10/bind10.py.in @@ -628,7 +628,7 @@ class BoB: # try to connect, and if we can't wait a short while try: self.cc_session = isc.cc.Session(self.msgq_socket_file) - logger.fatal(BIND10_MSGQ_ALREADY_RUNNING); + logger.fatal(BIND10_MSGQ_ALREADY_RUNNING) return "b10-msgq already running, or socket file not cleaned , cannot start" except isc.cc.session.SessionError: # this is the case we want, where the msgq is not running From 05c064b4bd3ea51553a34e37099aa1053c141060 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 1 Jul 2011 12:55:09 +0200 Subject: [PATCH 054/974] [trac758] addressed (most of the) review comments --- src/bin/bind10/bind10.py.in | 10 ++++++---- src/bin/bind10/bind10_messages.mes | 22 +++++++++------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in index e9e23a96f8..a74a63e349 100755 --- a/src/bin/bind10/bind10.py.in +++ b/src/bin/bind10/bind10.py.in @@ -262,7 +262,7 @@ class BoB: if new_config['start_' + name]: if not started: if self.uid is not None: - logger.warn(BIND10_START_AS_NON_ROOT, name) + logger.info(BIND10_START_AS_NON_ROOT, name) start() else: stop() @@ -368,7 +368,8 @@ class BoB: def log_starting(self, process, port = None, address = None): """ A convenience function to output a "Starting xxx" message if the - verbose option is set. Putting this into a separate method ensures + logging is set to DEBUG with debuglevel DBG_PROCESS or higher. + Putting this into a separate method ensures that the output form is consistent across all processes. The process name (passed as the first argument) is put into @@ -757,10 +758,10 @@ class BoB: # elsewhere. if self.runnable: if exit_status is None: - logger.info(BIND10_PROCESS_ENDED_NO_EXIT_STATUS, + logger.warn(BIND10_PROCESS_ENDED_NO_EXIT_STATUS, proc_info.name, proc_info.pid) else: - logger.info(BIND10_PROCESS_ENDED_WITH_EXIT_STATUS, + logger.warn(BIND10_PROCESS_ENDED_WITH_EXIT_STATUS, proc_info.name, proc_info.pid, exit_status) @@ -1022,6 +1023,7 @@ def main(): boss_of_bind.ccs.check_command() except isc.cc.session.ProtocolError: logger.fatal(BIND10_MSGQ_DISAPPEARED) + self.runnable = False break elif fd == wakeup_fd: os.read(wakeup_fd, 32) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index e2cc17f20d..718efefb3a 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -28,21 +28,21 @@ started according to the configuration. This message shows whether or not the resolver should be started according to the configuration. -% BIND10_INVALID_USER invalid user: '%1' +% BIND10_INVALID_USER invalid user: %1 The boss process was started with the -u option, to drop root privileges and continue running as the specified user, but the user is unknown. % BIND10_KILL_PROCESS killing process %1 -The boss module is sending a kill signal to the given process, as part -of the process of killing all started processes during a failed startup, -as described for BIND10_KILLING_ALL_PROCESSES +The boss module is sending a kill signal to process with the given name, +as part of the process of killing all started processes during a failed +startup, as described for BIND10_KILLING_ALL_PROCESSES % BIND10_KILLING_ALL_PROCESSES killing all started processes The boss module was not able to start every process it needed to start during startup, and will now kill the processes that did get started. % BIND10_MSGQ_ALREADY_RUNNING msgq daemon already running, cannot start -There already appears to me a message bus daemon running. Either an +There already appears to be a message bus daemon running. Either an old process was not shut down correctly, and needs to be killed, or another instance of BIND10, with the same msgq domain socket, is running, which needs to be stopped. @@ -58,12 +58,8 @@ inconsistent state of the system, and BIND 10 will now shut down. % BIND10_PROCESS_ENDED_NO_EXIT_STATUS process %1 (PID %2) died: exit status not available The given process ended unexpectedly, but no exit status is -available. Depending on which module it was, it may simply be -restarted, or it may be a problem that will cause the boss module to -shut down too. The latter happens if it was the message bus daemon, -which, if it has died suddenly, may leave the system in an -inconsistent state. BIND10 will also shut down now if it has been run -with --brittle. +available. See BIND10_PROCESS_ENDED_WITH_EXIT_STATUS for a longer +description. % BIND10_PROCESS_ENDED_WITH_EXIT_STATUS process %1 (PID %2) terminated, exit status = %3 The given process ended unexpectedly with the given exit status. @@ -109,7 +105,7 @@ The boss module is sending a SIGTERM signal to the given process. % BIND10_SHUTDOWN stopping the server The boss process received a command or signal telling it to shut down. -It will send shutdown commands to each process. The processes that do +It will send a shutdown command to each process. The processes that do not shut down will then receive a SIGTERM signal. If that doesn't work, it shall send SIGKILL signals to the processes still alive. @@ -122,7 +118,7 @@ The given module is being started or restarted without root privileges. If the module needs these privileges, it may have problems starting. Note that this issue should be resolved by the pending 'socket-creator' process; once that has been implemented, modules should not need root -privileges anymore. +privileges anymore. See tickets #800 and #801 for more information. % BIND10_STARTED_PROCESS started %1 The given process has successfully been started. From b9755c94e619471f8d9769c7c0d230c1e40b9584 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 1 Jul 2011 12:57:10 +0200 Subject: [PATCH 055/974] [trac741] Logging statements in LocalZone --- src/lib/cache/cache_messages.mes | 6 ++++++ src/lib/cache/local_zone_data.cc | 4 ++++ src/lib/cache/logger.cc | 2 +- src/lib/cache/logger.h | 4 +++- src/lib/cache/tests/run_unittests.cc | 4 ++++ 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index e1fbf368d9..7637e0c882 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -13,3 +13,9 @@ # PERFORMANCE OF THIS SOFTWARE. $NAMESPACE isc::cache + +% CACHE_LOCALZONE_UPDATE updating local zone element at key %1 + +% CACHE_LOCALZONE_UNKNOWN entry with key %1 not found in local zone data + +% CACHE_LOCALZONE_FOUND found entry with key %1 in local zone data diff --git a/src/lib/cache/local_zone_data.cc b/src/lib/cache/local_zone_data.cc index 61ce35a1b8..13d1d75d88 100644 --- a/src/lib/cache/local_zone_data.cc +++ b/src/lib/cache/local_zone_data.cc @@ -16,6 +16,7 @@ #include "local_zone_data.h" #include "cache_entry_key.h" #include "rrset_copy.h" +#include "logger.h" using namespace std; using namespace isc::dns; @@ -33,8 +34,10 @@ LocalZoneData::lookup(const isc::dns::Name& name, string key = genCacheEntryName(name, type); RRsetMapIterator iter = rrsets_map_.find(key); if (iter == rrsets_map_.end()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_LOCALZONE_UNKNOWN).arg(key); return (RRsetPtr()); } else { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_LOCALZONE_FOUND).arg(key); return (iter->second); } } @@ -43,6 +46,7 @@ void LocalZoneData::update(const isc::dns::RRset& rrset) { //TODO Do we really need to recreate the rrset again? string key = genCacheEntryName(rrset.getName(), rrset.getType()); + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_LOCALZONE_UPDATE).arg(key); RRset* rrset_copy = new RRset(rrset.getName(), rrset.getClass(), rrset.getType(), rrset.getTTL()); diff --git a/src/lib/cache/logger.cc b/src/lib/cache/logger.cc index bcf37ecccf..f4b0f25494 100644 --- a/src/lib/cache/logger.cc +++ b/src/lib/cache/logger.cc @@ -15,7 +15,7 @@ #include namespace isc { -namespace datasrc { +namespace cache { isc::log::Logger logger("cache"); diff --git a/src/lib/cache/logger.h b/src/lib/cache/logger.h index 46a48b8e08..8159ed4fa3 100644 --- a/src/lib/cache/logger.h +++ b/src/lib/cache/logger.h @@ -26,7 +26,7 @@ /// cc files. namespace isc { -namespace datasrc { +namespace cache { /// \brief The logger for this library extern isc::log::Logger logger; @@ -34,6 +34,8 @@ extern isc::log::Logger logger; enum { /// \brief Trace basic operations DBG_TRACE_BASIC = 10, + /// \brief Trace data operations + DBG_TRACE_DATA = 40, }; } diff --git a/src/lib/cache/tests/run_unittests.cc b/src/lib/cache/tests/run_unittests.cc index b75fc06606..370bc698a8 100644 --- a/src/lib/cache/tests/run_unittests.cc +++ b/src/lib/cache/tests/run_unittests.cc @@ -19,11 +19,15 @@ #include +#include + int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR); isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR); + isc::log::initLogger(); + return (isc::util::unittests::run_all()); } From 9ef0e6eeb1ec8477b1f6867d118d4c599f41c0ae Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 1 Jul 2011 13:20:21 +0200 Subject: [PATCH 056/974] [trac758] change address:port notation to address#port --- src/bin/bind10/bind10_messages.mes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 718efefb3a..3f5f637415 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -136,9 +136,9 @@ The boss module is starting the given process. The boss module is starting the given process, which will listen on the given port number. -% BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2:%3) +% BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2#%3) The boss module is starting the given process, which will listen on the -given address and port number. +given address and port number (written as
#). % BIND10_STARTUP_COMPLETE BIND 10 started All modules have been successfully started, and BIND 10 is now running. From ee8c5f5bee062c8943e955184146d839c05bd2da Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 1 Jul 2011 13:27:09 +0200 Subject: [PATCH 057/974] [trac758] change logger and root name and level of starting messages --- src/bin/bind10/bind10.py.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in index a74a63e349..6e4997dc49 100755 --- a/src/bin/bind10/bind10.py.in +++ b/src/bin/bind10/bind10.py.in @@ -68,8 +68,8 @@ import isc.net.parse import isc.log from bind10_messages import * -isc.log.init("b10-bind10") -logger = isc.log.Logger("bind10") +isc.log.init("b10-boss") +logger = isc.log.Logger("boss") # Pending system-wide debug level definitions, the ones we # use here are hardcoded for now @@ -380,12 +380,12 @@ class BoB: """ self.curproc = process if port is None and address is None: - logger.debug(DBG_PROCESS, BIND10_STARTING_PROCESS, self.curproc) + logger.info(BIND10_STARTING_PROCESS, self.curproc) elif address is None: - logger.debug(DBG_PROCESS, BIND10_STARTING_PROCESS_PORT, self.curproc, + logger.info(BIND10_STARTING_PROCESS_PORT, self.curproc, port) else: - logger.debug(DBG_PROCESS, BIND10_STARTING_PROCESS_PORT_ADDRESS, + logger.info(BIND10_STARTING_PROCESS_PORT_ADDRESS, self.curproc, address, port) def log_started(self, pid = None): From 612a96ab3eea34e232fd97e834599745401b73eb Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 1 Jul 2011 13:39:14 +0200 Subject: [PATCH 058/974] [trac741] Log statements for message cache --- src/lib/cache/cache_messages.mes | 16 ++++++++++++++++ src/lib/cache/message_cache.cc | 20 ++++++++++++++++++++ src/lib/cache/message_cache.h | 2 +- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 7637e0c882..429c74630c 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -19,3 +19,19 @@ $NAMESPACE isc::cache % CACHE_LOCALZONE_UNKNOWN entry with key %1 not found in local zone data % CACHE_LOCALZONE_FOUND found entry with key %1 in local zone data + +% CACHE_MESSAGES_INIT initialized message cache for %1 %2 messages + +% CACHE_MESSAGES_DEINIT deinitialized message cache + +% CACHE_MESSAGES_FOUND found a message entry for %1 in the message cache + +% CACHE_MESSAGES_EXPIRED found an expired message entry for %1 in the message cache + +% CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache + +% CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3 + +% CACHE_MESSAGES_UPDATE updating message entry %1/%2/%3 + +% CACHE_MESSAGES_REMOVE removing old instance of %1/%2/%3 first diff --git a/src/lib/cache/message_cache.cc b/src/lib/cache/message_cache.cc index 816ffe330b..e141bb52f5 100644 --- a/src/lib/cache/message_cache.cc +++ b/src/lib/cache/message_cache.cc @@ -1,6 +1,7 @@ // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or 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. // @@ -20,6 +21,7 @@ #include "message_cache.h" #include "message_utility.h" #include "cache_entry_key.h" +#include "logger.h" namespace isc { namespace cache { @@ -39,11 +41,14 @@ MessageCache::MessageCache(const RRsetCachePtr& rrset_cache, message_lru_((3 * cache_size), new HashDeleter(message_table_)) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_MESSAGES_INIT).arg(cache_size). + arg(RRClass(message_class)); } MessageCache::~MessageCache() { // Destroy all the message entries in the cache. message_lru_.clear(); + LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_MESSAGES_DEINIT); } bool @@ -57,26 +62,38 @@ MessageCache::lookup(const isc::dns::Name& qname, if(msg_entry) { // Check whether the message entry has expired. if (msg_entry->getExpireTime() > time(NULL)) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_FOUND). + arg(entry_name); message_lru_.touch(msg_entry); return (msg_entry->genMessage(time(NULL), response)); } else { // message entry expires, remove it from hash table and lru list. + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_EXPIRED). + arg(entry_name); message_table_.remove(entry_key); message_lru_.remove(msg_entry); return (false); } } + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_UNKNOWN).arg(entry_name); return (false); } bool MessageCache::update(const Message& msg) { if (!canMessageBeCached(msg)){ + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_UNCACHEABLE). + arg((*msg.beginQuestion())->getName()). + arg((*msg.beginQuestion())->getType()). + arg((*msg.beginQuestion())->getClass()); return (false); } QuestionIterator iter = msg.beginQuestion(); + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_UPDATE). + arg((*iter)->getName()).arg((*iter)->getType()). + arg((*iter)->getClass()); std::string entry_name = genCacheEntryName((*iter)->getName(), (*iter)->getType()); HashKey entry_key = HashKey(entry_name, RRClass(message_class_)); @@ -88,6 +105,9 @@ MessageCache::update(const Message& msg) { // add the message entry, maybe there is one way to touch it once. MessageEntryPtr old_msg_entry = message_table_.get(entry_key); if (old_msg_entry) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_REMOVE). + arg((*iter)->getName()).arg((*iter)->getType()). + arg((*iter)->getClass()); message_lru_.remove(old_msg_entry); } diff --git a/src/lib/cache/message_cache.h b/src/lib/cache/message_cache.h index 979b81455e..44d7fd1cec 100644 --- a/src/lib/cache/message_cache.h +++ b/src/lib/cache/message_cache.h @@ -39,7 +39,7 @@ private: MessageCache& operator=(const MessageCache& source); public: /// \param rrset_cache The cache that stores the RRsets that the - /// message entry will points to + /// message entry will point to /// \param cache_size The size of message cache. /// \param message_class The class of the message cache /// \param negative_soa_cache The cache that stores the SOA record From 46e6d4b1702e5c30c8bcd33e7fc73733872bc620 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 1 Jul 2011 14:17:59 +0200 Subject: [PATCH 059/974] [trac741] Messages for message entry --- src/lib/cache/cache_messages.mes | 2 ++ src/lib/cache/message_entry.cc | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 429c74630c..48407bf652 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -35,3 +35,5 @@ $NAMESPACE isc::cache % CACHE_MESSAGES_UPDATE updating message entry %1/%2/%3 % CACHE_MESSAGES_REMOVE removing old instance of %1/%2/%3 first + +% CACHE_ENTRY_MISSING_RRSET missing RRset to generate message for %1 diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc index de4ea8916d..66f601f195 100644 --- a/src/lib/cache/message_entry.cc +++ b/src/lib/cache/message_entry.cc @@ -64,7 +64,7 @@ static uint32_t MAX_UINT32 = numeric_limits::max(); // tunable. Values of one to three hours have been found to work well // and would make sensible a default. Values exceeding one day have // been found to be problematic. (sec 5, RFC2308) -// The default value is 3 hourse (10800 seconds) +// The default value is 3 hours (10800 seconds) // TODO:Give an option to let user configure static uint32_t MAX_NEGATIVE_CACHE_TTL = 10800; @@ -142,6 +142,8 @@ MessageEntry::genMessage(const time_t& time_now, // has expired, if it is, return false. vector rrset_entry_vec; if (false == getRRsetEntries(rrset_entry_vec, time_now)) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_ENTRY_MISSING_RRSET). + arg(entry_name_); return (false); } From ec45081d781ae19de834b11e000acc35415a8f30 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 1 Jul 2011 15:55:37 +0200 Subject: [PATCH 060/974] [trac1072] review comments --- src/lib/cc/data.cc | 3 +-- src/lib/cc/tests/data_unittests.cc | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index 3f630ef0d2..b7627c7fcf 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -456,8 +456,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line, char c = in.peek(); if (c == EOF) { throwJSONError(std::string("Unterminated map, or } expected"), file, line, pos); - } - if (c == '}') { + } else if (c == '}') { // empty map, skip closing curly c = in.get(); } else { diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index 2f0a61ca0d..53d5ab8902 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -409,6 +409,11 @@ TEST(Element, to_and_from_wire) { EXPECT_THROW(Element::fromJSON("[ 1, 2, }"), isc::data::JSONError); EXPECT_THROW(Element::fromJSON("[ 1, 2, {}"), isc::data::JSONError); EXPECT_THROW(Element::fromJSON("[ 1, 2, { ]"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("[ "), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{{}}"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{[]}"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("{ \"a\", \"b\" }"), isc::data::JSONError); + EXPECT_THROW(Element::fromJSON("[ \"a\": \"b\" ]"), isc::data::JSONError); } ConstElementPtr From 87fe9bf486e8671d74ed7e6683309a77add03f51 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 1 Jul 2011 17:07:24 +0200 Subject: [PATCH 061/974] [trac764] add empty messages file for lib/python/isc/config --- src/lib/python/isc/config/Makefile.am | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am index 1efb6fc06c..a2378189ba 100644 --- a/src/lib/python/isc/config/Makefile.am +++ b/src/lib/python/isc/config/Makefile.am @@ -1,19 +1,21 @@ SUBDIRS = . tests python_PYTHON = __init__.py ccsession.py cfgmgr.py config_data.py module_spec.py -pyexec_DATA = cfgmgr_messages.py +pyexec_DATA = cfgmgr_messages.py config_messages.py pythondir = $(pyexecdir)/isc/config # Define rule to build logging source files from message file cfgmgr_messages.py: cfgmgr_messages.mes $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes +config_messages.py: config_messages.mes + $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/config_messages.mes -CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc +CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc config_messages.py config_messages.pyc CLEANDIRS = __pycache__ -EXTRA_DIST = cfgmgr_messages.mes +EXTRA_DIST = cfgmgr_messages.mes config_messages.mes clean-local: rm -rf $(CLEANDIRS) From dbe54369eb40d9ba95b8fd77859a243f076b5966 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 1 Jul 2011 17:16:45 +0200 Subject: [PATCH 062/974] [trac741] Logging statements for resolver_cache --- src/lib/cache/cache_messages.mes | 28 ++++++++++++++++++++++++++++ src/lib/cache/message_entry.cc | 1 + src/lib/cache/resolver_cache.cc | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 48407bf652..357adc594d 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -37,3 +37,31 @@ $NAMESPACE isc::cache % CACHE_MESSAGES_REMOVE removing old instance of %1/%2/%3 first % CACHE_ENTRY_MISSING_RRSET missing RRset to generate message for %1 + +% CACHE_RESOLVER_INIT initializing resolver cache for class %1 + +% CACHE_RESOLVER_INIT_INFO initializing resolver cache for class %1 + +% CACHE_RESOLVER_LOOKUP_MSG looking up message in resolver cache for %1/%2 + +% CACHE_RESOLVER_LOOKUP_RRSET looking up rrset in resolver cache for %1/%2 + +% CACHE_RESOLVER_NO_QUESTION answer message for %1/%2 has empty question section + +% CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data + +% CACHE_RESOLVER_LOCAL_RRSET rrset for %1/%2 found in local zone data + +% CACHE_RESOLVER_UPDATE_MSG updating message for %1/%2/%3 + +% CACHE_RESOLVER_UPDATE_RRSET updating rrset for %1/%2/%3 + +% CACHE_RESOLVER_UNKNOWN_CLASS_MSG no cache for class %1 + +% CACHE_RESOLVER_UNKNOWN_CLASS_RRSET no cache for class %1 + +% CACHE_RESOLVER_DEEPEST looking up deepest NS for %1/%2 + +% CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG no cache for class %1 + +% CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET no cache for class %1 diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc index 66f601f195..d9560a6b3f 100644 --- a/src/lib/cache/message_entry.cc +++ b/src/lib/cache/message_entry.cc @@ -20,6 +20,7 @@ #include "message_entry.h" #include "message_utility.h" #include "rrset_cache.h" +#include "logger.h" using namespace isc::dns; using namespace std; diff --git a/src/lib/cache/resolver_cache.cc b/src/lib/cache/resolver_cache.cc index 6602f79b95..57935c06ea 100644 --- a/src/lib/cache/resolver_cache.cc +++ b/src/lib/cache/resolver_cache.cc @@ -17,6 +17,7 @@ #include "resolver_cache.h" #include "dns/message.h" #include "rrset_cache.h" +#include "logger.h" #include #include @@ -29,6 +30,7 @@ namespace cache { ResolverClassCache::ResolverClassCache(const RRClass& cache_class) : cache_class_(cache_class) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_RESOLVER_INIT).arg(cache_class); local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(cache_class_.getCode())); rrsets_cache_ = RRsetCachePtr(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE, cache_class_.getCode())); @@ -45,6 +47,8 @@ ResolverClassCache::ResolverClassCache(const RRClass& cache_class) : ResolverClassCache::ResolverClassCache(const CacheSizeInfo& cache_info) : cache_class_(cache_info.cclass) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_RESOLVER_INIT_INFO). + arg(cache_class_); uint16_t klass = cache_class_.getCode(); // TODO We should find one way to load local zone data. local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(klass)); @@ -69,8 +73,11 @@ ResolverClassCache::lookup(const isc::dns::Name& qname, const isc::dns::RRType& qtype, isc::dns::Message& response) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_LOOKUP_MSG). + arg(qname).arg(qtype); // message response should has question section already. if (response.beginQuestion() == response.endQuestion()) { + LOG_ERROR(logger, CACHE_RESOLVER_NO_QUESTION).arg(qname).arg(qtype); isc_throw(MessageNoQuestionSection, "Message has no question section"); } @@ -79,6 +86,8 @@ ResolverClassCache::lookup(const isc::dns::Name& qname, // answer section. RRsetPtr rrset_ptr = local_zone_data_->lookup(qname, qtype); if (rrset_ptr) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_LOCAL_MSG). + arg(qname).arg(qtype); response.addRRset(Message::SECTION_ANSWER, rrset_ptr); return (true); } @@ -91,11 +100,15 @@ isc::dns::RRsetPtr ResolverClassCache::lookup(const isc::dns::Name& qname, const isc::dns::RRType& qtype) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_LOOKUP_RRSET). + arg(qname).arg(qtype); // Algorithm: // 1. Search in local zone data first, // 2. Then do search in rrsets_cache_. RRsetPtr rrset_ptr = local_zone_data_->lookup(qname, qtype); if (rrset_ptr) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_LOCAL_RRSET). + arg(qname).arg(qtype); return (rrset_ptr); } else { RRsetEntryPtr rrset_entry = rrsets_cache_->lookup(qname, qtype); @@ -109,6 +122,10 @@ ResolverClassCache::lookup(const isc::dns::Name& qname, bool ResolverClassCache::update(const isc::dns::Message& msg) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_UPDATE_MSG). + arg((*msg.beginQuestion())->getName()). + arg((*msg.beginQuestion())->getType()). + arg((*msg.beginQuestion())->getClass()); return (messages_cache_->update(msg)); } @@ -130,6 +147,9 @@ ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr& rrset_ptr, bool ResolverClassCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_UPDATE_RRSET). + arg(rrset_ptr->getName()).arg(rrset_ptr->getType()). + arg(rrset_ptr->getClass()); // First update local zone, then update rrset cache. local_zone_data_->update((*rrset_ptr.get())); updateRRsetCache(rrset_ptr, rrsets_cache_); @@ -166,6 +186,8 @@ ResolverCache::lookup(const isc::dns::Name& qname, if (cc) { return (cc->lookup(qname, qtype, response)); } else { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_UNKNOWN_CLASS_MSG). + arg(qclass); return (false); } } @@ -179,6 +201,8 @@ ResolverCache::lookup(const isc::dns::Name& qname, if (cc) { return (cc->lookup(qname, qtype)); } else { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_UNKNOWN_CLASS_RRSET). + arg(qclass); return (RRsetPtr()); } } @@ -187,6 +211,8 @@ isc::dns::RRsetPtr ResolverCache::lookupDeepestNS(const isc::dns::Name& qname, const isc::dns::RRClass& qclass) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_DEEPEST).arg(qname). + arg(qclass); isc::dns::RRType qtype = RRType::NS(); ResolverClassCache* cc = getClassCache(qclass); if (cc) { @@ -213,6 +239,9 @@ ResolverCache::update(const isc::dns::Message& msg) { if (cc) { return (cc->update(msg)); } else { + LOG_DEBUG(logger, DBG_TRACE_DATA, + CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG). + arg((*msg.beginQuestion())->getClass()); return (false); } } @@ -223,6 +252,9 @@ ResolverCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) { if (cc) { return (cc->update(rrset_ptr)); } else { + LOG_DEBUG(logger, DBG_TRACE_DATA, + CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET). + arg(rrset_ptr->getClass()); return (false); } } From 1a915fef55d9902cb4e0c5d077e9c602101419dc Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 1 Jul 2011 17:41:59 +0200 Subject: [PATCH 063/974] [trac741] Messages for the rrset cache --- src/lib/cache/cache_messages.mes | 20 +++++++++++++++++--- src/lib/cache/rrset_cache.cc | 27 ++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 357adc594d..b583487302 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -44,17 +44,17 @@ $NAMESPACE isc::cache % CACHE_RESOLVER_LOOKUP_MSG looking up message in resolver cache for %1/%2 -% CACHE_RESOLVER_LOOKUP_RRSET looking up rrset in resolver cache for %1/%2 +% CACHE_RESOLVER_LOOKUP_RRSET looking up RRset in resolver cache for %1/%2 % CACHE_RESOLVER_NO_QUESTION answer message for %1/%2 has empty question section % CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data -% CACHE_RESOLVER_LOCAL_RRSET rrset for %1/%2 found in local zone data +% CACHE_RESOLVER_LOCAL_RRSET RRset for %1/%2 found in local zone data % CACHE_RESOLVER_UPDATE_MSG updating message for %1/%2/%3 -% CACHE_RESOLVER_UPDATE_RRSET updating rrset for %1/%2/%3 +% CACHE_RESOLVER_UPDATE_RRSET updating RRset for %1/%2/%3 % CACHE_RESOLVER_UNKNOWN_CLASS_MSG no cache for class %1 @@ -65,3 +65,17 @@ $NAMESPACE isc::cache % CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG no cache for class %1 % CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET no cache for class %1 + +% CACHE_RRSET_INIT initializing RRset cache for %2 RRsets of class %1 + +% CACHE_RRSET_LOOKUP looking up %1/%2 in RRset cache + +% CACHE_RRSET_EXPIRED found expired RRset %1/%2 + +% CACHE_RRSET_NOT_FOUND no RRset found for %1/%2 + +% CACHE_RRSET_UPDATE updating RRset %1/%2/%3 in the cache + +% CACHE_RRSET_UNTRUSTED not replacing old RRset for %1/%2/%3, it has higher trust level + +% CACHE_RRSET_REMOVE_OLD removing old RRset for %1/%2/%3 to make space for new one diff --git a/src/lib/cache/rrset_cache.cc b/src/lib/cache/rrset_cache.cc index da19b6d2a0..4a77d48ed8 100644 --- a/src/lib/cache/rrset_cache.cc +++ b/src/lib/cache/rrset_cache.cc @@ -14,8 +14,9 @@ #include -#include #include "rrset_cache.h" +#include "logger.h" +#include #include #include #include @@ -34,20 +35,28 @@ RRsetCache::RRsetCache(uint32_t cache_size, rrset_lru_((3 * cache_size), new HashDeleter(rrset_table_)) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_RRSET_INIT).arg(cache_size). + arg(rrset_class); } RRsetEntryPtr RRsetCache::lookup(const isc::dns::Name& qname, const isc::dns::RRType& qtype) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_LOOKUP).arg(qname). + arg(qtype); const string entry_name = genCacheEntryName(qname, qtype); - RRsetEntryPtr entry_ptr = rrset_table_.get(HashKey(entry_name, RRClass(class_))); + + RRsetEntryPtr entry_ptr = rrset_table_.get(HashKey(entry_name, + RRClass(class_))); if (entry_ptr) { if (entry_ptr->getExpireTime() > time(NULL)) { // Only touch the non-expired rrset entries rrset_lru_.touch(entry_ptr); return (entry_ptr); } else { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_EXPIRED).arg(qname). + arg(qtype); // the rrset entry has expired, so just remove it from // hash table and lru list. rrset_table_.remove(entry_ptr->hashKey()); @@ -55,19 +64,31 @@ RRsetCache::lookup(const isc::dns::Name& qname, } } + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_NOT_FOUND).arg(qname). + arg(qtype); return (RRsetEntryPtr()); } RRsetEntryPtr -RRsetCache::update(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) { +RRsetCache::update(const isc::dns::RRset& rrset, + const RRsetTrustLevel& level) +{ + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_UPDATE).arg(rrset.getName()). + arg(rrset.getType()).arg(rrset.getClass()); // TODO: If the RRset is an NS, we should update the NSAS as well // lookup first RRsetEntryPtr entry_ptr = lookup(rrset.getName(), rrset.getType()); if (entry_ptr) { if (entry_ptr->getTrustLevel() > level) { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_UNTRUSTED). + arg(rrset.getName()).arg(rrset.getType()). + arg(rrset.getClass()); // existed rrset entry is more authoritative, just return it return (entry_ptr); } else { + LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_REMOVE_OLD). + arg(rrset.getName()).arg(rrset.getType()). + arg(rrset.getClass()); // Remove the old rrset entry from the lru list. rrset_lru_.remove(entry_ptr); } From 756a7d1a0816670445219db901364074b79f158a Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 1 Jul 2011 18:13:00 +0200 Subject: [PATCH 064/974] [trac741] Message descriptions --- src/lib/cache/cache_messages.mes | 157 +++++++++++++++++++++++++------ 1 file changed, 128 insertions(+), 29 deletions(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index b583487302..31109eaeaa 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -14,68 +14,167 @@ $NAMESPACE isc::cache -% CACHE_LOCALZONE_UPDATE updating local zone element at key %1 +% CACHE_ENTRY_MISSING_RRSET missing RRset to generate message for %1 -% CACHE_LOCALZONE_UNKNOWN entry with key %1 not found in local zone data +The cache tried to generate the complete answer message. It knows the structure +of the message, but some of the RRsets to be put there are not in cache (they +probably expired already). Therefore it pretends the message was not found. % CACHE_LOCALZONE_FOUND found entry with key %1 in local zone data -% CACHE_MESSAGES_INIT initialized message cache for %1 %2 messages +Debug message, noting that the requested data was successfully found in the +local zone data of the cache. + +% CACHE_LOCALZONE_UNKNOWN entry with key %1 not found in local zone data + +Debug message. The requested data was not found in the local zone data. + +% CACHE_LOCALZONE_UPDATE updating local zone element at key %1 + +Debug message issued when there's update to the local zone section of cache. % CACHE_MESSAGES_DEINIT deinitialized message cache -% CACHE_MESSAGES_FOUND found a message entry for %1 in the message cache +Debug message. It is issued when the server deinitializes the message cache. % CACHE_MESSAGES_EXPIRED found an expired message entry for %1 in the message cache -% CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache +Debug message. The requested data was found in the message cache, but it +already expired. Therefore the cache removes the entry and pretends it found +nothing. -% CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3 +% CACHE_MESSAGES_FOUND found a message entry for %1 in the message cache -% CACHE_MESSAGES_UPDATE updating message entry %1/%2/%3 +Debug message. We found the whole message in the cache, so it can be returned +to user without any other lookups. + +% CACHE_MESSAGES_INIT initialized message cache for %1 %2 messages + +Debug message issued when a new message cache is issued. It lists the class +of messages it can hold and the maximum size of the cache. % CACHE_MESSAGES_REMOVE removing old instance of %1/%2/%3 first -% CACHE_ENTRY_MISSING_RRSET missing RRset to generate message for %1 +Debug message. This may follow CACHE_MESSAGES_UPDATE and indicates that, while +updating, the old instance is being removed prior of inserting a new one. -% CACHE_RESOLVER_INIT initializing resolver cache for class %1 +% CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3 -% CACHE_RESOLVER_INIT_INFO initializing resolver cache for class %1 +Debug message, noting that the given message can not be cached. This is for +some reason described in RFC2308. -% CACHE_RESOLVER_LOOKUP_MSG looking up message in resolver cache for %1/%2 +% CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache -% CACHE_RESOLVER_LOOKUP_RRSET looking up RRset in resolver cache for %1/%2 +Debug message. The message cache didn't find any entry for the given key. -% CACHE_RESOLVER_NO_QUESTION answer message for %1/%2 has empty question section +% CACHE_MESSAGES_UPDATE updating message entry %1/%2/%3 -% CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data - -% CACHE_RESOLVER_LOCAL_RRSET RRset for %1/%2 found in local zone data - -% CACHE_RESOLVER_UPDATE_MSG updating message for %1/%2/%3 - -% CACHE_RESOLVER_UPDATE_RRSET updating RRset for %1/%2/%3 - -% CACHE_RESOLVER_UNKNOWN_CLASS_MSG no cache for class %1 - -% CACHE_RESOLVER_UNKNOWN_CLASS_RRSET no cache for class %1 +Debug message issued when the message cache is being updated with a new +message. Either the old instance is removed or, if none is found, new one +is created. % CACHE_RESOLVER_DEEPEST looking up deepest NS for %1/%2 +Debug message. The resolver cache is looking up the deepest known nameserver, +so the resolution doesn't have to start from the root. + +% CACHE_RESOLVER_INIT_INFO initializing resolver cache for class %1 + +Debug message, the resolver cache is being created for this given class. The +difference from CACHE_RESOLVER_INIT is only in different format of passed +information, otherwise it does the same. + +% CACHE_RESOLVER_INIT initializing resolver cache for class %1 + +Debug message. The resolver cache is being created for this given class. + +% CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data + +Debug message. The resolver cache found a complete message for the user query +in the zone data. + +% CACHE_RESOLVER_LOCAL_RRSET RRset for %1/%2 found in local zone data + +Debug message. The resolver cache found a requested RRset in the local zone +data. + +% CACHE_RESOLVER_LOOKUP_MSG looking up message in resolver cache for %1/%2 + +Debug message. The resolver cache is trying to find a message to answer the +user query. + +% CACHE_RESOLVER_LOOKUP_RRSET looking up RRset in resolver cache for %1/%2 + +Debug message. The resolver cache is trying to find an RRset (which usually +originates as internally from resolver). + +% CACHE_RESOLVER_NO_QUESTION answer message for %1/%2 has empty question section + +The cache tried to fill in found data into the response message. But it +discovered the message contains no question section, which is invalid. +This is likely a programmer error, please submit a bug report. + +% CACHE_RESOLVER_UNKNOWN_CLASS_MSG no cache for class %1 + +Debug message. While trying to lookup a message in the resolver cache, it was +discovered there's no cache for this class at all. Therefore no message is +found. + +% CACHE_RESOLVER_UNKNOWN_CLASS_RRSET no cache for class %1 + +Debug message. While trying to lookup an RRset in the resolver cache, it was +discovered there's no cache for this class at all. Therefore no data is found. + +% CACHE_RESOLVER_UPDATE_MSG updating message for %1/%2/%3 + +Debug message. The resolver is updating a message in the cache. + +% CACHE_RESOLVER_UPDATE_RRSET updating RRset for %1/%2/%3 + +Debug message. The resolver is updating an RRset in the cache. + % CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG no cache for class %1 +Debug message. While trying to insert a message into the cache, it was +discovered that there's no cache for the class of message. Therefore +the message will not be cached. + % CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET no cache for class %1 -% CACHE_RRSET_INIT initializing RRset cache for %2 RRsets of class %1 - -% CACHE_RRSET_LOOKUP looking up %1/%2 in RRset cache +Debug message. While trying to insert an RRset into the cache, it was +discovered that there's no cache for the class of the RRset. Therefore +the message will not be cached. % CACHE_RRSET_EXPIRED found expired RRset %1/%2 +Debug message. There' the data requested in the RRset cache. However, it is +expired, so the cache removed it and is going to pretend nothing was found. + +% CACHE_RRSET_INIT initializing RRset cache for %2 RRsets of class %1 + +Debug message. The RRset cache to hold at most this many RRsets for the given +class is being created. + +% CACHE_RRSET_LOOKUP looking up %1/%2 in RRset cache + +Debug message. The resolver is trying to look up data in the RRset cache. + % CACHE_RRSET_NOT_FOUND no RRset found for %1/%2 -% CACHE_RRSET_UPDATE updating RRset %1/%2/%3 in the cache +Debug message which can follow CACHE_RRSET_LOOKUP. This means the data is not +in the cache. + +% CACHE_RRSET_REMOVE_OLD removing old RRset for %1/%2/%3 to make space for new one + +Debug message which can follow CACHE_RRSET_UPDATE. During the update, the cache +removed an old instance of the RRset to replace it with the new one. % CACHE_RRSET_UNTRUSTED not replacing old RRset for %1/%2/%3, it has higher trust level -% CACHE_RRSET_REMOVE_OLD removing old RRset for %1/%2/%3 to make space for new one +Debug message which can follow CACHE_RRSET_UPDATE. The cache already holds the +same RRset, but from more trusted source, so the old one is kept and new one +ignored. + +% CACHE_RRSET_UPDATE updating RRset %1/%2/%3 in the cache + +Debug message. The RRset is updating its data with this giver RRset. From 3bd81bcaed4c9c2ca6c6ed5fab00f350be5c2eef Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 1 Jul 2011 10:30:03 -0700 Subject: [PATCH 065/974] [trac1069] use isc::acl::dns::RequestACL directly instead of a separate typedef. RequestACL is itself a shortcut, so it doesn't make sense to define another one. --- src/bin/resolver/resolver.cc | 15 ++++++++------- src/bin/resolver/resolver.h | 8 +++----- src/bin/resolver/tests/resolver_unittest.cc | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc index 1a481c2adb..75e3d52a52 100644 --- a/src/bin/resolver/resolver.cc +++ b/src/bin/resolver/resolver.cc @@ -62,6 +62,7 @@ using boost::shared_ptr; using namespace isc; using namespace isc::util; using namespace isc::acl; +using isc::acl::dns::RequestACL; using namespace isc::dns; using namespace isc::data; using namespace isc::config; @@ -162,11 +163,11 @@ public: OutputBufferPtr buffer, DNSServer* server); - const Resolver::QueryACL& getQueryACL() const { + const RequestACL& getQueryACL() const { return (*query_acl_); } - void setQueryACL(shared_ptr new_acl) { + void setQueryACL(shared_ptr new_acl) { query_acl_ = new_acl; } @@ -194,7 +195,7 @@ public: private: /// ACL on incoming queries - shared_ptr query_acl_; + shared_ptr query_acl_; /// Object to handle upstream queries RecursiveQuery* rec_query_; @@ -595,9 +596,9 @@ Resolver::updateConfig(ConstElementPtr config) { AddressList listenAddresses(parseAddresses(listenAddressesE, "listen_on")); const ConstElementPtr query_acl_cfg(config->get("query_acl")); - const shared_ptr query_acl = + const shared_ptr query_acl = query_acl_cfg ? acl::dns::getLoader().load(query_acl_cfg) : - shared_ptr(); + shared_ptr(); bool set_timeouts(false); int qtimeout = impl_->query_timeout_; int ctimeout = impl_->client_timeout_; @@ -757,13 +758,13 @@ Resolver::getListenAddresses() const { return (impl_->listen_); } -const Resolver::QueryACL& +const RequestACL& Resolver::getQueryACL() const { return (impl_->getQueryACL()); } void -Resolver::setQueryACL(shared_ptr new_acl) { +Resolver::setQueryACL(shared_ptr new_acl) { if (!new_acl) { isc_throw(InvalidParameter, "NULL pointer is passed to setQueryACL"); } diff --git a/src/bin/resolver/resolver.h b/src/bin/resolver/resolver.h index 656723d316..4b9c773c7f 100644 --- a/src/bin/resolver/resolver.h +++ b/src/bin/resolver/resolver.h @@ -239,13 +239,10 @@ public: */ int getRetries() const; - // Shortcut typedef used for query ACL. - typedef isc::acl::ACL QueryACL; - /// Get the query ACL. /// /// \exception None - const QueryACL& getQueryACL() const; + const isc::acl::dns::RequestACL& getQueryACL() const; /// Set the new query ACL. /// @@ -258,7 +255,8 @@ public: /// \exception InvalidParameter The given pointer is NULL /// /// \param new_acl The new ACL to replace the existing one. - void setQueryACL(boost::shared_ptr new_acl); + void setQueryACL(boost::shared_ptr + new_acl); private: ResolverImpl* impl_; diff --git a/src/bin/resolver/tests/resolver_unittest.cc b/src/bin/resolver/tests/resolver_unittest.cc index 6eda44268c..71474dd17a 100644 --- a/src/bin/resolver/tests/resolver_unittest.cc +++ b/src/bin/resolver/tests/resolver_unittest.cc @@ -27,6 +27,7 @@ using namespace std; using namespace isc::dns; using namespace isc::data; +using isc::acl::dns::RequestACL; using namespace isc::testutils; using isc::UnitTestUtil; @@ -156,8 +157,7 @@ TEST_F(ResolverTest, notifyFail) { TEST_F(ResolverTest, setQueryACL) { // valid cases are tested through other tests. We only explicitly check // an invalid case: passing a NULL shared pointer. - EXPECT_THROW(server.setQueryACL( - boost::shared_ptr()), + EXPECT_THROW(server.setQueryACL(boost::shared_ptr()), isc::InvalidParameter); } From a6de7efe8fbf314c5182744d462699283464d9f0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 1 Jul 2011 10:32:02 -0700 Subject: [PATCH 066/974] [trac1069] grammar fix in the doc (although it's not introdued in this branch) --- src/lib/acl/loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/acl/loader.h b/src/lib/acl/loader.h index ff9827bab5..a4b9b03071 100644 --- a/src/lib/acl/loader.h +++ b/src/lib/acl/loader.h @@ -301,7 +301,7 @@ public: * and returns it. * * No exceptions from \c loadCheck (therefore from whatever creator is - * used) and from the actionLoader passed to constructor are not caught. + * used) and from the actionLoader passed to constructor are caught. * * \exception InvalidParameter The given element is NULL (most likely a * caller's bug) From ac06e0bbad2fd39f8cc77fac06fc397be14f92c2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 1 Jul 2011 10:43:25 -0700 Subject: [PATCH 067/974] [trac1069] throw an exception when getSockAddr fails (which shouldn't happen) instead of returning a dummy structure, expecting it to cause a subsequent failure. --- src/lib/acl/tests/sockaddr.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/lib/acl/tests/sockaddr.h b/src/lib/acl/tests/sockaddr.h index 11d63872df..bac1f6eb82 100644 --- a/src/lib/acl/tests/sockaddr.h +++ b/src/lib/acl/tests/sockaddr.h @@ -19,6 +19,8 @@ #include #include +#include + namespace isc { namespace acl { namespace tests { @@ -35,7 +37,8 @@ inline const struct sockaddr& getSockAddr(const char* const addr) { struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; + //hints.ai_family = AF_UNSPEC; + hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; @@ -48,11 +51,11 @@ getSockAddr(const char* const addr) { } // We don't expect getaddrinfo to fail for our tests. But if that - // ever happens we return a dummy value that would make subsequent test - // fail. - static struct sockaddr sa_dummy; - sa_dummy.sa_family = AF_UNSPEC; - return (sa_dummy); + // ever happens we throw an exception to make sure the corresponding test + // fail (either due to a failure of *_NO_THROW or the uncaught exception). + isc_throw(Unexpected, + "failed to convert textual IP address to sockaddr for " << + addr); } } // end of namespace "tests" From 18dcf1d0ec44f4ddf701d5872f6d5e493d3c4fdb Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 1 Jul 2011 12:08:37 -0700 Subject: [PATCH 068/974] [trac1069] rename dns::getLoader() to getRequestLoader() as discussed on jabber. I also noticed some of the comments in the tests are stale or not really correct, so I fixed them, too. --- src/bin/resolver/resolver.cc | 4 ++-- src/lib/acl/dns.cc | 2 +- src/lib/acl/dns.h | 2 +- src/lib/acl/tests/dns_test.cc | 30 +++++++++++++++--------------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc index 75e3d52a52..a3a7a69892 100644 --- a/src/bin/resolver/resolver.cc +++ b/src/bin/resolver/resolver.cc @@ -85,7 +85,7 @@ public: retries_(3), // we apply "reject all" (implicit default of the loader) ACL by // default: - query_acl_(acl::dns::getLoader().load(Element::fromJSON("[]"))), + query_acl_(acl::dns::getRequestLoader().load(Element::fromJSON("[]"))), rec_query_(NULL) {} @@ -597,7 +597,7 @@ Resolver::updateConfig(ConstElementPtr config) { "listen_on")); const ConstElementPtr query_acl_cfg(config->get("query_acl")); const shared_ptr query_acl = - query_acl_cfg ? acl::dns::getLoader().load(query_acl_cfg) : + query_acl_cfg ? acl::dns::getRequestLoader().load(query_acl_cfg) : shared_ptr(); bool set_timeouts(false); int qtimeout = impl_->query_timeout_; diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index 93b6297c58..0d0b3988f1 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -86,7 +86,7 @@ internal::RequestCheckCreator::create(const string& name, } RequestLoader& -getLoader() { +getRequestLoader() { static RequestLoader* loader(NULL); if (loader == NULL) { // Creator registration may throw, so we first store the new loader diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h index bb0834d7ee..118e5fda24 100644 --- a/src/lib/acl/dns.h +++ b/src/lib/acl/dns.h @@ -103,7 +103,7 @@ typedef acl::Loader RequestLoader; * one is enough, this one will have registered default checks and it * is known one, so any plugins can registrer additional checks as well. */ -RequestLoader& getLoader(); +RequestLoader& getRequestLoader(); // The following is essentially private to the implementation and could // be hidden in the implementation file. But it's visible via this header diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc index aa2d342869..87fd8a1569 100644 --- a/src/lib/acl/tests/dns_test.cc +++ b/src/lib/acl/tests/dns_test.cc @@ -37,20 +37,20 @@ using namespace std; using boost::scoped_ptr; using namespace isc::data; using namespace isc::acl; +using namespace isc::acl::dns; using isc::acl::LoaderError; namespace { -// Tests that the getLoader actually returns something, returns the same every -// time and the returned value can be used to anything. It is not much of a -// test, but the getLoader is not much of a function. -TEST(DNSACL, getLoader) { - dns::RequestLoader* l(&dns::getLoader()); +TEST(DNSACL, getRequestLoader) { + dns::RequestLoader* l(&getRequestLoader()); ASSERT_TRUE(l != NULL); - EXPECT_EQ(l, &dns::getLoader()); + EXPECT_EQ(l, &getRequestLoader()); EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\"}]"))); - // Test check rules registered by default, i.e. RequestCheck + // Confirm it can load the ACl syntax acceptable to a default creator. + // Tests to see whether the loaded rules work correctly will be in + // other dedicated tests below. EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\"," " \"from\": \"192.0.2.1\"}]"))); } @@ -77,7 +77,7 @@ TEST_F(RequestCheckCreatorTest, allowListAbbreviation) { // are done in the tests for IPCheck. TEST_F(RequestCheckCreatorTest, createIPv4Check) { check_ = creator_.create("from", Element::fromJSON("\"192.0.2.1\""), - dns::getLoader()); + getRequestLoader()); const dns::internal::RequestIPCheck& ipcheck_ = dynamic_cast(*check_); EXPECT_EQ(AF_INET, ipcheck_.getFamily()); @@ -92,7 +92,7 @@ TEST_F(RequestCheckCreatorTest, createIPv4Check) { TEST_F(RequestCheckCreatorTest, createIPv6Check) { check_ = creator_.create("from", Element::fromJSON("\"2001:db8::5300/120\""), - dns::getLoader()); + getRequestLoader()); const dns::internal::RequestIPCheck& ipcheck_ = dynamic_cast(*check_); EXPECT_EQ(AF_INET6, ipcheck_.getFamily()); @@ -109,23 +109,23 @@ TEST_F(RequestCheckCreatorTest, createIPv6Check) { TEST_F(RequestCheckCreatorTest, badCreate) { // Invalid name EXPECT_THROW(creator_.create("bad", Element::fromJSON("\"192.0.2.1\""), - dns::getLoader()), LoaderError); + getRequestLoader()), LoaderError); // Invalid type of parameter EXPECT_THROW(creator_.create("from", Element::fromJSON("4"), - dns::getLoader()), + getRequestLoader()), isc::data::TypeError); EXPECT_THROW(creator_.create("from", Element::fromJSON("[]"), - dns::getLoader()), + getRequestLoader()), isc::data::TypeError); // Syntax error for IPCheck EXPECT_THROW(creator_.create("from", Element::fromJSON("\"bad\""), - dns::getLoader()), + getRequestLoader()), isc::InvalidParameter); // NULL pointer - EXPECT_THROW(creator_.create("from", ConstElementPtr(), dns::getLoader()), + EXPECT_THROW(creator_.create("from", ConstElementPtr(), getRequestLoader()), LoaderError); } @@ -137,7 +137,7 @@ protected: ConstRequestCheckPtr createIPCheck(const string& prefix) { return (creator_.create("from", Element::fromJSON( string("\"") + prefix + string("\"")), - dns::getLoader())); + getRequestLoader())); } // create a one time request context for a specific test. Note that From 99a63ce0a562d9b26ef1ad68b9426d91e6ec35d7 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 1 Jul 2011 17:47:34 -0700 Subject: [PATCH 069/974] [trac983] added minimal set of files. right now this is mostly just a placeholder for subsequent development. --- configure.ac | 2 ++ src/lib/python/isc/Makefile.am | 2 +- src/lib/python/isc/acl/Makefile.am | 20 +++++++++++++ src/lib/python/isc/acl/__init__.py | 3 ++ src/lib/python/isc/acl/dns.cc | 38 ++++++++++++++++++++++++ src/lib/python/isc/acl/dns.py | 33 ++++++++++++++++++++ src/lib/python/isc/acl/tests/Makefile.am | 30 +++++++++++++++++++ src/lib/python/isc/acl/tests/dns_test.py | 25 ++++++++++++++++ 8 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/lib/python/isc/acl/Makefile.am create mode 100644 src/lib/python/isc/acl/__init__.py create mode 100644 src/lib/python/isc/acl/dns.cc create mode 100644 src/lib/python/isc/acl/dns.py create mode 100644 src/lib/python/isc/acl/tests/Makefile.am create mode 100644 src/lib/python/isc/acl/tests/dns_test.py diff --git a/configure.ac b/configure.ac index 348708fde1..8ac0208c43 100644 --- a/configure.ac +++ b/configure.ac @@ -809,6 +809,8 @@ AC_CONFIG_FILES([Makefile src/lib/cc/tests/Makefile src/lib/python/Makefile src/lib/python/isc/Makefile + src/lib/python/isc/acl/Makefile + src/lib/python/isc/acl/tests/Makefile src/lib/python/isc/util/Makefile src/lib/python/isc/util/tests/Makefile src/lib/python/isc/datasrc/Makefile diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am index bfc5a912cc..b391c1ee81 100644 --- a/src/lib/python/isc/Makefile.am +++ b/src/lib/python/isc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = datasrc cc config log net notify util testutils +SUBDIRS = datasrc cc config log net notify util testutils acl python_PYTHON = __init__.py diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am new file mode 100644 index 0000000000..b6acc53336 --- /dev/null +++ b/src/lib/python/isc/acl/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +#Do we need boost? +#AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(B10_CXXFLAGS) + +pyexec_LTLIBRARIES = dns.la +dns_la_SOURCES = dns.cc +dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) +dns_la_LDFLAGS = $(PYTHON_LDFLAGS) +# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be +# placed after -Wextra defined in AM_CXXFLAGS +acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) + +# Python prefers .so, while some OSes (specifically MacOS) use a different +# suffix for dynamic objects. -module is necessary to work this around. +dns_la_LDFLAGS += -module +dns_la_LIBADD = $(top_builddir)/src/lib/acl/libacl.la +dns_la_LIBADD += $(PYTHON_LIB) diff --git a/src/lib/python/isc/acl/__init__.py b/src/lib/python/isc/acl/__init__.py new file mode 100644 index 0000000000..4b61e6a849 --- /dev/null +++ b/src/lib/python/isc/acl/__init__.py @@ -0,0 +1,3 @@ +""" +Here are function and classes for manipulating access control lists. +""" diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc new file mode 100644 index 0000000000..d0c0f60da2 --- /dev/null +++ b/src/lib/python/isc/acl/dns.cc @@ -0,0 +1,38 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +namespace { +PyModuleDef dnsacl = { + { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, + "acl", + "This module provides Python bindings for the C++ classes in the " + "isc::acl::dns namespace. Specifically, it defines Python interfaces of " + "handling access control lists (ACLs) with DNS related contexts.\n\n" + "These bindings are close match to the C++ API, but they are not complete " + "(some parts are not needed) and some are done in more python-like ways.", + -1, + NULL, + NULL, + NULL, + NULL, + NULL +}; +} // end of unnamed namespace + +PyMODINIT_FUNC +PyInit_dns(void) { + return (PyModule_Create(&dnsacl)); +} diff --git a/src/lib/python/isc/acl/dns.py b/src/lib/python/isc/acl/dns.py new file mode 100644 index 0000000000..8070559b0a --- /dev/null +++ b/src/lib/python/isc/acl/dns.py @@ -0,0 +1,33 @@ +# 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 file is not installed. The log.so is installed into the right place. +# It is only to find it in the .libs directory when we run as a test or +# from the build directory. +# But as nobody gives us the builddir explicitly (and we can't use generation +# from .in file, as it would put us into the builddir and we wouldn't be found) +# we guess from current directory. Any idea for something better? This should +# be enough for the tests, but would it work for B10_FROM_SOURCE as well? +# Should we look there? Or define something in bind10_config? + +import os +import sys + +for base in sys.path[:]: + bindingdir = os.path.join(base, 'isc/acl/.libs') + if os.path.exists(bindingdir): + sys.path.insert(0, bindingdir) + +from dns import * diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am new file mode 100644 index 0000000000..1b9e9dfe09 --- /dev/null +++ b/src/lib/python/isc/acl/tests/Makefile.am @@ -0,0 +1,30 @@ +PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ +PYTESTS = dns_test.py + +EXTRA_DIST = $(PYTESTS) + +# If necessary (rare cases), explicitly specify paths to dynamic libraries +# required by loadable python modules. +LIBRARY_PATH_PLACEHOLDER = +if SET_ENV_LIBRARY_PATH +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +endif + +# test using command-line arguments, so use check-local target instead of TESTS +check-local: +if ENABLE_PYTHON_COVERAGE + touch $(abs_top_srcdir)/.coverage + rm -f .coverage + ${LN_S} $(abs_top_srcdir)/.coverage .coverage +endif + for pytest in $(PYTESTS) ; do \ + echo Running test: $$pytest ; \ + env PYTHONPATH=$(abs_top_builddir)/src/lib/isc/python/acl/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \ + $(LIBRARY_PATH_PLACEHOLDER) \ + $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ + done + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py new file mode 100644 index 0000000000..2954a610d7 --- /dev/null +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -0,0 +1,25 @@ +# 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. + +import unittest +from isc.acl.dns import * + +class DNSACLTest(unittest.TestCase): + + def test_placeholder(self): + pass + +if __name__ == '__main__': + unittest.main() From 803a215c662c5a692c3b057fc7c0bae6c91b3587 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 11:04:11 +0200 Subject: [PATCH 070/974] [trac1069] Remove experiment relict --- src/lib/acl/tests/sockaddr.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/acl/tests/sockaddr.h b/src/lib/acl/tests/sockaddr.h index bac1f6eb82..f004f7ddcb 100644 --- a/src/lib/acl/tests/sockaddr.h +++ b/src/lib/acl/tests/sockaddr.h @@ -37,8 +37,7 @@ inline const struct sockaddr& getSockAddr(const char* const addr) { struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); - //hints.ai_family = AF_UNSPEC; - hints.ai_family = AF_INET; + hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; From 03d707719016e3d3a6d98b3fb9eb786c90df69ec Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Mon, 4 Jul 2011 10:33:25 +0100 Subject: [PATCH 071/974] [trac747] Typo corrected and minor changes of phrasing --- src/lib/server_common/server_common_messages.mes | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes index b85fa1304d..5fbbb0ba58 100644 --- a/src/lib/server_common/server_common_messages.mes +++ b/src/lib/server_common/server_common_messages.mes @@ -24,7 +24,7 @@ IP address - port pairs isn't a list at all but something else. The server failed to bind to one of the address/port pair it should according to configuration, for reason listed in the message (usually because that pair is already used by other service or missing privileges). The server will try -to recover and bind the address/port pairs it was listening before, if any. +to recover and bind the address/port pairs it was listening to before (if any). % SRVCOMM_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 This points to an error in configuration. An address specification in the @@ -54,7 +54,7 @@ per pair). This appears only after SRVCOMM_SET_LISTEN, but might be hidden, as it has higher debug level. % SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring -Debug message indicating that the server is deinilizing the TSIG keyring. +Debug message indicating that the server is deinitializing the TSIG keyring. % SRVCOMM_KEYS_INIT initializing TSIG keyring Debug message indicating that the server is initializing the global TSIG @@ -65,8 +65,8 @@ Debug message indicating new keyring is being loaded from configuration (either on startup or as a result of configuration update). % SRVCOMM_PORT_RANGE port out of valid range (%1 in %2) -This points to an error in configuration. The port in some address -specification is out of the valid range (0-65535). +This points to an error in configuration. The port in an address +specification is outside the valid range of 0 to 65535. % SRVCOMM_SET_LISTEN setting addresses to listen to Debug message, noting that the server is about to start listening on a From af1dbc5024d5b3289841868ee49929ba4f4d3f50 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 12:33:57 +0200 Subject: [PATCH 072/974] [trac747] Add doxygen params --- src/lib/log/log_formatter.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h index 4cecee2925..f43e4afda6 100644 --- a/src/lib/log/log_formatter.h +++ b/src/lib/log/log_formatter.h @@ -163,6 +163,8 @@ public: } /// \brief String version of arg. + /// + /// \param arg The text to place into the placeholder. Formatter& arg(const std::string& arg) { if (logger_) { // Note that this method does a replacement and returns the @@ -182,7 +184,8 @@ public: /// \brief Exception version of arg. /// - /// Simply for convenience, so the .what() doesn't have to be typed. + /// \param e An exception which the "what()" is extracted an put into the + /// message. Formatter& arg(const std::exception& e) { if (logger_) { return (arg(e.what())); From 258663014324e165ea95d581498268915d176141 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Mon, 4 Jul 2011 12:08:15 +0100 Subject: [PATCH 073/974] [trac1071] Changes after review --- src/lib/log/README | 41 ++++++++++++------------ src/lib/log/tests/console_test.sh.in | 2 -- src/lib/log/tests/destination_test.sh.in | 5 +-- src/lib/log/tests/init_logger_test.sh.in | 6 ++-- src/lib/log/tests/local_file_test.sh.in | 2 -- src/lib/log/tests/severity_test.sh.in | 4 +-- 6 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/lib/log/README b/src/lib/log/README index 298004da9d..23196fae89 100644 --- a/src/lib/log/README +++ b/src/lib/log/README @@ -142,19 +142,18 @@ Points to note: the error originated from the logging library and the "WRITE_ERROR" indicates that there was a problem in a write operation. - * The rest of the line - from the first non-space character to the last non- - space character - is taken exactly as-is for the text of the message. There - are no restrictions on what characters may be in this text, other than they - be printable. (This means that both single-quote (') and double-quote (") - characters are allowed.) + * The rest of the line - from the first non-space character to the + last non- space character - is taken exactly as-is for the text + of the message. There are no restrictions on what characters may + be in this text, other than they be printable. (This means that + both single-quote (') and double-quote (") characters are allowed.) + The message text may include replacement tokens (the strings "%1", + "%2" etc.). When a message is logged, these are replaced with the + arguments passed to the logging call: %1 refers to the first argument, + %2 to the second etc. Within the message text, the placeholders + can appear in any order and placeholders can be repeated. - * The replacement tokens are the strings "%1", "%2" etc. When a message - is logged, these are replaced with the arguments passed to the logging - call: %1 refers to the first argument, %2 to the second etc. Within the - message text, the placeholders can appear in any order and placeholders - can be repeated. - -* Remaining lines indicate an explanation for the preceding message. These +* Remaining lines indicate an explanation for the preceding message. These are intended to be processed by a separate program and used to generate an error messages manual. They are ignored by the message compiler. @@ -238,7 +237,7 @@ Using the Logging - C++ ======================= 1. Build message header file and source file as describe above. -2. The main program unit should include a call to isc::log::initLogger() +2. The main program unit must include a call to isc::log::initLogger() (described in more detail below) to set the logging severity, debug log level, and external message file: @@ -285,7 +284,7 @@ Using the Logging - Python ========================== 1. Build message module as describe above. -2. The main program unit should include a call to isc.log.init() +2. The main program unit must include a call to isc.log.init() (described in more detail below) to set the to set the logging severity, debug log level, and external message file: @@ -326,7 +325,7 @@ Logging Initialization ====================== In all cases, if an attempt is made to use a logging method before the logging has been initialized, the program will terminate with a LoggingNotInitialized -call. +exception. C++ --- @@ -336,9 +335,9 @@ unit tests. Variant #1, Used by Production Programs --------------------------------------- - void initLogger(const std::string& root, - isc::log::Severity severity = isc::log::INFO, - int dbglevel = 0, const char* file = NULL); +void isc::log::initLogger(const std::string& root, + isc::log::Severity severity = isc::log::INFO, + int dbglevel = 0, const char* file = NULL); This is the call that should be used by production programs: @@ -356,18 +355,18 @@ dbglevel The debug level used if "severity" is set to isc::log::DEBUG. file -The name of a local message file. This will be read and its defintitions used +The name of a local message file. This will be read and its definitions used to replace the compiled-in text of the messages. Variant #2, Used by Unit Tests ------------------------------ - void initLogger() + void isc::log::initLogger() This is the call that should be used by unit tests. In this variant, all the options are supplied by environment variables. (It should not be used for production programs to avoid the chance that the program operation is affected -by inadvertantly-defined environment variables.) +by inadvertently-defined environment variables.) The environment variables are: diff --git a/src/lib/log/tests/console_test.sh.in b/src/lib/log/tests/console_test.sh.in index 7ef2684471..a16dc23187 100755 --- a/src/lib/log/tests/console_test.sh.in +++ b/src/lib/log/tests/console_test.sh.in @@ -13,8 +13,6 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# \brief -# # The logger supports the idea of a "console" logger than logs to either stdout # or stderr. This test checks that both these options work. diff --git a/src/lib/log/tests/destination_test.sh.in b/src/lib/log/tests/destination_test.sh.in index 41a52ee9ad..1cfb9fb4f6 100755 --- a/src/lib/log/tests/destination_test.sh.in +++ b/src/lib/log/tests/destination_test.sh.in @@ -13,10 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# \brief Severity test -# -# Checks that the logger will limit the output of messages less severy than -# the severity/debug setting. +# Checks that the logger will route messages to the chosen destination. testname="Destination test" echo $testname diff --git a/src/lib/log/tests/init_logger_test.sh.in b/src/lib/log/tests/init_logger_test.sh.in index 6e199a43f7..8ca0e0b6fd 100755 --- a/src/lib/log/tests/init_logger_test.sh.in +++ b/src/lib/log/tests/init_logger_test.sh.in @@ -13,10 +13,8 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# \brief Severity test -# -# Checks that the logger will limit the output of messages less severy than -# the severity/debug setting. +# Checks that the initLogger() call uses for unit tests respects the setting of +# the environment variables. testname="initLogger test" echo $testname diff --git a/src/lib/log/tests/local_file_test.sh.in b/src/lib/log/tests/local_file_test.sh.in index d76f48f619..9b898e6e21 100755 --- a/src/lib/log/tests/local_file_test.sh.in +++ b/src/lib/log/tests/local_file_test.sh.in @@ -13,8 +13,6 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# \brief Local message file test -# # Checks that a local message file can override the definitions in the message # dictionary. diff --git a/src/lib/log/tests/severity_test.sh.in b/src/lib/log/tests/severity_test.sh.in index a5827ded98..78d5050734 100755 --- a/src/lib/log/tests/severity_test.sh.in +++ b/src/lib/log/tests/severity_test.sh.in @@ -13,9 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# \brief Severity test -# -# Checks that the logger will limit the output of messages less severy than +# Checks that the logger will limit the output of messages less severe than # the severity/debug setting. testname="Severity test" From 2e29bef63a7fa200af54b9b0fc69e5cf2573b467 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 13:39:58 +0200 Subject: [PATCH 074/974] [trac981] Tests for the NOT operator --- src/lib/acl/logic_check.h | 15 +++++++++++++++ src/lib/acl/tests/logic_check_test.cc | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/lib/acl/logic_check.h b/src/lib/acl/logic_check.h index 6e1c567a96..70e657affc 100644 --- a/src/lib/acl/logic_check.h +++ b/src/lib/acl/logic_check.h @@ -200,6 +200,21 @@ private: const std::string name_; }; +template +class NotCheck : public CompoundCheck { +public: + NotCheck(const boost::shared_ptr >& expr) { + + } + virtual typename CompoundCheck::Checks getSubexpressions() const { + + } + virtual bool matches(const Context& context) const { + } +private: + const boost::shared_ptr > expr_; +}; + } } diff --git a/src/lib/acl/tests/logic_check_test.cc b/src/lib/acl/tests/logic_check_test.cc index eec6d51b8a..3bf13dc5e4 100644 --- a/src/lib/acl/tests/logic_check_test.cc +++ b/src/lib/acl/tests/logic_check_test.cc @@ -242,4 +242,24 @@ TEST_F(LogicCreatorTest, nested) { log_.checkFirst(2); } +void notTest(bool value) { + NotCheck notOp(shared_ptr >(new ConstCheck(value, 0))); + Log log; + // It returns negated value + EXPECT_EQ(!value, notOp.matches(log)); + // And runs the only one thing there + log.checkFirst(1); + // Check the getSubexpressions does sane things + ASSERT_EQ(1, notOp.getSubexpressions().size()); + EXPECT_EQ(value, notOp.getSubexpressions()[0]->matches(log)); +} + +TEST(Not, trueValue) { + notTest(true); +} + +TEST(Not, trueValue) { + notTest(true); +} + } From faa2bca6751f7a8837e8c593ae723ea81fd40b69 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 13:48:03 +0200 Subject: [PATCH 075/974] [trac981] Implementation of NOT --- src/lib/acl/logic_check.h | 11 +++++++---- src/lib/acl/tests/logic_check_test.cc | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/lib/acl/logic_check.h b/src/lib/acl/logic_check.h index 70e657affc..f9fbec2e8c 100644 --- a/src/lib/acl/logic_check.h +++ b/src/lib/acl/logic_check.h @@ -203,13 +203,16 @@ private: template class NotCheck : public CompoundCheck { public: - NotCheck(const boost::shared_ptr >& expr) { - - } + NotCheck(const boost::shared_ptr >& expr) : + expr_(expr) + { } virtual typename CompoundCheck::Checks getSubexpressions() const { - + typename CompoundCheck::Checks result; + result.push_back(expr_.get()); + return (result); } virtual bool matches(const Context& context) const { + return (!expr_->matches(context)); } private: const boost::shared_ptr > expr_; diff --git a/src/lib/acl/tests/logic_check_test.cc b/src/lib/acl/tests/logic_check_test.cc index 3bf13dc5e4..2e5fa58dcf 100644 --- a/src/lib/acl/tests/logic_check_test.cc +++ b/src/lib/acl/tests/logic_check_test.cc @@ -258,8 +258,8 @@ TEST(Not, trueValue) { notTest(true); } -TEST(Not, trueValue) { - notTest(true); +TEST(Not, falseValue) { + notTest(false); } } From 2ab154b25ceb6264f87ba6a3ca139ec44c7db275 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 13:54:49 +0200 Subject: [PATCH 076/974] [trac981] Doxygen documentation --- src/lib/acl/logic_check.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib/acl/logic_check.h b/src/lib/acl/logic_check.h index f9fbec2e8c..cf511baea5 100644 --- a/src/lib/acl/logic_check.h +++ b/src/lib/acl/logic_check.h @@ -200,21 +200,39 @@ private: const std::string name_; }; +/** + * \brief The NOT operator for ACLs. + * + * This simply returns the negation of whatever returns the subexpression. + */ template class NotCheck : public CompoundCheck { public: + /** + * \brief Constructor + * + * \param expr The subexpression to be negated by this NOT. + */ NotCheck(const boost::shared_ptr >& expr) : expr_(expr) { } + /** + * \brief The list of subexpressions + * + * \return The vector will contain single value and it is the expression + * passed by constructor. + */ virtual typename CompoundCheck::Checks getSubexpressions() const { typename CompoundCheck::Checks result; result.push_back(expr_.get()); return (result); } + /// \brief The matching function virtual bool matches(const Context& context) const { return (!expr_->matches(context)); } private: + /// \brief The subexpression const boost::shared_ptr > expr_; }; From cdadcd5d6a6bd594fdbcb9efe628067879623df6 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 13:59:21 +0200 Subject: [PATCH 077/974] [master] Compilation fix Clang seems to be confused by too many templates and function overloading, so we sacrifice little bit of convenience to make it happy. --- src/lib/log/log_formatter.h | 13 ------------- src/lib/log/tests/log_formatter_unittest.cc | 15 --------------- src/lib/server_common/portconfig.cc | 4 ++-- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h index f43e4afda6..11dec84332 100644 --- a/src/lib/log/log_formatter.h +++ b/src/lib/log/log_formatter.h @@ -181,19 +181,6 @@ public: } return (*this); } - - /// \brief Exception version of arg. - /// - /// \param e An exception which the "what()" is extracted an put into the - /// message. - Formatter& arg(const std::exception& e) { - if (logger_) { - return (arg(e.what())); - } else { - return (*this); - } - } - }; } diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc index 6fda840518..b91665dc80 100644 --- a/src/lib/log/tests/log_formatter_unittest.cc +++ b/src/lib/log/tests/log_formatter_unittest.cc @@ -123,19 +123,4 @@ TEST_F(FormatterTest, noRecurse) { EXPECT_EQ("%1 %1", outputs[0].second); } -// Test it can accept exceptions (which don't have a default conversion -// to string by themself) -TEST_F(FormatterTest, exception) { - class Ex : public std::exception { - public: - virtual const char* what() const throw() { - return "Exception test"; - } - }; - Formatter(isc::log::INFO, s("%1"), this).arg(Ex()); - ASSERT_EQ(1, outputs.size()); - EXPECT_EQ(isc::log::INFO, outputs[0].first); - EXPECT_EQ("Exception test", outputs[0].second); -} - } diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc index c60e18d2da..379a0a17c4 100644 --- a/src/lib/server_common/portconfig.cc +++ b/src/lib/server_common/portconfig.cc @@ -114,12 +114,12 @@ installListenAddresses(const AddressList& newAddresses, * user will get error info, command control can be used to set new * address. So we just catch the exception without propagating outside */ - LOG_ERROR(logger, SRVCOMM_ADDRESS_FAIL).arg(e); + LOG_ERROR(logger, SRVCOMM_ADDRESS_FAIL).arg(e.what()); try { setAddresses(service, addressStore); } catch (const exception& e2) { - LOG_FATAL(logger, SRVCOMM_ADDRESS_UNRECOVERABLE).arg(e2); + LOG_FATAL(logger, SRVCOMM_ADDRESS_UNRECOVERABLE).arg(e2.what()); } //Anyway the new configure has problem, we need to notify configure //manager the new configure doesn't work From 2f088998525f389391efb86ac4e917174449df85 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 4 Jul 2011 14:46:19 +0200 Subject: [PATCH 078/974] [trac764] add -d output_dir option to log message compiler/message --- src/lib/log/compiler/message.cc | 49 +++++++++++++++++++++---- src/lib/util/filename.cc | 12 ++++++ src/lib/util/filename.h | 3 ++ src/lib/util/tests/filename_unittest.cc | 37 +++++++++++++++++++ 4 files changed, 93 insertions(+), 8 deletions(-) diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc index 68335dc35a..872921c133 100644 --- a/src/lib/log/compiler/message.cc +++ b/src/lib/log/compiler/message.cc @@ -85,6 +85,7 @@ usage() { "-h Print this message and exit\n" << "-v Print the program version and exit\n" << "-p Output python source instead of C++ ones\n" << + "-d Place output files in given directory\n" << "\n" << " is the name of the input message file.\n"; } @@ -249,14 +250,21 @@ writeClosingNamespace(ostream& output, const vector& ns) { /// \param file Name of the message file. The source code is written to a file /// file of the same name but with a .py suffix. /// \param dictionary The dictionary holding the message definitions. +/// \param output_directory if not null NULL, output files are written +/// to the given directory. If NULL, they are written to the current +/// working directory. /// /// \note We don't use the namespace as in C++. We don't need it, because /// python file/module works as implicit namespace as well. void -writePythonFile(const string& file, MessageDictionary& dictionary) { +writePythonFile(const string& file, MessageDictionary& dictionary, + const char* output_directory) { Filename message_file(file); Filename python_file(Filename(message_file.name()).useAsDefault(".py")); + if (output_directory) { + python_file.setDirectory(output_directory); + } // Open the file for writing ofstream pyfile(python_file.fullName().c_str()); @@ -291,13 +299,19 @@ writePythonFile(const string& file, MessageDictionary& dictionary) { /// \param ns Namespace in which the definitions are to be placed. An empty /// string indicates no namespace. /// \param dictionary Dictionary holding the message definitions. +/// \param output_directory if not null NULL, output files are written +/// to the given directory. If NULL, they are written to the current +/// working directory. void writeHeaderFile(const string& file, const vector& ns_components, - MessageDictionary& dictionary) + MessageDictionary& dictionary, const char* output_directory) { Filename message_file(file); Filename header_file(Filename(message_file.name()).useAsDefault(".h")); + if (output_directory) { + header_file.setDirectory(output_directory); + } // Text to use as the sentinels. string sentinel_text = sentinel(header_file); @@ -382,13 +396,25 @@ replaceNonAlphaNum(char c) { /// optimisation is done at link-time, not compiler-time. In this it _may_ /// decide to remove the initializer object because of a lack of references /// to it. But until BIND-10 is ported to Windows, we won't know. - +/// +/// \param file Name of the message file. The header file is written to a +/// file of the same name but with a .h suffix. +/// \param ns Namespace in which the definitions are to be placed. An empty +/// string indicates no namespace. +/// \param dictionary Dictionary holding the message definitions. +/// \param output_directory if not null NULL, output files are written +/// to the given directory. If NULL, they are written to the current +/// working directory. void writeProgramFile(const string& file, const vector& ns_components, - MessageDictionary& dictionary) + MessageDictionary& dictionary, + const char* output_directory) { Filename message_file(file); Filename program_file(Filename(message_file.name()).useAsDefault(".cc")); + if (output_directory) { + program_file.setDirectory(output_directory); + } // Open the output file for writing ofstream ccfile(program_file.fullName().c_str()); @@ -496,15 +522,20 @@ warnDuplicates(MessageReader& reader) { int main(int argc, char* argv[]) { - const char* soptions = "hvp"; // Short options + const char* soptions = "hvpd:"; // Short options optind = 1; // Ensure we start a new scan int opt; // Value of the option bool doPython = false; + const char *output_directory = NULL; while ((opt = getopt(argc, argv, soptions)) != -1) { switch (opt) { + case 'd': + output_directory = optarg; + break; + case 'p': doPython = true; break; @@ -552,7 +583,7 @@ main(int argc, char* argv[]) { } // Write the whole python file - writePythonFile(message_file, dictionary); + writePythonFile(message_file, dictionary, output_directory); } else { // Get the namespace into which the message definitions will be put and // split it into components. @@ -560,10 +591,12 @@ main(int argc, char* argv[]) { splitNamespace(reader.getNamespace()); // Write the header file. - writeHeaderFile(message_file, ns_components, dictionary); + writeHeaderFile(message_file, ns_components, dictionary, + output_directory); // Write the file that defines the message symbols and text - writeProgramFile(message_file, ns_components, dictionary); + writeProgramFile(message_file, ns_components, dictionary, + output_directory); } // Finally, warn of any duplicates encountered. diff --git a/src/lib/util/filename.cc b/src/lib/util/filename.cc index 1f2e5db43c..70e054dd72 100644 --- a/src/lib/util/filename.cc +++ b/src/lib/util/filename.cc @@ -132,6 +132,18 @@ Filename::useAsDefault(const string& name) const { return (retstring); } +void +Filename::setDirectory(const std::string& new_directory) { + directory_ = new_directory; + // append '/' if necessary + size_t sep = directory_.rfind('/'); + if (sep == std::string::npos || sep < directory_.size() - 1) { + directory_ += "/"; + } + // and regenerate the full name + full_name_ = directory_ + name_ + extension_; +} + } // namespace log } // namespace isc diff --git a/src/lib/util/filename.h b/src/lib/util/filename.h index 984ecb08d5..1fcbbc7e4d 100644 --- a/src/lib/util/filename.h +++ b/src/lib/util/filename.h @@ -86,6 +86,9 @@ public: return (directory_); } + /// \return Set directory for the file + void setDirectory(const std::string& new_directory); + /// \return Name of Given File Name std::string name() const { return (name_); diff --git a/src/lib/util/tests/filename_unittest.cc b/src/lib/util/tests/filename_unittest.cc index 33e6456413..c6706d6970 100644 --- a/src/lib/util/tests/filename_unittest.cc +++ b/src/lib/util/tests/filename_unittest.cc @@ -177,3 +177,40 @@ TEST_F(FilenameTest, UseAsDefault) { EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u")); EXPECT_EQ("/a/b/c", fname.useAsDefault("")); } + +TEST_F(FilenameTest, setDirectory) { + Filename fname("a.b"); + EXPECT_EQ("", fname.directory()); + EXPECT_EQ("a.b", fname.fullName()); + EXPECT_EQ("a.b", fname.expandWithDefault("")); + + fname.setDirectory("/just/some/dir/"); + EXPECT_EQ("/just/some/dir/", fname.directory()); + EXPECT_EQ("/just/some/dir/a.b", fname.fullName()); + EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault("")); + + fname.setDirectory("/just/some/dir"); + EXPECT_EQ("/just/some/dir/", fname.directory()); + EXPECT_EQ("/just/some/dir/a.b", fname.fullName()); + EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault("")); + + fname.setDirectory("/"); + EXPECT_EQ("/", fname.directory()); + EXPECT_EQ("/a.b", fname.fullName()); + EXPECT_EQ("/a.b", fname.expandWithDefault("")); + + fname.setDirectory(""); + EXPECT_EQ("/", fname.directory()); + EXPECT_EQ("/a.b", fname.fullName()); + EXPECT_EQ("/a.b", fname.expandWithDefault("")); + + fname = Filename("/first/a.b"); + EXPECT_EQ("/first/", fname.directory()); + EXPECT_EQ("/first/a.b", fname.fullName()); + EXPECT_EQ("/first/a.b", fname.expandWithDefault("")); + + fname.setDirectory("/just/some/dir"); + EXPECT_EQ("/just/some/dir/", fname.directory()); + EXPECT_EQ("/just/some/dir/a.b", fname.fullName()); + EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault("")); +} From 18ba901c91b5bd1e910c7ccc8ae1ebbb1e00fa36 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 4 Jul 2011 14:47:26 +0200 Subject: [PATCH 079/974] [trac764] log messages for src/lib/python/isc/config --- src/bin/bindctl/bindcmd.py | 5 ++- src/lib/python/Makefile.am | 4 +-- src/lib/python/isc/config/Makefile.am | 6 ++-- src/lib/python/isc/config/ccsession.py | 14 ++++---- src/lib/python/isc/config/config_messages.mes | 33 +++++++++++++++++++ .../python/isc/config/tests/ccsession_test.py | 2 ++ 6 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 src/lib/python/isc/config/config_messages.mes diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index 8973aa5d64..0bfcda5541 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -674,7 +674,10 @@ class BindCmdInterpreter(Cmd): elif cmd.command == "revert": self.config_data.clear_local_changes() elif cmd.command == "commit": - self.config_data.commit() + try: + self.config_data.commit() + except isc.config.ModuleCCSessionError as mcse: + print(str(mcse)) elif cmd.command == "diff": print(self.config_data.get_local_changes()); elif cmd.command == "go": diff --git a/src/lib/python/Makefile.am b/src/lib/python/Makefile.am index 5924294b9c..014517a47f 100644 --- a/src/lib/python/Makefile.am +++ b/src/lib/python/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS = isc -python_PYTHON = bind10_config.py +python_PYTHON = bind10_config.py config_messages.py pythondir = $(pyexecdir) # Explicitly define DIST_COMMON so ${python_PYTHON} is not included @@ -10,7 +10,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in bind10_config.py.in # When setting DIST_COMMON, then need to add the .in file too. EXTRA_DIST = bind10_config.py.in -CLEANFILES = bind10_config.pyc +CLEANFILES = bind10_config.pyc config_messages.pyc config_messages.py CLEANDIRS = __pycache__ clean-local: diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am index a2378189ba..b039e1dd2e 100644 --- a/src/lib/python/isc/config/Makefile.am +++ b/src/lib/python/isc/config/Makefile.am @@ -1,15 +1,15 @@ SUBDIRS = . tests python_PYTHON = __init__.py ccsession.py cfgmgr.py config_data.py module_spec.py -pyexec_DATA = cfgmgr_messages.py config_messages.py +pyexec_DATA = cfgmgr_messages.py $(top_builddir)/src/lib/python/config_messages.py pythondir = $(pyexecdir)/isc/config # Define rule to build logging source files from message file cfgmgr_messages.py: cfgmgr_messages.mes $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes -config_messages.py: config_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/config_messages.mes +$(top_builddir)/src/lib/python/config_messages.py: config_messages.mes + $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/config_messages.mes -d $(top_builddir)/src/lib/python CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc config_messages.py config_messages.pyc diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index bff4f58c84..8bf7d33ad0 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -43,6 +43,9 @@ from isc.util.file import path_search import bind10_config from isc.log import log_config_update import json +from config_messages import * + +logger = isc.log.Logger("config") class ModuleCCSessionError(Exception): pass @@ -127,10 +130,7 @@ def default_logconfig_handler(new_config, config_data): 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) + logger.error(CONFIG_LOG_CONFIG_ERRORS, errors) class ModuleCCSession(ConfigData): """This class maintains a connection to the command channel, as @@ -385,8 +385,7 @@ class ModuleCCSession(ConfigData): "Wrong data in configuration: " + " ".join(errors)) else: - # log error - print("[" + self._module_name + "] Error requesting configuration: " + value) + logger.error(CONFIG_GET_FAILED, value) else: raise ModuleCCSessionError("No answer from configuration manager") except isc.cc.SessionTimeout: @@ -498,7 +497,6 @@ class UIModuleCCSession(MultiConfigData): self.request_current_config() self.clear_local_changes() elif "error" in answer: - print("Error: " + answer["error"]) - print("Configuration not committed") + raise ModuleCCSessionError("Error: " + str(answer["error"]) + "\n" + "Configuration not committed") else: raise ModuleCCSessionError("Unknown format of answer in commit(): " + str(answer)) diff --git a/src/lib/python/isc/config/config_messages.mes b/src/lib/python/isc/config/config_messages.mes new file mode 100644 index 0000000000..c52efb4301 --- /dev/null +++ b/src/lib/python/isc/config/config_messages.mes @@ -0,0 +1,33 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# No namespace declaration - these constants go in the global namespace +# of the config_messages python module. + +# since these messages are for the python config library, care must +# be taken that names do not conflict with the messages from the c++ +# config library. A checker script should verify that, but we do not +# have that at this moment. So when adding a message, make sure that +# the name is not already used in src/lib/config/config_messages.mes + +% CONFIG_LOG_CONFIG_ERRORS error(s) in logging configuration: %1 +There was a logging configuration update, but the internal validator +for logging configuration found that it contained errors. The errors +are shown, and the update is ignored. + +% CONFIG_GET_FAILED error getting configuration from cfgmgr: %1 +The configuration manager returned an error response when the module +requested its configuration. The full error message answer from the +configuration manager is appended to the log error. + diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py index 830cbd762d..5d09c968c4 100644 --- a/src/lib/python/isc/config/tests/ccsession_test.py +++ b/src/lib/python/isc/config/tests/ccsession_test.py @@ -23,6 +23,7 @@ from isc.config.ccsession import * from isc.config.config_data import BIND10_CONFIG_DATA_VERSION from unittest_fakesession import FakeModuleCCSession, WouldBlockForever import bind10_config +import isc.log class TestHelperFunctions(unittest.TestCase): def test_parse_answer(self): @@ -739,5 +740,6 @@ class TestUIModuleCCSession(unittest.TestCase): uccs.commit() if __name__ == '__main__': + isc.log.init("bind10") unittest.main() From 4082b7fdb7a1b23518e2dcbb5077f52a79bffa8c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 4 Jul 2011 15:22:15 +0200 Subject: [PATCH 080/974] [trac764] add (empty) messages file for notify out --- src/lib/python/Makefile.am | 4 ++-- src/lib/python/isc/config/Makefile.am | 5 ++++- src/lib/python/isc/notify/Makefile.am | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/lib/python/Makefile.am b/src/lib/python/Makefile.am index 014517a47f..5924294b9c 100644 --- a/src/lib/python/Makefile.am +++ b/src/lib/python/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS = isc -python_PYTHON = bind10_config.py config_messages.py +python_PYTHON = bind10_config.py pythondir = $(pyexecdir) # Explicitly define DIST_COMMON so ${python_PYTHON} is not included @@ -10,7 +10,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in bind10_config.py.in # When setting DIST_COMMON, then need to add the .in file too. EXTRA_DIST = bind10_config.py.in -CLEANFILES = bind10_config.pyc config_messages.pyc config_messages.py +CLEANFILES = bind10_config.pyc CLEANDIRS = __pycache__ clean-local: diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am index b039e1dd2e..ca9e7e04f2 100644 --- a/src/lib/python/isc/config/Makefile.am +++ b/src/lib/python/isc/config/Makefile.am @@ -8,10 +8,13 @@ pythondir = $(pyexecdir)/isc/config # Define rule to build logging source files from message file cfgmgr_messages.py: cfgmgr_messages.mes $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes + $(top_builddir)/src/lib/python/config_messages.py: config_messages.mes $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/config_messages.mes -d $(top_builddir)/src/lib/python -CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc config_messages.py config_messages.pyc +CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc +CLEANFILES += $(top_builddir)/src/lib/python/config_messages.py +CLEANFILES += $(top_builddir)/src/lib/python/config_messages.pyc CLEANDIRS = __pycache__ diff --git a/src/lib/python/isc/notify/Makefile.am b/src/lib/python/isc/notify/Makefile.am index 4081a1703d..a3a7dcfa8d 100644 --- a/src/lib/python/isc/notify/Makefile.am +++ b/src/lib/python/isc/notify/Makefile.am @@ -1,9 +1,18 @@ SUBDIRS = . tests python_PYTHON = __init__.py notify_out.py +pyexec_DATA = $(top_builddir)/src/lib/python/notify_out_messages.py pythondir = $(pyexecdir)/isc/notify +$(top_builddir)/src/lib/python/notify_out_messages.py: notify_out_messages.mes + $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/notify/notify_out_messages.mes -d $(top_builddir)/src/lib/python + +EXTRA_DIST = notify_out_messages.mes + +CLEANFILES = $(top_builddir)/src/lib/python/notify_out_messages.pyc +CLEANFILES += $(top_builddir)/src/lib/python/notify_out_messages.py + CLEANDIRS = __pycache__ clean-local: From 95e0a157b2ae5a4027c06b7bb1aec04f9eb883fd Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Mon, 4 Jul 2011 14:52:06 +0100 Subject: [PATCH 081/974] [trac1071] remove intermediate file from a couple of tests --- src/lib/log/tests/init_logger_test.sh.in | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/lib/log/tests/init_logger_test.sh.in b/src/lib/log/tests/init_logger_test.sh.in index 8ca0e0b6fd..d26ca5ddf5 100755 --- a/src/lib/log/tests/init_logger_test.sh.in +++ b/src/lib/log/tests/init_logger_test.sh.in @@ -44,9 +44,8 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID . -rm -f $destfile -B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test 2> $destfile -cut -d' ' -f3- $destfile | diff $tempfile - +B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test 2>&1 | \ + cut -d' ' -f3- | diff $tempfile - passfail $? echo -n " - severity=DEBUG, dbglevel=50: " @@ -58,9 +57,8 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID . -rm -f $destfile -B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test 2> $destfile -cut -d' ' -f3- $destfile | diff $tempfile - +B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test 2>&1 | \ + cut -d' ' -f3- | diff $tempfile - passfail $? echo -n " - severity=WARN: " @@ -69,9 +67,8 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID . -rm -f $destfile -B10_LOGGER_SEVERITY=WARN ./init_logger_test 2> $destfile -cut -d' ' -f3- $destfile | diff $tempfile - +B10_LOGGER_SEVERITY=WARN ./init_logger_test 2>&1 | \ + cut -d' ' -f3- | diff $tempfile - passfail $? echo "2. Checking that B10_LOGGER_DESTINATION works" From c0442d5d6e70643e10e639efe1162b64c44cce45 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 15:55:21 +0200 Subject: [PATCH 082/974] [trac981] Tests for the not creator --- src/lib/acl/logic_check.h | 12 ++++++++++++ src/lib/acl/tests/logic_check_test.cc | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/lib/acl/logic_check.h b/src/lib/acl/logic_check.h index cf511baea5..8d6864604d 100644 --- a/src/lib/acl/logic_check.h +++ b/src/lib/acl/logic_check.h @@ -236,6 +236,18 @@ private: const boost::shared_ptr > expr_; }; +template +class NotCreator : public Loader::CheckCreator { +public: + NotCreator(const std::string& name); + virtual std::vector names() const; + virtual boost::shared_ptr > create(const std::string&, + data::ConstElementPtr + definition, + const Loader& loader); +}; + } } diff --git a/src/lib/acl/tests/logic_check_test.cc b/src/lib/acl/tests/logic_check_test.cc index 2e5fa58dcf..2fa0da5bd1 100644 --- a/src/lib/acl/tests/logic_check_test.cc +++ b/src/lib/acl/tests/logic_check_test.cc @@ -93,6 +93,7 @@ public: LogicCreator("ALL"))); loader_.registerCreator(CreatorPtr(new ThrowCreator)); loader_.registerCreator(CreatorPtr(new LogCreator)); + loader_.registerCreator(CreatorPtr(new NotCreator("NOT"))); } // To mark which parts of the check did run Log log_; @@ -262,4 +263,29 @@ TEST(Not, falseValue) { notTest(false); } +TEST_F(LogicCreatorTest, notInvalid) { + EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": null}")), + LoaderError); + EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": \"hello\"}")), + LoaderError); + EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": true}")), + LoaderError); + EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": 42}")), + LoaderError); + EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": []}")), + LoaderError); + EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": [{" + "\"logcheck\": [0, true]" + "]}]}")), + LoaderError); +} + +TEST_F(LogicCreatorTest, notValid) { + shared_ptr > notOp(load >("{\"NOT\":" + " {\"logcheck\":" + " [0, true]}}")); + EXPECT_FALSE(notOp->matches(log_)); + log_.checkFirst(1); +} + } From 376fd546007d1bf592e391f11b5fdf08993914c2 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 16:03:58 +0200 Subject: [PATCH 083/974] [trac981] Implementation of the NOT creator --- src/lib/acl/logic_check.h | 38 ++++++++++++++++++++++++--- src/lib/acl/tests/logic_check_test.cc | 2 +- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/lib/acl/logic_check.h b/src/lib/acl/logic_check.h index 8d6864604d..fe16f7f9be 100644 --- a/src/lib/acl/logic_check.h +++ b/src/lib/acl/logic_check.h @@ -239,13 +239,45 @@ private: template class NotCreator : public Loader::CheckCreator { public: - NotCreator(const std::string& name); - virtual std::vector names() const; + /** + * \brief Constructor + * + * \param name The name of the NOT operator to be loaded as. + */ + NotCreator(const std::string& name) : + name_(name) + { } + /** + * \brief List of the names this loads + * + * \return Single-value vector containing the name passed to the + * constructor. + */ + virtual std::vector names() const { + std::vector result; + result.push_back(name_); + return (result); + } + /// \brief Create the check. virtual boost::shared_ptr > create(const std::string&, data::ConstElementPtr definition, const Loader& loader); + Action>& loader) + { + return (boost::shared_ptr >(new NotCheck( + loader.loadCheck(definition)))); + } + /** + * \brief Or-abbreviated form. + * + * This returns false. In theory, the NOT operator could be used with + * the abbreviated form, but it would be confusing. Such syntax is + * therefore explicitly forbidden. + */ + virtual bool allowListAbbreviation() const { return (false); } +public: + const std::string name_; }; } diff --git a/src/lib/acl/tests/logic_check_test.cc b/src/lib/acl/tests/logic_check_test.cc index 2fa0da5bd1..1d1979a612 100644 --- a/src/lib/acl/tests/logic_check_test.cc +++ b/src/lib/acl/tests/logic_check_test.cc @@ -276,7 +276,7 @@ TEST_F(LogicCreatorTest, notInvalid) { LoaderError); EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": [{" "\"logcheck\": [0, true]" - "]}]}")), + "}]}")), LoaderError); } From ea2a4f906dc3b5bae939a0348a5f82fa690bbec5 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 16:16:03 +0200 Subject: [PATCH 084/974] [trac981] Register the creators --- src/lib/acl/dns.cc | 17 +++++++++++++++-- src/lib/acl/tests/dns_test.cc | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index 16f1bf5dcb..4dae297f50 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -13,6 +13,9 @@ // PERFORMANCE OF THIS SOFTWARE. #include "dns.h" +#include "logic_check.h" + +using boost::shared_ptr; namespace isc { namespace acl { @@ -22,9 +25,19 @@ Loader& getLoader() { static Loader* loader(NULL); if (loader == NULL) { + // TODO: + // There's some trick with auto_ptr in the branch when IP check + // is added here. That one is better, bring it in on merge. loader = new Loader(REJECT); - // TODO: This is the place where we register default check creators - // like IP check, etc, once we have them. + loader->registerCreator( + shared_ptr >( + new NotCreator("NOT"))); + loader->registerCreator( + shared_ptr >( + new LogicCreator("ANY"))); + loader->registerCreator( + shared_ptr >( + new LogicCreator("ALL"))); } return (*loader); } diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc index e5e0f3a18a..b2f967843e 100644 --- a/src/lib/acl/tests/dns_test.cc +++ b/src/lib/acl/tests/dns_test.cc @@ -32,4 +32,24 @@ TEST(DNSACL, getLoader) { // check, are loaded. } +// The following tests test only the creators are registered, they are tested +// elsewhere + +// TODO: Enable the tests when we merge the IP check branch here (is it called +// "from" in the branch?) +TEST(DNSACL, DISABLED_notLoad) { + EXPECT_NO_THROW(getLoader().loadCheck(isc::data::Element::fromJSON( + "{\"NOT\": {\"from\": \"192.0.2.1\"}}"))); +} + +TEST(DNSACL, DISABLED_allLoad) { + EXPECT_NO_THROW(getLoader().loadCheck(isc::data::Element::fromJSON( + "{\"ALL\": [{\"from\": \"192.0.2.1\"}]}"))); +} + +TEST(DNSACL, DISABLED_anyLoad) { + EXPECT_NO_THROW(getLoader().loadCheck(isc::data::Element::fromJSON( + "{\"ANY\": [{\"from\": \"192.0.2.1\"}]}"))); +} + } From 35d4ca9a46fd8372cce752577944b2fdc458a0f5 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 4 Jul 2011 16:29:02 +0200 Subject: [PATCH 085/974] [trac764] convert logging in xfrout --- src/lib/python/isc/notify/notify_out.py | 59 ++++++++++--------- .../isc/notify/tests/notify_out_test.py | 16 ++--- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py index 4b254633f3..fca0fec136 100644 --- a/src/lib/python/isc/notify/notify_out.py +++ b/src/lib/python/isc/notify/notify_out.py @@ -23,11 +23,14 @@ import errno from isc.datasrc import sqlite3_ds from isc.net import addr import isc +from notify_out_messages import * + +logger = isc.log.Logger("notify_out") + try: from pydnspp import * except ImportError as e: - # C++ loadable module may not be installed; - sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e)) + logger.error(NOTIFY_OUT_IMPORT_DNS, str(e)) ZONE_NEW_DATA_READY_CMD = 'zone_new_data_ready' _MAX_NOTIFY_NUM = 30 @@ -46,9 +49,6 @@ _BAD_QR = 4 _BAD_REPLY_PACKET = 5 SOCK_DATA = b's' -def addr_to_str(addr): - return '%s#%s' % (addr[0], addr[1]) - class ZoneNotifyInfo: '''This class keeps track of notify-out information for one zone.''' @@ -105,11 +105,10 @@ class NotifyOut: notify message to its slaves). notify service can be started by calling dispatcher(), and it can be stoped by calling shutdown() in another thread. ''' - def __init__(self, datasrc_file, log=None, verbose=True): + def __init__(self, datasrc_file, verbose=True): self._notify_infos = {} # key is (zone_name, zone_class) self._waiting_zones = [] self._notifying_zones = [] - self._log = log self._serving = False self._read_sock, self._write_sock = socket.socketpair() self._read_sock.setblocking(False) @@ -367,13 +366,13 @@ class NotifyOut: self._notify_next_target(zone_notify_info) elif event_type == _EVENT_TIMEOUT and zone_notify_info.notify_try_num > 0: - self._log_msg('info', 'notify retry to %s' % addr_to_str(tgt)) + logger.info(NOTIFY_OUT_TIMEOUT_RETRY, tgt[0], tgt[1]) tgt = zone_notify_info.get_current_notify_target() if tgt: zone_notify_info.notify_try_num += 1 if zone_notify_info.notify_try_num > _MAX_NOTIFY_TRY_NUM: - self._log_msg('info', 'notify to %s: retried exceeded' % addr_to_str(tgt)) + logger.info(NOTIFY_OUT_RETRY_EXCEEDED, tgt[0], tgt[1]) self._notify_next_target(zone_notify_info) else: # set exponential backoff according rfc1996 section 3.6 @@ -412,10 +411,15 @@ class NotifyOut: try: sock = zone_notify_info.create_socket(addrinfo[0]) sock.sendto(render.get_data(), 0, addrinfo) - self._log_msg('info', 'sending notify to %s' % addr_to_str(addrinfo)) + logger.info(NOTIFY_OUT_SENDING_NOTIFY, addrinfo[0], + addrinfo[1]) except (socket.error, addr.InvalidAddress) as err: - self._log_msg('error', 'send notify to %s failed: %s' % - (addr_to_str(addrinfo), str(err))) + logger.error(NOTIFY_OUT_SOCKET_ERROR, addrinfo[0], + addrinfo[1], err) + return False + except addr.InvalidAddress as iae: + logger.error(NOTIFY_OUT_INVALID_ADDRESS, addrinfo[0], + addrinfo[1], iae) return False return True @@ -446,34 +450,40 @@ class NotifyOut: msg.add_rrset(Message.SECTION_ANSWER, rrset_soa) return msg, qid - def _handle_notify_reply(self, zone_notify_info, msg_data): + def _handle_notify_reply(self, zone_notify_info, msg_data, from_addr): '''Parse the notify reply message. TODO, the error message should be refined properly. rcode will not checked here, If we get the response from the slave, it means the slaves has got the notify.''' msg = Message(Message.PARSE) try: - errstr = 'notify reply error: ' + #errstr = 'notify reply error: ' msg.from_wire(msg_data) if not msg.get_header_flag(Message.HEADERFLAG_QR): - self._log_msg('error', errstr + 'bad flags') + logger.error(NOTIFY_OUT_REPLY_QR_NOT_SET, from_addr[0], + from_addr[1]) return _BAD_QR if msg.get_qid() != zone_notify_info.notify_msg_id: - self._log_msg('error', errstr + 'bad query ID') + logger.error(NOTIFY_OUT_REPLY_BAD_QID, from_addr[0], + from_addr[1], msg.get_qid(), + zone_notify_info.notify_msg_id) return _BAD_QUERY_ID question = msg.get_question()[0] if question.get_name() != Name(zone_notify_info.zone_name): - self._log_msg('error', errstr + 'bad query name') + logger.error(NOTIFY_OUT_REPLY_BAD_QUERY_NAME, from_addr[0], + from_addr[1], question.get_name().to_text(), + Name(zone_notify_info.zone_name).to_text()) return _BAD_QUERY_NAME if msg.get_opcode() != Opcode.NOTIFY(): - self._log_msg('error', errstr + 'bad opcode') + logger.error(NOTIFY_OUT_REPLY_BAD_OPCODE, from_addr[0], + from_addr[1], msg.get_opcode().to_text()) return _BAD_OPCODE except Exception as err: # We don't care what exception, just report it? - self._log_msg('error', errstr + str(err)) + logger.error(NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION, err) return _BAD_REPLY_PACKET return _REPLY_OK @@ -481,14 +491,9 @@ class NotifyOut: def _get_notify_reply(self, sock, tgt_addr): try: msg, addr = sock.recvfrom(512) - except socket.error: - self._log_msg('error', "notify to %s failed: can't read notify reply" % addr_to_str(tgt_addr)) + except socket.error as err: + logger.error(NOTIFY_OUT_SOCKET_RECV_ERROR, tgt_addr[0], + tgt_addr[1], err) return None return msg - - - def _log_msg(self, level, msg): - if self._log: - self._log.log_message(level, msg) - diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py index 0eb77a34ef..8ac9ba7481 100644 --- a/src/lib/python/isc/notify/tests/notify_out_test.py +++ b/src/lib/python/isc/notify/tests/notify_out_test.py @@ -21,6 +21,7 @@ import time import socket from isc.datasrc import sqlite3_ds from isc.notify import notify_out, SOCK_DATA +import isc.log # our fake socket, where we can read and insert messages class MockSocket(): @@ -79,7 +80,6 @@ class TestZoneNotifyInfo(unittest.TestCase): self.info.prepare_notify_out() self.assertEqual(self.info.get_current_notify_target(), ('127.0.0.1', 53)) - self.assertEqual('127.0.0.1#53', notify_out.addr_to_str(('127.0.0.1', 53))) self.info.set_next_notify_target() self.assertEqual(self.info.get_current_notify_target(), ('1.1.1.1', 5353)) self.info.set_next_notify_target() @@ -223,29 +223,30 @@ class TestNotifyOut(unittest.TestCase): self.assertEqual(0, len(self._notify._waiting_zones)) def test_handle_notify_reply(self): - self.assertEqual(notify_out._BAD_REPLY_PACKET, self._notify._handle_notify_reply(None, b'badmsg')) + fake_address = ('192.0.2.1', 53) + self.assertEqual(notify_out._BAD_REPLY_PACKET, self._notify._handle_notify_reply(None, b'badmsg', fake_address)) example_com_info = self._notify._notify_infos[('example.com.', 'IN')] example_com_info.notify_msg_id = 0X2f18 # test with right notify reply message data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01' - self.assertEqual(notify_out._REPLY_OK, self._notify._handle_notify_reply(example_com_info, data)) + self.assertEqual(notify_out._REPLY_OK, self._notify._handle_notify_reply(example_com_info, data, fake_address)) # test with unright query id data = b'\x2e\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01' - self.assertEqual(notify_out._BAD_QUERY_ID, self._notify._handle_notify_reply(example_com_info, data)) + self.assertEqual(notify_out._BAD_QUERY_ID, self._notify._handle_notify_reply(example_com_info, data, fake_address)) # test with unright query name data = b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03net\x00\x00\x06\x00\x01' - self.assertEqual(notify_out._BAD_QUERY_NAME, self._notify._handle_notify_reply(example_com_info, data)) + self.assertEqual(notify_out._BAD_QUERY_NAME, self._notify._handle_notify_reply(example_com_info, data, fake_address)) # test with unright opcode data = b'\x2f\x18\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01' - self.assertEqual(notify_out._BAD_OPCODE, self._notify._handle_notify_reply(example_com_info, data)) + self.assertEqual(notify_out._BAD_OPCODE, self._notify._handle_notify_reply(example_com_info, data, fake_address)) # test with unright qr data = b'\x2f\x18\x10\x10\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01' - self.assertEqual(notify_out._BAD_QR, self._notify._handle_notify_reply(example_com_info, data)) + self.assertEqual(notify_out._BAD_QR, self._notify._handle_notify_reply(example_com_info, data, fake_address)) def test_send_notify_message_udp_ipv4(self): example_com_info = self._notify._notify_infos[('example.net.', 'IN')] @@ -406,6 +407,7 @@ class TestNotifyOut(unittest.TestCase): self.assertFalse(thread.is_alive()) if __name__== "__main__": + isc.log.init("bind10") unittest.main() From e3d273a6048b48b7b39d55087d3a6b7ca0a623eb Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 4 Jul 2011 16:32:14 +0200 Subject: [PATCH 086/974] [trac1071] README tweak --- src/lib/log/README | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/log/README b/src/lib/log/README index 23196fae89..3747cb1dcf 100644 --- a/src/lib/log/README +++ b/src/lib/log/README @@ -143,7 +143,7 @@ Points to note: indicates that there was a problem in a write operation. * The rest of the line - from the first non-space character to the - last non- space character - is taken exactly as-is for the text + last non- space character - is taken exactly for the text of the message. There are no restrictions on what characters may be in this text, other than they be printable. (This means that both single-quote (') and double-quote (") characters are allowed.) @@ -151,7 +151,8 @@ Points to note: "%2" etc.). When a message is logged, these are replaced with the arguments passed to the logging call: %1 refers to the first argument, %2 to the second etc. Within the message text, the placeholders - can appear in any order and placeholders can be repeated. + can appear in any order and placeholders can be repeated. Otherwise, + the message is printed unmodified. * Remaining lines indicate an explanation for the preceding message. These are intended to be processed by a separate program and used to generate From e2127bd275b2263f06d7ba039123411c9b7cf07d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 4 Jul 2011 16:45:52 +0200 Subject: [PATCH 087/974] [trac764] forgot to commit messages file --- .../python/isc/notify/notify_out_messages.mes | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/lib/python/isc/notify/notify_out_messages.mes diff --git a/src/lib/python/isc/notify/notify_out_messages.mes b/src/lib/python/isc/notify/notify_out_messages.mes new file mode 100644 index 0000000000..af89612b56 --- /dev/null +++ b/src/lib/python/isc/notify/notify_out_messages.mes @@ -0,0 +1,77 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# No namespace declaration - these constants go in the global namespace +# of the notify_out_messages python module. + +% NOTIFY_OUT_IMPORT error importing python module: %1 +There was an error importing a python module. One of the modules +needed by notify_out could not be found. This suggests that either +some libraries are missing on the system, or the PYTHONPATH variable +is not correct. The specific place where this library needs to be +depends on your system and your specific installation. + +% NOTIFY_OUT_INVALID_ADDRESS invalid address %1#%2: %3 +The notify_out library tried to send a notify packet to the given +address, but it appears to be an invalid address. The configuration +for secondary nameservers might contain a typographic error. + +% NOTIFY_OUT_REPLY_BAD_OPCODE bad opcode in notify reply from %1#%2: %3 +The notify_out library sent a notify packet to the nameserver at the +given address, but the response did not have the opcode set to NOTIFY. +The opcode in the response is printed. + +% NOTIFY_OUT_REPLY_BAD_QID bad QID in notify reply from %1#%2: got %3, should be %4 +The notify_out library sent a notify packet to the nameserver at the +given address, but the query id in the response does not match the one +we sent. + +% NOTIFY_OUT_REPLY_BAD_QUERY_NAME bad query name in notify reply from %1#%2: got %3, should be %4 +The notify_out library sent a notify packet to the nameserver at the +given address, but the query name in the response does not match the one +we sent. + +% NOTIFY_OUT_REPLY_QR_NOT_SET QR flags set to 0 in reply to notify from %1#%2 +The notify_out library sent a notify packet to the namesever at the +given address, but the reply did not have the QR bit set to one. + +% NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries exceeded +The maximum number of retries for the notify target has been exceeded. +Either the address of the secondary nameserver is wrong, or it is not +responding. + +% NOTIFY_OUT_SENDING_NOTIFY sending notify to %1#%2 +A notify message is sent to the secondary nameserver at the given +address. + +% NOTIFY_OUT_SOCKET_ERROR socket error sending notify to %1#%2: %3 +There was a network connection error while trying to send a notify +packet to the given address. The socket error is printed and should +provide more information. + +% NOTIFY_OUT_SOCKET_RECV_ERROR socket error reading notify reply from %1#%2: %3 +There was a network connection error while trying to read a notify reply +packet from the given address. The socket error is printed and should +provide more information. + +% NOTIFY_OUT_TIMEOUT_RETRY retry notify to %1#%2 +The notify message to the given address (noted as address#port) has +timed out, and the message is sent again. + +% NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION uncaught exception: %1 +There was an uncaught exception in the handling of a notify reply +packet. The error is printed, and notify_out will treat the response +as a bad packet, but this does point to a programming error, since +all exceptions should have been caught explicitely. Please file +a bug report. From b60e7347e2b97d913b386b82b682c8c7ae2e3d4e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 4 Jul 2011 17:20:47 +0200 Subject: [PATCH 088/974] [trac714] review comment --- src/lib/python/isc/config/cfgmgr_messages.mes | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/config/cfgmgr_messages.mes b/src/lib/python/isc/config/cfgmgr_messages.mes index eb09ab4692..61a63ed2f7 100644 --- a/src/lib/python/isc/config/cfgmgr_messages.mes +++ b/src/lib/python/isc/config/cfgmgr_messages.mes @@ -24,7 +24,8 @@ are now applied, and no action from the administrator is necessary. The configuration manager sent a configuration update to a module, but the module responded with an answer that could not be parsed. The answer message appears to be invalid JSON data, or not decodable to a string. -This is likely to be a problem in the module in question. +This is likely to be a problem in the module in question. The update is +assumed to have failed, and will not be stored. % CFGMGR_CC_SESSION_ERROR Error connecting to command channel: %1 The configuration manager daemon was unable to connect to the messaging From 85b53414c2c8f70e541447ee204e004693289956 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Mon, 4 Jul 2011 22:54:42 -0500 Subject: [PATCH 089/974] [bind10-20110705-release] regenerate some docs regenerate the guide HTML (catch up on some software dependencies). regenerate messages xml and html --- doc/guide/bind10-guide.html | 56 +- doc/guide/bind10-messages.html | 1027 ++++++++++++----- doc/guide/bind10-messages.xml | 1935 +++++++++++++++++++++++++------- 3 files changed, 2325 insertions(+), 693 deletions(-) diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html index 5754cf001e..94adf4aa92 100644 --- a/doc/guide/bind10-guide.html +++ b/doc/guide/bind10-guide.html @@ -1,24 +1,24 @@ -BIND 10 Guide

BIND 10 Guide

Administrator Reference for BIND 10

This is the reference guide for BIND 10 version - 20110519.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by +BIND 10 Guide

BIND 10 Guide

Administrator Reference for BIND 10

This is the reference guide for BIND 10 version + 20110705.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers.

- This is the reference guide for BIND 10 version 20110519. + This is the reference guide for BIND 10 version 20110705. The most up-to-date version of this document, along with - other documents for BIND 10, can be found at http://bind10.isc.org/docs.


Chapter 1. Introduction

+ other documents for BIND 10, can be found at http://bind10.isc.org/docs.


Chapter 1. Introduction

BIND is the popular implementation of a DNS server, developer interfaces, and DNS tools. BIND 10 is a rewrite of BIND 9. BIND 10 is written in C++ and Python and provides a modular environment for serving and maintaining DNS.

Note

This guide covers the experimental prototype of - BIND 10 version 20110519. + BIND 10 version 20110705.

Note

BIND 10 provides a EDNS0- and DNSSEC-capable authoritative DNS server and a caching recursive name server which also provides forwarding. -

Supported Platforms

+

Supported Platforms

BIND 10 builds have been tested on Debian GNU/Linux 5, Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, and CentOS Linux 5.3. @@ -28,13 +28,15 @@ It is planned for BIND 10 to build, install and run on Windows and standard Unix-type platforms. -

Required Software

+

Required Software

BIND 10 requires Python 3.1. Later versions may work, but Python 3.1 is the minimum version which will work.

BIND 10 uses the Botan crypto library for C++. It requires - at least Botan version 1.8. To build BIND 10, install the - Botan libraries and development include headers. + at least Botan version 1.8. +

+ BIND 10 uses the log4cplus C++ logging library. It requires + at least log4cplus version 1.0.3.

The authoritative server requires SQLite 3.3.9 or newer. The b10-xfrin, b10-xfrout, @@ -136,7 +138,10 @@ and, of course, DNS. These include detailed developer documentation and code examples. -

Chapter 2. Installation

Building Requirements

Note

+

Chapter 2. Installation

Building Requirements

+ In addition to the run-time requirements, building BIND 10 + from source code requires various development include headers. +

Note

Some operating systems have split their distribution packages into a run-time and a development package. You will need to install the development package versions, which include header files and @@ -147,6 +152,11 @@

+ To build BIND 10, also install the Botan (at least version + 1.8) and the log4cplus (at least version 1.0.3) + development include headers. +

+ The Python Library and Python _sqlite3 module are required to enable the Xfrout and Xfrin support.

Note

@@ -156,7 +166,7 @@ Building BIND 10 also requires a C++ compiler and standard development headers, make, and pkg-config. BIND 10 builds have been tested with GCC g++ 3.4.3, 4.1.2, - 4.1.3, 4.2.1, 4.3.2, and 4.4.1. + 4.1.3, 4.2.1, 4.3.2, and 4.4.1; Clang++ 2.8; and Sun C++ 5.10.

Quick start

Note

This quickly covers the standard steps for installing and deploying BIND 10 as an authoritative name server using @@ -192,14 +202,14 @@ the Git code revision control system or as a downloadable tar file. It may also be available in pre-compiled ready-to-use packages from operating system vendors. -

Download Tar File

+

Download Tar File

Downloading a release tar file is the recommended method to obtain the source code.

The BIND 10 releases are available as tar file downloads from ftp://ftp.isc.org/isc/bind10/. Periodic development snapshots may also be available. -

Retrieve from Git

+

Retrieve from Git

Downloading this "bleeding edge" code is recommended only for developers or advanced users. Using development code in a production environment is not recommended. @@ -233,7 +243,7 @@ autoheader, automake, and related commands. -

Configure before the build

+

Configure before the build

BIND 10 uses the GNU Build System to discover build environment details. To generate the makefiles using the defaults, simply run: @@ -264,16 +274,16 @@

If the configure fails, it may be due to missing or old dependencies. -

Build

+

Build

After the configure step is complete, to build the executables from the C++ code and prepare the Python scripts, run:

$ make

-

Install

+

Install

To install the BIND 10 executables, support files, and documentation, run:

$ make install

-

Note

The install step may require superuser privileges.

Install Hierarchy

+

Note

The install step may require superuser privileges.

Install Hierarchy

The following is the layout of the complete BIND 10 installation:

  • bin/ — @@ -490,12 +500,12 @@ shutdown the details and relays (over a b10-msgq command channel) the configuration on to the specified module.

    -

Chapter 8. Authoritative Server

+

Chapter 8. Authoritative Server

The b10-auth is the authoritative DNS server. It supports EDNS0 and DNSSEC. It supports IPv6. Normally it is started by the bind10 master process. -

Server Configurations

+

Server Configurations

b10-auth is configured via the b10-cfgmgr configuration manager. The module name is Auth. @@ -515,7 +525,7 @@ This may be a temporary setting until then.

shutdown
Stop the authoritative DNS server.

-

Data Source Backends

Note

+

Data Source Backends

Note

For the development prototype release, b10-auth supports a SQLite3 data source backend and in-memory data source backend. @@ -529,7 +539,7 @@ This may be a temporary setting until then. The default is /usr/local/var/.) This data file location may be changed by defining the database_file configuration. -

Loading Master Zones Files

+

Loading Master Zones Files

RFC 1035 style DNS master zone files may imported into a BIND 10 data source by using the b10-loadzone utility. @@ -607,7 +617,7 @@ This may be a temporary setting until then.

Note

Access control (such as allowing notifies) is not yet provided. The primary/secondary service is not yet complete. -

Chapter 12. Recursive Name Server

Table of Contents

Forwarding

+

Chapter 12. Recursive Name Server

Table of Contents

Forwarding

The b10-resolver process is started by bind10. @@ -636,7 +646,7 @@ This may be a temporary setting until then. > config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }] > config commit

-

Forwarding

+

Forwarding

To enable forwarding, the upstream address and port must be configured to forward queries to, such as: diff --git a/doc/guide/bind10-messages.html b/doc/guide/bind10-messages.html index b075e96eb3..ecebcd825c 100644 --- a/doc/guide/bind10-messages.html +++ b/doc/guide/bind10-messages.html @@ -1,10 +1,10 @@ -BIND 10 Messages Manual

BIND 10 Messages Manual

This is the messages manual for BIND 10 version - 20110519.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by +BIND 10 Messages Manual

BIND 10 Messages Manual

This is the messages manual for BIND 10 version + 20110705.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers.

- This is the messages manual for BIND 10 version 20110519. + This is the messages manual for BIND 10 version 20110705. The most up-to-date version of this document, along with other documents for BIND 10, can be found at http://bind10.isc.org/docs. @@ -26,38 +26,337 @@ For information on configuring and using BIND 10 logging, refer to the BIND 10 Guide.

Chapter 2. BIND 10 Messages

-

ASIODNS_FETCHCOMP upstream fetch to %1(%2) has now completed

-A debug message, this records the the upstream fetch (a query made by the +

ASIODNS_FETCH_COMPLETED upstream fetch to %1(%2) has now completed

+A debug message, this records that the upstream fetch (a query made by the resolver on behalf of its client) to the specified address has completed. -

ASIODNS_FETCHSTOP upstream fetch to %1(%2) has been stopped

+

ASIODNS_FETCH_STOPPED upstream fetch to %1(%2) has been stopped

An external component has requested the halting of an upstream fetch. This is an allowed operation, and the message should only appear if debug is enabled. -

ASIODNS_OPENSOCK error %1 opening %2 socket to %3(%4)

+

ASIODNS_OPEN_SOCKET error %1 opening %2 socket to %3(%4)

The asynchronous I/O code encountered an error when trying to open a socket of the specified protocol in order to send a message to the target address. -The the number of the system error that cause the problem is given in the +The number of the system error that cause the problem is given in the message. -

ASIODNS_RECVSOCK error %1 reading %2 data from %3(%4)

-The asynchronous I/O code encountered an error when trying read data from -the specified address on the given protocol. The the number of the system +

ASIODNS_READ_DATA error %1 reading %2 data from %3(%4)

+The asynchronous I/O code encountered an error when trying to read data from +the specified address on the given protocol. The number of the system error that cause the problem is given in the message. -

ASIODNS_RECVTMO receive timeout while waiting for data from %1(%2)

+

ASIODNS_READ_TIMEOUT receive timeout while waiting for data from %1(%2)

An upstream fetch from the specified address timed out. This may happen for any number of reasons and is most probably a problem at the remote server or a problem on the network. The message will only appear if debug is enabled. -

ASIODNS_SENDSOCK error %1 sending data using %2 to %3(%4)

+

ASIODNS_SEND_DATA error %1 sending data using %2 to %3(%4)

The asynchronous I/O code encountered an error when trying send data to the specified address on the given protocol. The the number of the system error that cause the problem is given in the message. -

ASIODNS_UNKORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)

-This message should not appear and indicates an internal error if it does. -Please enter a bug report. -

ASIODNS_UNKRESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3)

-The termination method of the resolver's upstream fetch class was called with -an unknown result code (which is given in the message). This message should -not appear and may indicate an internal error. Please enter a bug report. +

ASIODNS_UNKNOWN_ORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)

+An internal consistency check on the origin of a message from the +asynchronous I/O module failed. This may indicate an internal error; +please submit a bug report. +

ASIODNS_UNKNOWN_RESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3)

+An internal error indicating that the termination method of the resolver's +upstream fetch class was called with an unknown result code (which is +given in the message). Please submit a bug report. +

AUTH_AXFR_ERROR error handling AXFR request: %1

+This is a debug message produced by the authoritative server when it +has encountered an error processing an AXFR request. The message gives +the reason for the error, and the server will return a SERVFAIL code to +the sender. +

AUTH_AXFR_UDP AXFR query received over UDP

+This is a debug message output when the authoritative server has received +an AXFR query over UDP. Use of UDP for AXFRs is not permitted by the +protocol, so the server will return a FORMERR error to the sender. +

AUTH_COMMAND_FAILED execution of command channel instruction '%1' failed: %2

+Execution of the specified command by the authoritative server failed. The +message contains the reason for the failure. +

AUTH_CONFIG_CHANNEL_CREATED configuration session channel created

+This is a debug message indicating that authoritative server has created +the channel to the configuration manager. It is issued during server +startup is an indication that the initialization is proceeding normally. +

AUTH_CONFIG_CHANNEL_ESTABLISHED configuration session channel established

+This is a debug message indicating that authoritative server +has established communication the configuration manager over the +previously-created channel. It is issued during server startup is an +indication that the initialization is proceeding normally. +

AUTH_CONFIG_CHANNEL_STARTED configuration session channel started

+This is a debug message, issued when the authoritative server has +posted a request to be notified when new configuration information is +available. It is issued during server startup is an indication that +the initialization is proceeding normally. +

AUTH_CONFIG_LOAD_FAIL load of configuration failed: %1

+An attempt to configure the server with information from the configuration +database during the startup sequence has failed. (The reason for +the failure is given in the message.) The server will continue its +initialization although it may not be configured in the desired way. +

AUTH_CONFIG_UPDATE_FAIL update of configuration failed: %1

+At attempt to update the configuration the server with information +from the configuration database has failed, the reason being given in +the message. +

AUTH_DATA_SOURCE data source database file: %1

+This is a debug message produced by the authoritative server when it accesses a +datebase data source, listing the file that is being accessed. +

AUTH_DNS_SERVICES_CREATED DNS services created

+This is a debug message indicating that the component that will handling +incoming queries for the authoritiative server (DNSServices) has been +successfully created. It is issued during server startup is an indication +that the initialization is proceeding normally. +

AUTH_HEADER_PARSE_FAIL unable to parse header in received DNS packet: %1

+This is a debug message, generated by the authoritative server when an +attempt to parse the header of a received DNS packet has failed. (The +reason for the failure is given in the message.) The server will drop the +packet. +

AUTH_LOAD_TSIG loading TSIG keys

+This is a debug message indicating that the authoritiative server +has requested the keyring holding TSIG keys from the configuration +database. It is issued during server startup is an indication that the +initialization is proceeding normally. +

AUTH_LOAD_ZONE loaded zone %1/%2

+This debug message is issued during the processing of the 'loadzone' command +when the authoritative server has successfully loaded the named zone of the +named class. +

AUTH_MEM_DATASRC_DISABLED memory data source is disabled for class %1

+This is a debug message reporting that the authoritative server has +discovered that the memory data source is disabled for the given class. +

AUTH_MEM_DATASRC_ENABLED memory data source is enabled for class %1

+This is a debug message reporting that the authoritative server has +discovered that the memory data source is enabled for the given class. +

AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY

+This debug message is logged by the authoritative server when it receives +a NOTIFY packet that contains zero or more than one question. (A valid +NOTIFY packet contains one question.) The server will return a FORMERR +error to the sender. +

AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY

+This debug message is logged by the authoritative server when it receives +a NOTIFY packet that an RR type of something other than SOA in the +question section. (The RR type received is included in the message.) The +server will return a FORMERR error to the sender. +

AUTH_NO_STATS_SESSION session interface for statistics is not available

+The authoritative server had no session with the statistics module at the +time it attempted to send it data: the attempt has been abandoned. This +could be an error in configuration. +

AUTH_NO_XFRIN received NOTIFY but XFRIN session is not running

+This is a debug message produced by the authoritative server when it receives +a NOTIFY packet but the XFRIN process is not running. The packet will be +dropped and nothing returned to the sender. +

AUTH_PACKET_PARSE_ERROR unable to parse received DNS packet: %1

+This is a debug message, generated by the authoritative server when an +attempt to parse a received DNS packet has failed due to something other +than a protocol error. The reason for the failure is given in the message; +the server will return a SERVFAIL error code to the sender. +

AUTH_PACKET_PROTOCOL_ERROR DNS packet protocol error: %1. Returning %2

+This is a debug message, generated by the authoritative server when an +attempt to parse a received DNS packet has failed due to a protocol error. +The reason for the failure is given in the message, as is the error code +that will be returned to the sender. +

AUTH_PACKET_RECEIVED message received:\n%1

+This is a debug message output by the authoritative server when it +receives a valid DNS packet. +

+Note: This message includes the packet received, rendered in the form of +multiple lines of text. For this reason, it is suggested that this log message +not be routed to the syslog file, where the multiple lines could confuse +programs that expect a format of one message per line. +

AUTH_PROCESS_FAIL message processing failure: %1

+This message is generated by the authoritative server when it has +encountered an internal error whilst processing a received packet: +the cause of the error is included in the message. +

+The server will return a SERVFAIL error code to the sender of the packet. +However, this message indicates a potential error in the server. +Please open a bug ticket for this issue. +

AUTH_RECEIVED_COMMAND command '%1' received

+This is a debug message issued when the authoritative server has received +a command on the command channel. +

AUTH_RECEIVED_SENDSTATS command 'sendstats' received

+This is a debug message issued when the authoritative server has received +a command from the statistics module to send it data. The 'sendstats' +command is handled differently to other commands, which is why the debug +message associated with it has its own code. +

AUTH_RESPONSE_RECEIVED received response message, ignoring

+This is a debug message, this is output if the authoritative server +receives a DNS packet with the QR bit set, i.e. a DNS response. The +server ignores the packet as it only responds to question packets. +

AUTH_SEND_ERROR_RESPONSE sending an error response (%1 bytes):\n%2

+This is a debug message recording that the authoritative server is sending +an error response to the originator of the query. A previous message will +have recorded details of the failure. +

+Note: This message includes the packet sent, rendered in the form of +multiple lines of text. For this reason, it is suggested that this log message +not be routed to the syslog file, where the multiple lines could confuse +programs that expect a format of one message per line. +

AUTH_SEND_NORMAL_RESPONSE sending an error response (%1 bytes):\n%2

+This is a debug message recording that the authoritative server is sending +a response to the originator of a query. +

+Note: This message includes the packet sent, rendered in the form of +multiple lines of text. For this reason, it is suggested that this log message +not be routed to the syslog file, where the multiple lines could confuse +programs that expect a format of one message per line. +

AUTH_SERVER_CREATED server created

+An informational message indicating that the authoritative server process has +been created and is initializing. The AUTH_SERVER_STARTED message will be +output when initialization has successfully completed and the server starts +accepting queries. +

AUTH_SERVER_FAILED server failed: %1

+The authoritative server has encountered a fatal error and is terminating. The +reason for the failure is included in the message. +

AUTH_SERVER_STARTED server started

+Initialization of the authoritative server has completed successfully +and it is entering the main loop, waiting for queries to arrive. +

AUTH_SQLITE3 nothing to do for loading sqlite3

+This is a debug message indicating that the authoritative server has +found that the data source it is loading is an SQLite3 data source, +so no further validation is needed. +

AUTH_STATS_CHANNEL_CREATED STATS session channel created

+This is a debug message indicating that the authoritative server has +created a channel to the statistics process. It is issued during server +startup is an indication that the initialization is proceeding normally. +

AUTH_STATS_CHANNEL_ESTABLISHED STATS session channel established

+This is a debug message indicating that the authoritative server +has established communication over the previously created statistics +channel. It is issued during server startup is an indication that the +initialization is proceeding normally. +

AUTH_STATS_COMMS communication error in sending statistics data: %1

+An error was encountered when the authoritiative server tried to send data +to the statistics daemon. The message includes additional information +describing the reason for the failure. +

AUTH_STATS_TIMEOUT timeout while sending statistics data: %1

+The authoritative server sent data to the statistics daemon but received +no acknowledgement within the specified time. The message includes +additional information describing the reason for the failure. +

AUTH_STATS_TIMER_DISABLED statistics timer has been disabled

+This is a debug message indicating that the statistics timer has been +disabled in the authoritative server and no statistics information is +being produced. +

AUTH_STATS_TIMER_SET statistics timer set to %1 second(s)

+This is a debug message indicating that the statistics timer has been +enabled and that the authoritative server will produce statistics data +at the specified interval. +

AUTH_UNSUPPORTED_OPCODE unsupported opcode: %1

+This is a debug message, produced when a received DNS packet being +processed by the authoritative server has been found to contain an +unsupported opcode. (The opcode is included in the message.) The server +will return an error code of NOTIMPL to the sender. +

AUTH_XFRIN_CHANNEL_CREATED XFRIN session channel created

+This is a debug message indicating that the authoritative server has +created a channel to the XFRIN (Transfer-in) process. It is issued +during server startup is an indication that the initialization is +proceeding normally. +

AUTH_XFRIN_CHANNEL_ESTABLISHED XFRIN session channel established

+This is a debug message indicating that the authoritative server has +established communication over the previously-created channel to the +XFRIN (Transfer-in) process. It is issued during server startup is an +indication that the initialization is proceeding normally. +

AUTH_ZONEMGR_COMMS error communicating with zone manager: %1

+This is a debug message output during the processing of a NOTIFY request. +An error (listed in the message) has been encountered whilst communicating +with the zone manager. The NOTIFY request will not be honored. +

AUTH_ZONEMGR_ERROR received error response from zone manager: %1

+This is a debug message output during the processing of a NOTIFY +request. The zone manager component has been informed of the request, +but has returned an error response (which is included in the message). The +NOTIFY request will not be honored. +

CC_ASYNC_READ_FAILED asynchronous read failed

+This marks a low level error, we tried to read data from the message queue +daemon asynchronously, but the ASIO library returned an error. +

CC_CONN_ERROR error connecting to message queue (%1)

+It is impossible to reach the message queue daemon for the reason given. It +is unlikely there'll be reason for whatever program this currently is to +continue running, as the communication with the rest of BIND 10 is vital +for the components. +

CC_DISCONNECT disconnecting from message queue daemon

+The library is disconnecting from the message queue daemon. This debug message +indicates that the program is trying to shut down gracefully. +

CC_ESTABLISH trying to establish connection with message queue daemon at %1

+This debug message indicates that the command channel library is about to +connect to the message queue daemon, which should be listening on the UNIX-domain +socket listed in the output. +

CC_ESTABLISHED successfully connected to message queue daemon

+This debug message indicates that the connection was successfully made, this +should follow CC_ESTABLISH. +

CC_GROUP_RECEIVE trying to receive a message

+Debug message, noting that a message is expected to come over the command +channel. +

CC_GROUP_RECEIVED message arrived ('%1', '%2')

+Debug message, noting that we successfully received a message (its envelope and +payload listed). This follows CC_GROUP_RECEIVE, but might happen some time +later, depending if we waited for it or just polled. +

CC_GROUP_SEND sending message '%1' to group '%2'

+Debug message, we're about to send a message over the command channel. +

CC_INVALID_LENGTHS invalid length parameters (%1, %2)

+This happens when garbage comes over the command channel or some kind of +confusion happens in the program. The data received from the socket make no +sense if we interpret it as lengths of message. The first one is total length +of message, the second length of the header. The header and it's length +(2 bytes) is counted in the total length. +

CC_LENGTH_NOT_READY length not ready

+There should be data representing length of message on the socket, but it +is not there. +

CC_NO_MESSAGE no message ready to be received yet

+The program polled for incoming messages, but there was no message waiting. +This is a debug message which may happen only after CC_GROUP_RECEIVE. +

CC_NO_MSGQ unable to connect to message queue (%1)

+It isn't possible to connect to the message queue daemon, for reason listed. +It is unlikely any program will be able continue without the communication. +

CC_READ_ERROR error reading data from command channel (%1)

+A low level error happened when the library tried to read data from the +command channel socket. The reason is listed. +

CC_READ_EXCEPTION error reading data from command channel (%1)

+We received an exception while trying to read data from the command +channel socket. The reason is listed. +

CC_REPLY replying to message from '%1' with '%2'

+Debug message, noting we're sending a response to the original message +with the given envelope. +

CC_SET_TIMEOUT setting timeout to %1ms

+Debug message. A timeout for which the program is willing to wait for a reply +is being set. +

CC_START_READ starting asynchronous read

+Debug message. From now on, when a message (or command) comes, it'll wake the +program and the library will automatically pass it over to correct place. +

CC_SUBSCRIBE subscribing to communication group %1

+Debug message. The program wants to receive messages addressed to this group. +

CC_TIMEOUT timeout reading data from command channel

+The program waited too long for data from the command channel (usually when it +sent a query to different program and it didn't answer for whatever reason). +

CC_UNSUBSCRIBE unsubscribing from communication group %1

+Debug message. The program no longer wants to receive messages addressed to +this group. +

CC_WRITE_ERROR error writing data to command channel (%1)

+A low level error happened when the library tried to write data to the command +channel socket. +

CC_ZERO_LENGTH invalid message length (0)

+The library received a message length being zero, which makes no sense, since +all messages must contain at least the envelope. +

CFGMGR_AUTOMATIC_CONFIG_DATABASE_UPDATE Updating configuration database from version %1 to %2

+An older version of the configuration database has been found, from which +there was an automatic upgrade path to the current version. These changes +are now applied, and no action from the administrator is necessary. +

CFGMGR_CC_SESSION_ERROR Error connecting to command channel: %1

+The configuration manager daemon was unable to connect to the messaging +system. The most likely cause is that msgq is not running. +

CFGMGR_DATA_READ_ERROR error reading configuration database from disk: %1

+There was a problem reading the persistent configuration data as stored +on disk. The file may be corrupted, or it is of a version from where +there is no automatic upgrade path. The file needs to be repaired or +removed. The configuration manager daemon will now shut down. +

CFGMGR_IOERROR_WHILE_WRITING_CONFIGURATION Unable to write configuration file; configuration not stored: %1

+There was an IO error from the system while the configuration manager +was trying to write the configuration database to disk. The specific +error is given. The most likely cause is that the directory where +the file is stored does not exist, or is not writable. The updated +configuration is not stored. +

CFGMGR_OSERROR_WHILE_WRITING_CONFIGURATION Unable to write configuration file; configuration not stored: %1

+There was an OS error from the system while the configuration manager +was trying to write the configuration database to disk. The specific +error is given. The most likely cause is that the system does not have +write access to the configuration database file. The updated +configuration is not stored. +

CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down

+There was a keyboard interrupt signal to stop the cfgmgr daemon. The +daemon will now shut down.

CONFIG_CCSESSION_MSG error in CC session message: %1

There was a problem with an incoming message on the command and control channel. The message does not appear to be a valid command, and is @@ -65,33 +364,36 @@ missing a required element or contains an unknown data format. This most likely means that another BIND10 module is sending a bad message. The message itself is ignored by this module.

CONFIG_CCSESSION_MSG_INTERNAL error handling CC session message: %1

-There was an internal problem handling an incoming message on the -command and control channel. An unexpected exception was thrown. This -most likely points to an internal inconsistency in the module code. The -exception message is appended to the log error, and the module will -continue to run, but will not send back an answer. -

CONFIG_FOPEN_ERR error opening %1: %2

-There was an error opening the given file. -

CONFIG_JSON_PARSE JSON parse error in %1: %2

-There was a parse error in 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_MANAGER_CONFIG error getting configuration from cfgmgr: %1

+There was an internal problem handling an incoming message on the command +and control channel. An unexpected exception was thrown, details of +which are appended to the message. The module will continue to run, +but will not send back an answer. +

+The most likely cause of this error is a programming error. Please raise +a bug report. +

CONFIG_GET_FAIL error getting configuration from cfgmgr: %1

The configuration manager returned an error when this module requested the configuration. The full error message answer from the configuration 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_MANAGER_MOD_SPEC module specification not accepted by cfgmgr: %1

-The module specification file for this module was rejected by the -configuration manager. The full error message answer from the -configuration manager is appended to the log error. The most likely -cause is that the module is of a different (specification file) version -than the running configuration manager. -

CONFIG_MODULE_SPEC module specification error in %1: %2

-The given file does not appear to be a valid specification file. Please -verify that the filename is correct and that its contents are a valid -BIND10 module specification. +

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 +and that its contents are a valid BIND10 module specification. +

CONFIG_MOD_SPEC_REJECT module specification rejected by cfgmgr: %1

+The specification file for this module was rejected by the configuration +manager. The full error message answer from the configuration manager is +appended to the log error. The most likely cause is that the module is of +a different (specification file) version than the running configuration +manager. +

CONFIG_OPEN_FAIL error opening %1: %2

+There was an error opening the given file. The reason for the failure +is included in the message.

DATASRC_CACHE_CREATE creating the hotspot cache

Debug information that the hotspot cache was created at startup.

DATASRC_CACHE_DESTROY destroying the hotspot cache

@@ -146,7 +448,7 @@ Debug information. The requested domain is an alias to a different domain, returning the CNAME instead.

DATASRC_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1'

This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the -other way around -- adding some outher data to CNAME. +other way around -- adding some other data to CNAME.

DATASRC_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1'

Someone or something tried to add a CNAME into a domain that already contains some other data. But the protocol forbids coexistence of CNAME with anything @@ -164,7 +466,7 @@ encountered on the way. This may lead to redirection to a different domain and stop the search.

DATASRC_MEM_DNAME_FOUND DNAME found at '%1'

Debug information. A DNAME was found instead of the requested information. -

DATASRC_MEM_DNAME_NS dNAME and NS can't coexist in non-apex domain '%1'

+

DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1'

It was requested for DNAME and NS records to be put into the same domain which is not the apex (the top of the zone). This is forbidden by RFC 2672, section 3. This indicates a problem with provided data. @@ -222,12 +524,12 @@ destroyed. Debug information. A domain above wildcard was reached, but there's something below the requested domain. Therefore the wildcard doesn't apply here. This behaviour is specified by RFC 1034, section 4.3.3 -

DATASRC_MEM_WILDCARD_DNAME dNAME record in wildcard domain '%1'

+

DATASRC_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1'

The software refuses to load DNAME records into a wildcard domain. It isn't explicitly forbidden, but the protocol is ambiguous about how this should behave and BIND 9 refuses that as well. Please describe your intention using different tools. -

DATASRC_MEM_WILDCARD_NS nS record in wildcard domain '%1'

+

DATASRC_MEM_WILDCARD_NS NS record in wildcard domain '%1'

The software refuses to load NS records into a wildcard domain. It isn't explicitly forbidden, but the protocol is ambiguous about how this should behave and BIND 9 refuses that as well. Please describe your intention using @@ -269,7 +571,7 @@ response message.

DATASRC_QUERY_DELEGATION looking for delegation on the path to '%1'

Debug information. The software is trying to identify delegation points on the way down to the given domain. -

DATASRC_QUERY_EMPTY_CNAME cNAME at '%1' is empty

+

DATASRC_QUERY_EMPTY_CNAME CNAME at '%1' is empty

There was an CNAME and it was being followed. But it contains no records, so there's nowhere to go. There will be no answer. This indicates a problem with supplied data. @@ -363,7 +665,7 @@ DNAMEs will be synthesized.

DATASRC_QUERY_TASK_FAIL task failed with %1

The query subtask failed. The reason should have been reported by the subtask already. The code is 1 for error, 2 for not implemented. -

DATASRC_QUERY_TOO_MANY_CNAMES cNAME chain limit exceeded at '%1'

+

DATASRC_QUERY_TOO_MANY_CNAMES CNAME chain limit exceeded at '%1'

A CNAME led to another CNAME and it led to another, and so on. After 16 CNAMEs, the software gave up. Long CNAME chains are discouraged, and this might possibly be a loop as well. Note that some of the CNAMEs might have @@ -385,15 +687,15 @@ While processing a wildcard, a referral was met. But it wasn't possible to get enough information for it. The code is 1 for error, 2 for not implemented.

DATASRC_SQLITE_CLOSE closing SQLite database

Debug information. The SQLite data source is closing the database file. -

DATASRC_SQLITE_CREATE sQLite data source created

+

DATASRC_SQLITE_CREATE SQLite data source created

Debug information. An instance of SQLite data source is being created. -

DATASRC_SQLITE_DESTROY sQLite data source destroyed

+

DATASRC_SQLITE_DESTROY SQLite data source destroyed

Debug information. An instance of SQLite data source is being destroyed.

DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1'

-Debug information. The SQLite data source is trying to identify, which zone +Debug information. The SQLite data source is trying to identify which zone should hold this domain.

DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it

-Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's +Debug information. The last SQLITE_ENCLOSURE query was unsuccessful; there's no such zone in our data.

DATASRC_SQLITE_FIND looking for RRset '%1/%2'

Debug information. The SQLite data source is looking up a resource record @@ -417,7 +719,7 @@ and type in the database. Debug information. The SQLite data source is identifying if this domain is a referral and where it goes.

DATASRC_SQLITE_FINDREF_BAD_CLASS class mismatch looking for referral ('%1' and '%2')

-The SQLite data source was trying to identify, if there's a referral. But +The SQLite data source was trying to identify if there's a referral. But it contains different class than the query was for.

DATASRC_SQLITE_FIND_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')

The SQLite data source was looking up an RRset, but the data source contains @@ -452,142 +754,173 @@ data source.

DATASRC_UNEXPECTED_QUERY_STATE unexpected query state

This indicates a programming error. An internal task of unknown type was generated. -

LOGIMPL_ABOVEDBGMAX debug level of %1 is too high and will be set to the maximum of %2

-A message from the underlying logger implementation code, the debug level -(as set by the string DEBGUGn) is above the maximum allowed value and has -been reduced to that value. -

LOGIMPL_BADDEBUG debug string is '%1': must be of the form DEBUGn

-The string indicating the extended logging level (used by the underlying -logger implementation code) is not of the stated form. In particular, -it starts DEBUG but does not end with an integer. -

LOGIMPL_BELOWDBGMIN debug level of %1 is too low and will be set to the minimum of %2

-A message from the underlying logger implementation code, the debug level -(as set by the string DEBGUGn) is below the minimum allowed value and has -been increased to that value. -

MSG_BADDESTINATION unrecognized log destination: %1

+

LOGIMPL_ABOVE_MAX_DEBUG debug level of %1 is too high and will be set to the maximum of %2

+A message from the interface to the underlying logger implementation reporting +that the debug level (as set by an internally-created string DEBUGn, where n +is an integer, e.g. DEBUG22) is above the maximum allowed value and has +been reduced to that value. The appearance of this message may indicate +a programming error - please submit a bug report. +

LOGIMPL_BAD_DEBUG_STRING debug string '%1' has invalid format

+A message from the interface to the underlying logger implementation +reporting that an internally-created string used to set the debug level +is not of the correct format (it should be of the form DEBUGn, where n +is an integer, e.g. DEBUG22). The appearance of this message indicates +a programming error - please submit a bug report. +

LOGIMPL_BELOW_MIN_DEBUG debug level of %1 is too low and will be set to the minimum of %2

+A message from the interface to the underlying logger implementation reporting +that the debug level (as set by an internally-created string DEBUGn, where n +is an integer, e.g. DEBUG22) is below the minimum allowed value and has +been increased to that value. The appearance of this message may indicate +a programming error - please submit a bug report. +

LOG_BAD_DESTINATION unrecognized log destination: %1

A logger destination value was given that was not recognized. The destination should be one of "console", "file", or "syslog". -

MSG_BADSEVERITY unrecognized log severity: %1

+

LOG_BAD_SEVERITY unrecognized log severity: %1

A logger severity value was given that was not recognized. The severity should be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL". -

MSG_BADSTREAM bad log console output stream: %1

-A log console output stream was given that was not recognized. The -output stream should be one of "stdout", or "stderr" -

MSG_DUPLNS line %1: duplicate $NAMESPACE directive found

-When reading a message file, more than one $NAMESPACE directive was found. In -this version of the code, such a condition is regarded as an error and the -read will be abandoned. -

MSG_DUPMSGID duplicate message ID (%1) in compiled code

-Indicative of a programming error, when it started up, BIND10 detected that -the given message ID had been registered by one or more modules. (All message -IDs should be unique throughout BIND10.) This has no impact on the operation -of the server other that erroneous messages may be logged. (When BIND10 loads -the message IDs (and their associated text), if a duplicate ID is found it is -discarded. However, when the module that supplied the duplicate ID logs that -particular message, the text supplied by the module that added the original -ID will be output - something that may bear no relation to the condition being -logged. -

MSG_IDNOTFND could not replace message text for '%1': no such message

-During start-up a local message file was read. A line with the listed -message identification was found in the file, but the identification is not -one contained in the compiled-in message dictionary. Either the message -identification has been mis-spelled in the file, or the local file was used -for an earlier version of the software and the message with that -identification has been removed. +

LOG_BAD_STREAM bad log console output stream: %1

+A log console output stream was given that was not recognized. The output +stream should be one of "stdout", or "stderr" +

LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code

+During start-up, BIND10 detected that the given message identification had +been defined multiple times in the BIND10 code.

-This message may appear a number of times in the file, once for every such -unknown message identification. -

MSG_INVMSGID line %1: invalid message identification '%2'

-The concatenation of the prefix and the message identification is used as -a symbol in the C++ module; as such it may only contain -

MSG_NOMSGID line %1: message definition line found without a message ID

-Message definition lines are lines starting with a "%". The rest of the line -should comprise the message ID and text describing the message. This error -indicates the message compiler found a line in the message file comprising -just the "%" and nothing else. -

MSG_NOMSGTXT line %1: line found containing a message ID ('%2') and no text

-Message definition lines are lines starting with a "%". The rest of the line -should comprise the message ID and text describing the message. This error -is generated when a line is found in the message file that contains the -leading "%" and the message identification but no text. -

MSG_NSEXTRARG line %1: $NAMESPACE directive has too many arguments

-The $NAMESPACE directive takes a single argument, a namespace in which all the -generated symbol names are placed. This error is generated when the -compiler finds a $NAMESPACE directive with more than one argument. -

MSG_NSINVARG line %1: $NAMESPACE directive has an invalid argument ('%2')

-The $NAMESPACE argument should be a valid C++ namespace. The reader does a -cursory check on its validity, checking that the characters in the namespace -are correct. The error is generated when the reader finds an invalid -character. (Valid are alphanumeric characters, underscores and colons.) -

MSG_NSNOARG line %1: no arguments were given to the $NAMESPACE directive

-The $NAMESPACE directive takes a single argument, a namespace in which all the -generated symbol names are placed. This error is generated when the -compiler finds a $NAMESPACE directive with no arguments. -

MSG_OPENIN unable to open message file %1 for input: %2

-The program was not able to open the specified input message file for the -reason given. -

MSG_OPENOUT unable to open %1 for output: %2

-The program was not able to open the specified output file for the reason -given. -

MSG_PRFEXTRARG line %1: $PREFIX directive has too many arguments

-The $PREFIX directive takes a single argument, a prefix to be added to the -symbol names when a C++ .h file is created. This error is generated when the -compiler finds a $PREFIX directive with more than one argument. -

MSG_PRFINVARG line %1: $PREFIX directive has an invalid argument ('%2')

-The $PREFIX argument is used in a symbol name in a C++ header file. As such, -it must adhere to restrictions on C++ symbol names (e.g. may only contain -alphanumeric characters or underscores, and may nor start with a digit). -A $PREFIX directive was found with an argument (given in the message) that -violates those restictions. -

MSG_RDLOCMES reading local message file %1

-This is an informational message output by BIND10 when it starts to read a -local message file. (A local message file may replace the text of one of more -messages; the ID of the message will not be changed though.) -

MSG_READERR error reading from message file %1: %2

+This has no ill-effects other than the possibility that an erronous +message may be logged. However, as it is indicative of a programming +error, please log a bug report. +

LOG_DUPLICATE_NAMESPACE line %1: duplicate $NAMESPACE directive found

+When reading a message file, more than one $NAMESPACE directive was found. +Such a condition is regarded as an error and the read will be abandoned. +

LOG_INPUT_OPEN_FAIL unable to open message file %1 for input: %2

+The program was not able to open the specified input message file for +the reason given. +

LOG_INVALID_MESSAGE_ID line %1: invalid message identification '%2'

+An invalid message identification (ID) has been found during the read of +a message file. Message IDs should comprise only alphanumeric characters +and the underscore, and should not start with a digit. +

LOG_NAMESPACE_EXTRA_ARGS line %1: $NAMESPACE directive has too many arguments

+The $NAMESPACE directive in a message file takes a single argument, a +namespace in which all the generated symbol names are placed. This error +is generated when the compiler finds a $NAMESPACE directive with more +than one argument. +

LOG_NAMESPACE_INVALID_ARG line %1: $NAMESPACE directive has an invalid argument ('%2')

+The $NAMESPACE argument in a message file should be a valid C++ namespace. +This message is output if the simple check on the syntax of the string +carried out by the reader fails. +

LOG_NAMESPACE_NO_ARGS line %1: no arguments were given to the $NAMESPACE directive

+The $NAMESPACE directive in a message file takes a single argument, +a C++ namespace in which all the generated symbol names are placed. +This error is generated when the compiler finds a $NAMESPACE directive +with no arguments. +

LOG_NO_MESSAGE_ID line %1: message definition line found without a message ID

+Within a message file, message are defined by lines starting with a "%". +The rest of the line should comprise the message ID and text describing +the message. This error indicates the message compiler found a line in +the message file comprising just the "%" and nothing else. +

LOG_NO_MESSAGE_TEXT line %1: line found containing a message ID ('%2') and no text

+Within a message file, message are defined by lines starting with a "%". +The rest of the line should comprise the message ID and text describing +the message. This error indicates the message compiler found a line +in the message file comprising just the "%" and message identification, +but no text. +

LOG_NO_SUCH_MESSAGE could not replace message text for '%1': no such message

+During start-up a local message file was read. A line with the listed +message identification was found in the file, but the identification is +not one contained in the compiled-in message dictionary. This message +may appear a number of times in the file, once for every such unknown +message identification. +

+There may be several reasons why this message may appear: +

+- The message ID has been mis-spelled in the local message file. +

+- The program outputting the message may not use that particular message +(e.g. it originates in a module not used by the program.) +

+- The local file was written for an earlier version of the BIND10 software +and the later version no longer generates that message. +

+Whatever the reason, there is no impact on the operation of BIND10. +

LOG_OPEN_OUTPUT_FAIL unable to open %1 for output: %2

+Originating within the logging code, the program was not able to open +the specified output file for the reason given. +

LOG_PREFIX_EXTRA_ARGS line %1: $PREFIX directive has too many arguments

+Within a message file, the $PREFIX directive takes a single argument, +a prefix to be added to the symbol names when a C++ file is created. +This error is generated when the compiler finds a $PREFIX directive with +more than one argument. +

+Note: the $PREFIX directive is deprecated and will be removed in a future +version of BIND10. +

LOG_PREFIX_INVALID_ARG line %1: $PREFIX directive has an invalid argument ('%2')

+Within a message file, the $PREFIX directive takes a single argument, +a prefix to be added to the symbol names when a C++ file is created. +As such, it must adhere to restrictions on C++ symbol names (e.g. may +only contain alphanumeric characters or underscores, and may nor start +with a digit). A $PREFIX directive was found with an argument (given +in the message) that violates those restictions. +

+Note: the $PREFIX directive is deprecated and will be removed in a future +version of BIND10. +

LOG_READING_LOCAL_FILE reading local message file %1

+This is an informational message output by BIND10 when it starts to read +a local message file. (A local message file may replace the text of +one of more messages; the ID of the message will not be changed though.) +

LOG_READ_ERROR error reading from message file %1: %2

The specified error was encountered reading from the named message file. -

MSG_UNRECDIR line %1: unrecognised directive '%2'

-A line starting with a dollar symbol was found, but the first word on the line -(shown in the message) was not a recognised message compiler directive. -

MSG_WRITERR error writing to %1: %2

-The specified error was encountered by the message compiler when writing to -the named output file. -

NSAS_INVRESPSTR queried for %1 but got invalid response

-This message indicates an internal error in the nameserver address store -component (NSAS) of the resolver. The NSAS made a query for a RR for the -specified nameserver but received an invalid response. Either the success -function was called without a DNS message or the message was invalid on some -way. (In the latter case, the error should have been picked up elsewhere in -the processing logic, hence the raising of the error here.) -

NSAS_INVRESPTC queried for %1 RR of type/class %2/%3, received response %4/%5

-This message indicates an internal error in the nameserver address store -component (NSAS) of the resolver. The NSAS made a query for the given RR -type and class, but instead received an answer with the given type and class. -

NSAS_LOOKUPCANCEL lookup for zone %1 has been cancelled

-A debug message, this is output when a NSAS (nameserver address store - -part of the resolver) lookup for a zone has been cancelled. -

NSAS_LOOKUPZONE searching NSAS for nameservers for zone %1

-A debug message, this is output when a call is made to the nameserver address -store (part of the resolver) to obtain the nameservers for the specified zone. -

NSAS_NSADDR asking resolver to obtain A and AAAA records for %1

-A debug message, the NSAS (nameserver address store - part of the resolver) is -making a callback into the resolver to retrieve the address records for the -specified nameserver. -

NSAS_NSLKUPFAIL failed to lookup any %1 for %2

-A debug message, the NSAS (nameserver address store - part of the resolver) -has been unable to retrieve the specified resource record for the specified -nameserver. This is not necessarily a problem - the nameserver may be -unreachable, in which case the NSAS will try other nameservers in the zone. -

NSAS_NSLKUPSUCC found address %1 for %2

-A debug message, the NSAS (nameserver address store - part of the resolver) -has retrieved the given address for the specified nameserver through an -external query. -

NSAS_SETRTT reporting RTT for %1 as %2; new value is now %3

+

LOG_UNRECOGNISED_DIRECTIVE line %1: unrecognised directive '%2'

+Within a message file, a line starting with a dollar symbol was found +(indicating the presence of a directive) but the first word on the line +(shown in the message) was not recognised. +

LOG_WRITE_ERROR error writing to %1: %2

+The specified error was encountered by the message compiler when writing +to the named output file. +

NSAS_FIND_NS_ADDRESS asking resolver to obtain A and AAAA records for %1

+A debug message issued when the NSAS (nameserver address store - part +of the resolver) is making a callback into the resolver to retrieve the +address records for the specified nameserver. +

NSAS_FOUND_ADDRESS found address %1 for %2

+A debug message issued when the NSAS (nameserver address store - part +of the resolver) has retrieved the given address for the specified +nameserver through an external query. +

NSAS_INVALID_RESPONSE queried for %1 but got invalid response

+The NSAS (nameserver address store - part of the resolver) made a query +for a RR for the specified nameserver but received an invalid response. +Either the success function was called without a DNS message or the +message was invalid on some way. (In the latter case, the error should +have been picked up elsewhere in the processing logic, hence the raising +of the error here.) +

+This message indicates an internal error in the NSAS. Please raise a +bug report. +

NSAS_LOOKUP_CANCEL lookup for zone %1 has been canceled

+A debug message issued when an NSAS (nameserver address store - part of +the resolver) lookup for a zone has been canceled. +

NSAS_NS_LOOKUP_FAIL failed to lookup any %1 for %2

+A debug message issued when the NSAS (nameserver address store - part of +the resolver) has been unable to retrieve the specified resource record +for the specified nameserver. This is not necessarily a problem - the +nameserver may be unreachable, in which case the NSAS will try other +nameservers in the zone. +

NSAS_SEARCH_ZONE_NS searching NSAS for nameservers for zone %1

+A debug message output when a call is made to the NSAS (nameserver +address store - part of the resolver) to obtain the nameservers for +the specified zone. +

NSAS_UPDATE_RTT update RTT for %1: was %2 ms, is now %3 ms

A NSAS (nameserver address store - part of the resolver) debug message -reporting the round-trip time (RTT) for a query made to the specified -nameserver. The RTT has been updated using the value given and the new RTT is -displayed. (The RTT is subject to a calculation that damps out sudden -changes. As a result, the new RTT is not necessarily equal to the RTT -reported.) +reporting the update of a round-trip time (RTT) for a query made to the +specified nameserver. The RTT has been updated using the value given +and the new RTT is displayed. (The RTT is subject to a calculation that +damps out sudden changes. As a result, the new RTT used by the NSAS in +future decisions of which nameserver to use is not necessarily equal to +the RTT reported.) +

NSAS_WRONG_ANSWER queried for %1 RR of type/class %2/%3, received response %4/%5

+A NSAS (nameserver address store - part of the resolver) made a query for +a resource record of a particular type and class, but instead received +an answer with a different given type and class. +

+This message indicates an internal error in the NSAS. Please raise a +bug report.

RESLIB_ANSWER answer received in response to query for <%1>

A debug message recording that an answer has been received to an upstream query for the specified question. Previous debug messages will have indicated @@ -599,95 +932,95 @@ the server to which the question was sent.

RESLIB_DEEPEST did not find <%1> in cache, deepest delegation found is %2

A debug message, a cache lookup did not find the specified <name, class, type> tuple in the cache; instead, the deepest delegation found is indicated. -

RESLIB_FOLLOWCNAME following CNAME chain to <%1>

+

RESLIB_FOLLOW_CNAME following CNAME chain to <%1>

A debug message, a CNAME response was received and another query is being issued for the <name, class, type> tuple. -

RESLIB_LONGCHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded

+

RESLIB_LONG_CHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded

A debug message recording that a CNAME response has been received to an upstream query for the specified question (Previous debug messages will have indicated the server to which the question was sent). However, receipt of this CNAME has meant that the resolver has exceeded the CNAME chain limit (a CNAME chain is where on CNAME points to another) and so an error is being returned. -

RESLIB_NONSRRSET no NS RRSet in referral response received to query for <%1>

+

RESLIB_NO_NS_RRSET no NS RRSet in referral response received to query for <%1>

A debug message, this indicates that a response was received for the specified -query and was categorised as a referral. However, the received message did +query and was categorized as a referral. However, the received message did not contain any NS RRsets. This may indicate a programming error in the response classification code. -

RESLIB_NSASLOOK looking up nameserver for zone %1 in the NSAS

+

RESLIB_NSAS_LOOKUP looking up nameserver for zone %1 in the NSAS

A debug message, the RunningQuery object is querying the NSAS for the nameservers for the specified zone. -

RESLIB_NXDOMRR NXDOMAIN/NXRRSET received in response to query for <%1>

+

RESLIB_NXDOM_NXRR NXDOMAIN/NXRRSET received in response to query for <%1>

A debug message recording that either a NXDOMAIN or an NXRRSET response has been received to an upstream query for the specified question. Previous debug messages will have indicated the server to which the question was sent.

RESLIB_PROTOCOL protocol error in answer for %1: %3

A debug message indicating that a protocol error was received. As there are no retries left, an error will be reported. -

RESLIB_PROTOCOLRTRY protocol error in answer for %1: %2 (retries left: %3)

+

RESLIB_PROTOCOL_RETRY protocol error in answer for %1: %2 (retries left: %3)

A debug message indicating that a protocol error was received and that the resolver is repeating the query to the same nameserver. After this repeated query, there will be the indicated number of retries left. -

RESLIB_RCODERR RCODE indicates error in response to query for <%1>

+

RESLIB_RCODE_ERR RCODE indicates error in response to query for <%1>

A debug message, the response to the specified query indicated an error that is not covered by a specific code path. A SERVFAIL will be returned. -

RESLIB_REFERRAL referral received in response to query for <%1>

-A debug message recording that a referral response has been received to an -upstream query for the specified question. Previous debug messages will -have indicated the server to which the question was sent. -

RESLIB_REFERZONE referred to zone %1

-A debug message indicating that the last referral message was to the specified -zone. -

RESLIB_RESCAFND found <%1> in the cache (resolve() instance %2)

+

RESLIB_RECQ_CACHE_FIND found <%1> in the cache (resolve() instance %2)

This is a debug message and indicates that a RecursiveQuery object found the the specified <name, class, type> tuple in the cache. The instance number at the end of the message indicates which of the two resolve() methods has been called. -

RESLIB_RESCANOTFND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2)

+

RESLIB_RECQ_CACHE_NO_FIND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2)

This is a debug message and indicates that the look in the cache made by the RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery object has been created to resolve the question. The instance number at the end of the message indicates which of the two resolve() methods has been called. +

RESLIB_REFERRAL referral received in response to query for <%1>

+A debug message recording that a referral response has been received to an +upstream query for the specified question. Previous debug messages will +have indicated the server to which the question was sent. +

RESLIB_REFER_ZONE referred to zone %1

+A debug message indicating that the last referral message was to the specified +zone.

RESLIB_RESOLVE asked to resolve <%1> (resolve() instance %2)

A debug message, the RecursiveQuery::resolve method has been called to resolve the specified <name, class, type> tuple. The first action will be to lookup the specified tuple in the cache. The instance number at the end of the message indicates which of the two resolve() methods has been called. -

RESLIB_RRSETFND found single RRset in the cache when querying for <%1> (resolve() instance %2)

+

RESLIB_RRSET_FOUND found single RRset in the cache when querying for <%1> (resolve() instance %2)

A debug message, indicating that when RecursiveQuery::resolve queried the cache, a single RRset was found which was put in the answer. The instance number at the end of the message indicates which of the two resolve() methods has been called.

RESLIB_RTT round-trip time of last query calculated as %1 ms

A debug message giving the round-trip time of the last query and response. -

RESLIB_RUNCAFND found <%1> in the cache

+

RESLIB_RUNQ_CACHE_FIND found <%1> in the cache

This is a debug message and indicates that a RunningQuery object found the specified <name, class, type> tuple in the cache. -

RESLIB_RUNCALOOK looking up up <%1> in the cache

+

RESLIB_RUNQ_CACHE_LOOKUP looking up up <%1> in the cache

This is a debug message and indicates that a RunningQuery object has made a call to its doLookup() method to look up the specified <name, class, type> tuple, the first action of which will be to examine the cache. -

RESLIB_RUNQUFAIL failure callback - nameservers are unreachable

+

RESLIB_RUNQ_FAIL failure callback - nameservers are unreachable

A debug message indicating that a RunningQuery's failure callback has been called because all nameservers for the zone in question are unreachable. -

RESLIB_RUNQUSUCC success callback - sending query to %1

+

RESLIB_RUNQ_SUCCESS success callback - sending query to %1

A debug message indicating that a RunningQuery's success callback has been called because a nameserver has been found, and that a query is being sent to the specified nameserver. -

RESLIB_TESTSERV setting test server to %1(%2)

+

RESLIB_TEST_SERVER setting test server to %1(%2)

This is an internal debugging message and is only generated in unit tests. It indicates that all upstream queries from the resolver are being routed to the specified server, regardless of the address of the nameserver to which the query would normally be routed. As it should never be seen in normal operation, it is a warning message instead of a debug message. -

RESLIB_TESTUPSTR sending upstream query for <%1> to test server at %2

+

RESLIB_TEST_UPSTREAM sending upstream query for <%1> to test server at %2

This is a debug message and should only be seen in unit tests. A query for the specified <name, class, type> tuple is being sent to a test nameserver whose address is given in the message.

RESLIB_TIMEOUT query <%1> to %2 timed out

A debug message indicating that the specified query has timed out and as there are no retries left, an error will be reported. -

RESLIB_TIMEOUTRTRY query <%1> to %2 timed out, re-trying (retries left: %3)

+

RESLIB_TIMEOUT_RETRY query <%1> to %2 timed out, re-trying (retries left: %3)

A debug message indicating that the specified query has timed out and that the resolver is repeating the query to the same nameserver. After this repeated query, there will be the indicated number of retries left. @@ -699,118 +1032,134 @@ gives no cause for concern.

RESLIB_UPSTREAM sending upstream query for <%1> to %2

A debug message indicating that a query for the specified <name, class, type> tuple is being sent to a nameserver whose address is given in the message. -

RESOLVER_AXFRTCP AXFR request received over TCP

+

RESOLVER_AXFR_TCP AXFR request received over TCP

A debug message, the resolver received a NOTIFY message over TCP. The server cannot process it and will return an error message to the sender with the RCODE set to NOTIMP. -

RESOLVER_AXFRUDP AXFR request received over UDP

+

RESOLVER_AXFR_UDP AXFR request received over UDP

A debug message, the resolver received a NOTIFY message over UDP. The server cannot process it (and in any case, an AXFR request should be sent over TCP) and will return an error message to the sender with the RCODE set to FORMERR. -

RESOLVER_CLTMOSMALL client timeout of %1 is too small

+

RESOLVER_CLIENT_TIME_SMALL client timeout of %1 is too small

An error indicating that the configuration value specified for the query timeout is too small. -

RESOLVER_CONFIGCHAN configuration channel created

+

RESOLVER_CONFIG_CHANNEL configuration channel created

A debug message, output when the resolver has successfully established a connection to the configuration channel. -

RESOLVER_CONFIGERR error in configuration: %1

+

RESOLVER_CONFIG_ERROR error in configuration: %1

An error was detected in a configuration update received by the resolver. This may be in the format of the configuration message (in which case this is a programming error) or it may be in the data supplied (in which case it is a user error). The reason for the error, given as a parameter in the message, will give more details. -

RESOLVER_CONFIGLOAD configuration loaded

+

RESOLVER_CONFIG_LOADED configuration loaded

A debug message, output when the resolver configuration has been successfully loaded. -

RESOLVER_CONFIGUPD configuration updated: %1

+

RESOLVER_CONFIG_UPDATED configuration updated: %1

A debug message, the configuration has been updated with the specified information.

RESOLVER_CREATED main resolver object created

A debug message, output when the Resolver() object has been created. -

RESOLVER_DNSMSGRCVD DNS message received: %1

+

RESOLVER_DNS_MESSAGE_RECEIVED DNS message received: %1

A debug message, this always precedes some other logging message and is the formatted contents of the DNS packet that the other message refers to. -

RESOLVER_DNSMSGSENT DNS message of %1 bytes sent: %2

+

RESOLVER_DNS_MESSAGE_SENT DNS message of %1 bytes sent: %2

A debug message, this contains details of the response sent back to the querying system.

RESOLVER_FAILED resolver failed, reason: %1

This is an error message output when an unhandled exception is caught by the resolver. All it can do is to shut down. -

RESOLVER_FWDADDR setting forward address %1(%2)

+

RESOLVER_FORWARD_ADDRESS setting forward address %1(%2)

This message may appear multiple times during startup, and it lists the forward addresses used by the resolver when running in forwarding mode. -

RESOLVER_FWDQUERY processing forward query

+

RESOLVER_FORWARD_QUERY processing forward query

The received query has passed all checks and is being forwarded to upstream servers. -

RESOLVER_HDRERR message received, exception when processing header: %1

+

RESOLVER_HEADER_ERROR message received, exception when processing header: %1

A debug message noting that an exception occurred during the processing of a received packet. The packet has been dropped.

RESOLVER_IXFR IXFR request received

The resolver received a NOTIFY message over TCP. The server cannot process it and will return an error message to the sender with the RCODE set to NOTIMP. -

RESOLVER_LKTMOSMALL lookup timeout of %1 is too small

+

RESOLVER_LOOKUP_TIME_SMALL lookup timeout of %1 is too small

An error indicating that the configuration value specified for the lookup timeout is too small. -

RESOLVER_NFYNOTAUTH NOTIFY arrived but server is not authoritative

-The resolver received a NOTIFY message. As the server is not authoritative it -cannot process it, so it returns an error message to the sender with the RCODE -set to NOTAUTH. -

RESOLVER_NORMQUERY processing normal query

-The received query has passed all checks and is being processed by the resolver. -

RESOLVER_NOROOTADDR no root addresses available

-A warning message during startup, indicates that no root addresses have been -set. This may be because the resolver will get them from a priming query. -

RESOLVER_NOTIN non-IN class request received, returning REFUSED message

+

RESOLVER_MESSAGE_ERROR error parsing received message: %1 - returning %2

+A debug message noting that the resolver received a message and the +parsing of the body of the message failed due to some error (although +the parsing of the header succeeded). The message parameters give a +textual description of the problem and the RCODE returned. +

RESOLVER_NEGATIVE_RETRIES negative number of retries (%1) specified in the configuration

+An error message indicating that the resolver configuration has specified a +negative retry count. Only zero or positive values are valid. +

RESOLVER_NON_IN_PACKET non-IN class request received, returning REFUSED message

A debug message, the resolver has received a DNS packet that was not IN class. The resolver cannot handle such packets, so is returning a REFUSED response to the sender. -

RESOLVER_NOTONEQUES query contained %1 questions, exactly one question was expected

+

RESOLVER_NORMAL_QUERY processing normal query

+The received query has passed all checks and is being processed by the resolver. +

RESOLVER_NOTIFY_RECEIVED NOTIFY arrived but server is not authoritative

+The resolver received a NOTIFY message. As the server is not authoritative it +cannot process it, so it returns an error message to the sender with the RCODE +set to NOTAUTH. +

RESOLVER_NOT_ONE_QUESTION query contained %1 questions, exactly one question was expected

A debug message, the resolver received a query that contained the number of entires in the question section detailed in the message. This is a malformed message, as a DNS query must contain only one question. The resolver will return a message to the sender with the RCODE set to FORMERR. -

RESOLVER_OPCODEUNS opcode %1 not supported by the resolver

-A debug message, the resolver received a message with an unsupported opcode -(it can only process QUERY opcodes). It will return a message to the sender -with the RCODE set to NOTIMP. -

RESOLVER_PARSEERR error parsing received message: %1 - returning %2

+

RESOLVER_NO_ROOT_ADDRESS no root addresses available

+A warning message during startup, indicates that no root addresses have been +set. This may be because the resolver will get them from a priming query. +

RESOLVER_PARSE_ERROR error parsing received message: %1 - returning %2

A debug message noting that the resolver received a message and the parsing of the body of the message failed due to some non-protocol related reason (although the parsing of the header succeeded). The message parameters give a textual description of the problem and the RCODE returned. -

RESOLVER_PRINTMSG print message command, aeguments are: %1

+

RESOLVER_PRINT_COMMAND print message command, arguments are: %1

This message is logged when a "print_message" command is received over the command channel. -

RESOLVER_PROTERR protocol error parsing received message: %1 - returning %2

+

RESOLVER_PROTOCOL_ERROR protocol error parsing received message: %1 - returning %2

A debug message noting that the resolver received a message and the parsing of the body of the message failed due to some protocol error (although the parsing of the header succeeded). The message parameters give a textual description of the problem and the RCODE returned. -

RESOLVER_QUSETUP query setup

+

RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4

+A debug message that indicates an incoming query is accepted in terms of +the query ACL. The log message shows the query in the form of +<query name>/<query type>/<query class>, and the client that sends the +query in the form of <Source IP address>#<source port>. +

RESOLVER_QUERY_DROPPED query dropped: '%1/%2/%3' from %4

+An informational message that indicates an incoming query is dropped +in terms of the query ACL. Unlike the RESOLVER_QUERY_REJECTED +case, the server does not return any response. The log message +shows the query in the form of <query name>/<query type>/<query +class>, and the client that sends the query in the form of <Source +IP address>#<source port>. +

RESOLVER_QUERY_REJECTED query rejected: '%1/%2/%3' from %4

+An informational message that indicates an incoming query is rejected +in terms of the query ACL. This results in a response with an RCODE of +REFUSED. The log message shows the query in the form of <query +name>/<query type>/<query class>, and the client that sends the +query in the form of <Source IP address>#<source port>. +

RESOLVER_QUERY_SETUP query setup

A debug message noting that the resolver is creating a RecursiveQuery object. -

RESOLVER_QUSHUT query shutdown

+

RESOLVER_QUERY_SHUTDOWN query shutdown

A debug message noting that the resolver is destroying a RecursiveQuery object. -

RESOLVER_QUTMOSMALL query timeout of %1 is too small

+

RESOLVER_QUERY_TIME_SMALL query timeout of %1 is too small

An error indicating that the configuration value specified for the query timeout is too small. -

RESOLVER_RECURSIVE running in recursive mode

-This is an informational message that appears at startup noting that the -resolver is running in recursive mode. -

RESOLVER_RECVMSG resolver has received a DNS message

+

RESOLVER_RECEIVED_MESSAGE resolver has received a DNS message

A debug message indicating that the resolver has received a message. Depending on the debug settings, subsequent log output will indicate the nature of the message. -

RESOLVER_RETRYNEG negative number of retries (%1) specified in the configuration

-An error message indicating that the resolver configuration has specified a -negative retry count. Only zero or positive values are valid. -

RESOLVER_ROOTADDR setting root address %1(%2)

-This message may appear multiple times during startup; it lists the root -addresses used by the resolver. -

RESOLVER_SERVICE service object created

+

RESOLVER_RECURSIVE running in recursive mode

+This is an informational message that appears at startup noting that the +resolver is running in recursive mode. +

RESOLVER_SERVICE_CREATED service object created

A debug message, output when the main service object (which handles the received queries) is created. -

RESOLVER_SETPARAM query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4

-A debug message, lists the parameters associated with the message. These are: +

RESOLVER_SET_PARAMS query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4

+A debug message, lists the parameters being set for the resolver. These are: query timeout: the timeout (in ms) used for queries originated by the resolver to upstream servers. Client timeout: the interval to resolver a query by a client: after this time, the resolver sends back a SERVFAIL to the client @@ -819,14 +1168,20 @@ resolver gives up trying to resolve a query. Retry count: the number of times the resolver will retry a query to an upstream server if it gets a timeout.

The client and lookup timeouts require a bit more explanation. The -resolution of the clent query might require a large number of queries to +resolution of the client query might require a large number of queries to upstream nameservers. Even if none of these queries timeout, the total time taken to perform all the queries may exceed the client timeout. When this happens, a SERVFAIL is returned to the client, but the resolver continues with the resolution process. Data received is added to the cache. However, -there comes a time - the lookup timeout - when even the resolve gives up. +there comes a time - the lookup timeout - when even the resolver gives up. At this point it will wait for pending upstream queries to complete or timeout and drop the query. +

RESOLVER_SET_QUERY_ACL query ACL is configured

+A debug message that appears when a new query ACL is configured for the +resolver. +

RESOLVER_SET_ROOT_ADDRESS setting root address %1(%2)

+This message may appear multiple times during startup; it lists the root +addresses used by the resolver.

RESOLVER_SHUTDOWN resolver shutdown complete

This information message is output when the resolver has shut down.

RESOLVER_STARTED resolver started

@@ -834,8 +1189,166 @@ This informational message is output by the resolver when all initialization has been completed and it is entering its main loop.

RESOLVER_STARTING starting resolver with command line '%1'

An informational message, this is output when the resolver starts up. -

RESOLVER_UNEXRESP received unexpected response, ignoring

+

RESOLVER_UNEXPECTED_RESPONSE received unexpected response, ignoring

A debug message noting that the server has received a response instead of a query and is ignoring it. +

RESOLVER_UNSUPPORTED_OPCODE opcode %1 not supported by the resolver

+A debug message, the resolver received a message with an unsupported opcode +(it can only process QUERY opcodes). It will return a message to the sender +with the RCODE set to NOTIMP. +

XFRIN_AXFR_DATABASE_FAILURE AXFR transfer of zone %1 failed: %2

+The AXFR transfer for the given zone has failed due to a database problem. +The error is shown in the log message. +

XFRIN_AXFR_INTERNAL_FAILURE AXFR transfer of zone %1 failed: %2

+The AXFR transfer for the given zone has failed due to an internal +problem in the bind10 python wrapper library. +The error is shown in the log message. +

XFRIN_AXFR_TRANSFER_FAILURE AXFR transfer of zone %1 failed: %2

+The AXFR transfer for the given zone has failed due to a protocol error. +The error is shown in the log message. +

XFRIN_AXFR_TRANSFER_STARTED AXFR transfer of zone %1 started

+A connection to the master server has been made, the serial value in +the SOA record has been checked, and a zone transfer has been started. +

XFRIN_AXFR_TRANSFER_SUCCESS AXFR transfer of zone %1 succeeded

+The AXFR transfer of the given zone was successfully completed. +

XFRIN_BAD_MASTER_ADDR_FORMAT bad format for master address: %1

+The given master address is not a valid IP address. +

XFRIN_BAD_MASTER_PORT_FORMAT bad format for master port: %1

+The master port as read from the configuration is not a valid port number. +

XFRIN_BAD_TSIG_KEY_STRING bad TSIG key string: %1

+The TSIG key string as read from the configuration does not represent +a valid TSIG key. +

XFRIN_BAD_ZONE_CLASS Invalid zone class: %1

+The zone class as read from the configuration is not a valid DNS class. +

XFRIN_CC_SESSION_ERROR error reading from cc channel: %1

+There was a problem reading from the command and control channel. The +most likely cause is that xfrin the msgq daemon is not running. +

XFRIN_COMMAND_ERROR error while executing command '%1': %2

+There was an error while the given command was being processed. The +error is given in the log message. +

XFRIN_CONNECT_MASTER error connecting to master at %1: %2

+There was an error opening a connection to the master. The error is +shown in the log message. +

XFRIN_IMPORT_DNS error importing python DNS module: %1

+There was an error importing the python DNS module pydnspp. The most +likely cause is a PYTHONPATH problem. +

XFRIN_MSGQ_SEND_ERROR error while contacting %1 and %2

+There was a problem sending a message to the xfrout module or the +zone manager. This most likely means that the msgq daemon has quit or +was killed. +

XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER error while contacting %1

+There was a problem sending a message to the zone manager. This most +likely means that the msgq daemon has quit or was killed. +

XFRIN_RETRANSFER_UNKNOWN_ZONE got notification to retransfer unknown zone %1

+There was an internal command to retransfer the given zone, but the +zone is not known to the system. This may indicate that the configuration +for xfrin is incomplete, or there was a typographical error in the +zone name in the configuration. +

XFRIN_STARTING starting resolver with command line '%1'

+An informational message, this is output when the resolver starts up. +

XFRIN_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down

+There was a keyboard interrupt signal to stop the xfrin daemon. The +daemon will now shut down. +

XFRIN_UNKNOWN_ERROR unknown error: %1

+An uncaught exception was raised while running the xfrin daemon. The +exception message is printed in the log message. +

XFROUT_AXFR_TRANSFER_DONE transfer of %1/%2 complete

+The transfer of the given zone has been completed successfully, or was +aborted due to a shutdown event. +

XFROUT_AXFR_TRANSFER_ERROR error transferring zone %1/%2: %3

+An uncaught exception was encountered while sending the response to +an AXFR query. The error message of the exception is included in the +log message, but this error most likely points to incomplete exception +handling in the code. +

XFROUT_AXFR_TRANSFER_FAILED transfer of %1/%2 failed, rcode: %3

+A transfer out for the given zone failed. An error response is sent +to the client. The given rcode is the rcode that is set in the error +response. This is either NOTAUTH (we are not authoritative for the +zone), SERVFAIL (our internal database is missing the SOA record for +the zone), or REFUSED (the limit of simultaneous outgoing AXFR +transfers, as specified by the configuration value +Xfrout/max_transfers_out, has been reached). +

XFROUT_AXFR_TRANSFER_STARTED transfer of zone %1/%2 has started

+A transfer out of the given zone has started. +

XFROUT_BAD_TSIG_KEY_STRING bad TSIG key string: %1

+The TSIG key string as read from the configuration does not represent +a valid TSIG key. +

XFROUT_CC_SESSION_ERROR error reading from cc channel: %1

+There was a problem reading from the command and control channel. The +most likely cause is that the msgq daemon is not running. +

XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response

+There was a problem reading a response from antoher module over the +command and control channel. The most likely cause is that the +configuration manager b10-cfgmgr is not running. +

XFROUT_FETCH_REQUEST_ERROR socket error while fetching a request from the auth daemon

+There was a socket error while contacting the b10-auth daemon to +fetch a transfer request. The auth daemon may have shutdown. +

XFROUT_HANDLE_QUERY_ERROR error while handling query: %1

+There was a general error handling an xfrout query. The error is shown +in the message. In principle this error should not appear, and points +to an oversight catching exceptions in the right place. However, to +ensure the daemon keeps running, this error is caught and reported. +

XFROUT_IMPORT error importing python module: %1

+There was an error importing a python module. One of the modules needed +by xfrout could not be found. This suggests that either some libraries +are missing on the system, or the PYTHONPATH variable is not correct. +The specific place where this library needs to be depends on your +system and your specific installation. +

XFROUT_NEW_CONFIG Update xfrout configuration

+New configuration settings have been sent from the configuration +manager. The xfrout daemon will now apply them. +

XFROUT_NEW_CONFIG_DONE Update xfrout configuration done

+The xfrout daemon is now done reading the new configuration settings +received from the configuration manager. +

XFROUT_NOTIFY_COMMAND received command to send notifies for %1/%2

+The xfrout daemon received a command on the command channel that +NOTIFY packets should be sent for the given zone. +

XFROUT_PARSE_QUERY_ERROR error parsing query: %1

+There was a parse error while reading an incoming query. The parse +error is shown in the log message. A remote client sent a packet we +do not understand or support. The xfrout request will be ignored. +In general, this should only occur for unexpected problems like +memory allocation failures, as the query should already have been +parsed by the b10-auth daemon, before it was passed here. +

XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %2

+There was an error processing a transfer request. The error is included +in the log message, but at this point no specific information other +than that could be given. This points to incomplete exception handling +in the code. +

XFROUT_RECEIVED_SHUTDOWN_COMMAND shutdown command received

+The xfrout daemon received a shutdown command from the command channel +and will now shut down. +

XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection

+There was an error receiving the file descriptor for the transfer +request. Normally, the request is received by b10-auth, and passed on +to the xfrout daemon, so it can answer directly. However, there was a +problem receiving this file descriptor. The request will be ignored. +

XFROUT_REMOVE_OLD_UNIX_SOCKET_FILE_ERROR error removing unix socket file %1: %2

+The unix socket file xfrout needs for contact with the auth daemon +already exists, and needs to be removed first, but there is a problem +removing it. It is likely that we do not have permission to remove +this file. The specific error is show in the log message. The xfrout +daemon will shut down. +

XFROUT_REMOVE_UNIX_SOCKET_FILE_ERROR error clearing unix socket file %1: %2

+When shutting down, the xfrout daemon tried to clear the unix socket +file used for communication with the auth daemon. It failed to remove +the file. The reason for the failure is given in the error message. +

XFROUT_SOCKET_SELECT_ERROR error while calling select() on request socket: %1

+There was an error while calling select() on the socket that informs +the xfrout daemon that a new xfrout request has arrived. This should +be a result of rare local error such as memory allocation failure and +shouldn't happen under normal conditions. The error is included in the +log message. +

XFROUT_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down

+There was a keyboard interrupt signal to stop the xfrout daemon. The +daemon will now shut down. +

XFROUT_STOPPING the xfrout daemon is shutting down

+The current transfer is aborted, as the xfrout daemon is shutting down. +

XFROUT_UNIX_SOCKET_FILE_IN_USE another xfrout process seems to be using the unix socket file %1

+While starting up, the xfrout daemon tried to clear the unix domain +socket needed for contacting the b10-auth daemon to pass requests +on, but the file is in use. The most likely cause is that another +xfrout daemon process is still running. This xfrout daemon (the one +printing this message) will not start.

diff --git a/doc/guide/bind10-messages.xml b/doc/guide/bind10-messages.xml index eaa8bb99a1..d146a9ca56 100644 --- a/doc/guide/bind10-messages.xml +++ b/doc/guide/bind10-messages.xml @@ -5,6 +5,12 @@ %version; ]> + @@ -62,16 +68,16 @@ - -ASIODNS_FETCHCOMP upstream fetch to %1(%2) has now completed + +ASIODNS_FETCH_COMPLETED upstream fetch to %1(%2) has now completed -A debug message, this records the the upstream fetch (a query made by the +A debug message, this records that the upstream fetch (a query made by the resolver on behalf of its client) to the specified address has completed. - -ASIODNS_FETCHSTOP upstream fetch to %1(%2) has been stopped + +ASIODNS_FETCH_STOPPED upstream fetch to %1(%2) has been stopped An external component has requested the halting of an upstream fetch. This is an allowed operation, and the message should only appear if debug is @@ -79,27 +85,27 @@ enabled. - -ASIODNS_OPENSOCK error %1 opening %2 socket to %3(%4) + +ASIODNS_OPEN_SOCKET error %1 opening %2 socket to %3(%4) The asynchronous I/O code encountered an error when trying to open a socket of the specified protocol in order to send a message to the target address. -The the number of the system error that cause the problem is given in the +The number of the system error that cause the problem is given in the message. - -ASIODNS_RECVSOCK error %1 reading %2 data from %3(%4) + +ASIODNS_READ_DATA error %1 reading %2 data from %3(%4) -The asynchronous I/O code encountered an error when trying read data from -the specified address on the given protocol. The the number of the system +The asynchronous I/O code encountered an error when trying to read data from +the specified address on the given protocol. The number of the system error that cause the problem is given in the message. - -ASIODNS_RECVTMO receive timeout while waiting for data from %1(%2) + +ASIODNS_READ_TIMEOUT receive timeout while waiting for data from %1(%2) An upstream fetch from the specified address timed out. This may happen for any number of reasons and is most probably a problem at the remote server @@ -108,8 +114,8 @@ enabled. - -ASIODNS_SENDSOCK error %1 sending data using %2 to %3(%4) + +ASIODNS_SEND_DATA error %1 sending data using %2 to %3(%4) The asynchronous I/O code encountered an error when trying send data to the specified address on the given protocol. The the number of the system @@ -117,20 +123,674 @@ error that cause the problem is given in the message. - -ASIODNS_UNKORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3) + +ASIODNS_UNKNOWN_ORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3) -This message should not appear and indicates an internal error if it does. -Please enter a bug report. +An internal consistency check on the origin of a message from the +asynchronous I/O module failed. This may indicate an internal error; +please submit a bug report. - -ASIODNS_UNKRESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3) + +ASIODNS_UNKNOWN_RESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3) -The termination method of the resolver's upstream fetch class was called with -an unknown result code (which is given in the message). This message should -not appear and may indicate an internal error. Please enter a bug report. +An internal error indicating that the termination method of the resolver's +upstream fetch class was called with an unknown result code (which is +given in the message). Please submit a bug report. + + + + +AUTH_AXFR_ERROR error handling AXFR request: %1 + +This is a debug message produced by the authoritative server when it +has encountered an error processing an AXFR request. The message gives +the reason for the error, and the server will return a SERVFAIL code to +the sender. + + + + +AUTH_AXFR_UDP AXFR query received over UDP + +This is a debug message output when the authoritative server has received +an AXFR query over UDP. Use of UDP for AXFRs is not permitted by the +protocol, so the server will return a FORMERR error to the sender. + + + + +AUTH_COMMAND_FAILED execution of command channel instruction '%1' failed: %2 + +Execution of the specified command by the authoritative server failed. The +message contains the reason for the failure. + + + + +AUTH_CONFIG_CHANNEL_CREATED configuration session channel created + +This is a debug message indicating that authoritative server has created +the channel to the configuration manager. It is issued during server +startup is an indication that the initialization is proceeding normally. + + + + +AUTH_CONFIG_CHANNEL_ESTABLISHED configuration session channel established + +This is a debug message indicating that authoritative server +has established communication the configuration manager over the +previously-created channel. It is issued during server startup is an +indication that the initialization is proceeding normally. + + + + +AUTH_CONFIG_CHANNEL_STARTED configuration session channel started + +This is a debug message, issued when the authoritative server has +posted a request to be notified when new configuration information is +available. It is issued during server startup is an indication that +the initialization is proceeding normally. + + + + +AUTH_CONFIG_LOAD_FAIL load of configuration failed: %1 + +An attempt to configure the server with information from the configuration +database during the startup sequence has failed. (The reason for +the failure is given in the message.) The server will continue its +initialization although it may not be configured in the desired way. + + + + +AUTH_CONFIG_UPDATE_FAIL update of configuration failed: %1 + +At attempt to update the configuration the server with information +from the configuration database has failed, the reason being given in +the message. + + + + +AUTH_DATA_SOURCE data source database file: %1 + +This is a debug message produced by the authoritative server when it accesses a +datebase data source, listing the file that is being accessed. + + + + +AUTH_DNS_SERVICES_CREATED DNS services created + +This is a debug message indicating that the component that will handling +incoming queries for the authoritiative server (DNSServices) has been +successfully created. It is issued during server startup is an indication +that the initialization is proceeding normally. + + + + +AUTH_HEADER_PARSE_FAIL unable to parse header in received DNS packet: %1 + +This is a debug message, generated by the authoritative server when an +attempt to parse the header of a received DNS packet has failed. (The +reason for the failure is given in the message.) The server will drop the +packet. + + + + +AUTH_LOAD_TSIG loading TSIG keys + +This is a debug message indicating that the authoritiative server +has requested the keyring holding TSIG keys from the configuration +database. It is issued during server startup is an indication that the +initialization is proceeding normally. + + + + +AUTH_LOAD_ZONE loaded zone %1/%2 + +This debug message is issued during the processing of the 'loadzone' command +when the authoritative server has successfully loaded the named zone of the +named class. + + + + +AUTH_MEM_DATASRC_DISABLED memory data source is disabled for class %1 + +This is a debug message reporting that the authoritative server has +discovered that the memory data source is disabled for the given class. + + + + +AUTH_MEM_DATASRC_ENABLED memory data source is enabled for class %1 + +This is a debug message reporting that the authoritative server has +discovered that the memory data source is enabled for the given class. + + + + +AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY + +This debug message is logged by the authoritative server when it receives +a NOTIFY packet that contains zero or more than one question. (A valid +NOTIFY packet contains one question.) The server will return a FORMERR +error to the sender. + + + + +AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY + +This debug message is logged by the authoritative server when it receives +a NOTIFY packet that an RR type of something other than SOA in the +question section. (The RR type received is included in the message.) The +server will return a FORMERR error to the sender. + + + + +AUTH_NO_STATS_SESSION session interface for statistics is not available + +The authoritative server had no session with the statistics module at the +time it attempted to send it data: the attempt has been abandoned. This +could be an error in configuration. + + + + +AUTH_NO_XFRIN received NOTIFY but XFRIN session is not running + +This is a debug message produced by the authoritative server when it receives +a NOTIFY packet but the XFRIN process is not running. The packet will be +dropped and nothing returned to the sender. + + + + +AUTH_PACKET_PARSE_ERROR unable to parse received DNS packet: %1 + +This is a debug message, generated by the authoritative server when an +attempt to parse a received DNS packet has failed due to something other +than a protocol error. The reason for the failure is given in the message; +the server will return a SERVFAIL error code to the sender. + + + + +AUTH_PACKET_PROTOCOL_ERROR DNS packet protocol error: %1. Returning %2 + +This is a debug message, generated by the authoritative server when an +attempt to parse a received DNS packet has failed due to a protocol error. +The reason for the failure is given in the message, as is the error code +that will be returned to the sender. + + + + +AUTH_PACKET_RECEIVED message received:\n%1 + +This is a debug message output by the authoritative server when it +receives a valid DNS packet. + +Note: This message includes the packet received, rendered in the form of +multiple lines of text. For this reason, it is suggested that this log message +not be routed to the syslog file, where the multiple lines could confuse +programs that expect a format of one message per line. + + + + +AUTH_PROCESS_FAIL message processing failure: %1 + +This message is generated by the authoritative server when it has +encountered an internal error whilst processing a received packet: +the cause of the error is included in the message. + +The server will return a SERVFAIL error code to the sender of the packet. +However, this message indicates a potential error in the server. +Please open a bug ticket for this issue. + + + + +AUTH_RECEIVED_COMMAND command '%1' received + +This is a debug message issued when the authoritative server has received +a command on the command channel. + + + + +AUTH_RECEIVED_SENDSTATS command 'sendstats' received + +This is a debug message issued when the authoritative server has received +a command from the statistics module to send it data. The 'sendstats' +command is handled differently to other commands, which is why the debug +message associated with it has its own code. + + + + +AUTH_RESPONSE_RECEIVED received response message, ignoring + +This is a debug message, this is output if the authoritative server +receives a DNS packet with the QR bit set, i.e. a DNS response. The +server ignores the packet as it only responds to question packets. + + + + +AUTH_SEND_ERROR_RESPONSE sending an error response (%1 bytes):\n%2 + +This is a debug message recording that the authoritative server is sending +an error response to the originator of the query. A previous message will +have recorded details of the failure. + +Note: This message includes the packet sent, rendered in the form of +multiple lines of text. For this reason, it is suggested that this log message +not be routed to the syslog file, where the multiple lines could confuse +programs that expect a format of one message per line. + + + + +AUTH_SEND_NORMAL_RESPONSE sending an error response (%1 bytes):\n%2 + +This is a debug message recording that the authoritative server is sending +a response to the originator of a query. + +Note: This message includes the packet sent, rendered in the form of +multiple lines of text. For this reason, it is suggested that this log message +not be routed to the syslog file, where the multiple lines could confuse +programs that expect a format of one message per line. + + + + +AUTH_SERVER_CREATED server created + +An informational message indicating that the authoritative server process has +been created and is initializing. The AUTH_SERVER_STARTED message will be +output when initialization has successfully completed and the server starts +accepting queries. + + + + +AUTH_SERVER_FAILED server failed: %1 + +The authoritative server has encountered a fatal error and is terminating. The +reason for the failure is included in the message. + + + + +AUTH_SERVER_STARTED server started + +Initialization of the authoritative server has completed successfully +and it is entering the main loop, waiting for queries to arrive. + + + + +AUTH_SQLITE3 nothing to do for loading sqlite3 + +This is a debug message indicating that the authoritative server has +found that the data source it is loading is an SQLite3 data source, +so no further validation is needed. + + + + +AUTH_STATS_CHANNEL_CREATED STATS session channel created + +This is a debug message indicating that the authoritative server has +created a channel to the statistics process. It is issued during server +startup is an indication that the initialization is proceeding normally. + + + + +AUTH_STATS_CHANNEL_ESTABLISHED STATS session channel established + +This is a debug message indicating that the authoritative server +has established communication over the previously created statistics +channel. It is issued during server startup is an indication that the +initialization is proceeding normally. + + + + +AUTH_STATS_COMMS communication error in sending statistics data: %1 + +An error was encountered when the authoritiative server tried to send data +to the statistics daemon. The message includes additional information +describing the reason for the failure. + + + + +AUTH_STATS_TIMEOUT timeout while sending statistics data: %1 + +The authoritative server sent data to the statistics daemon but received +no acknowledgement within the specified time. The message includes +additional information describing the reason for the failure. + + + + +AUTH_STATS_TIMER_DISABLED statistics timer has been disabled + +This is a debug message indicating that the statistics timer has been +disabled in the authoritative server and no statistics information is +being produced. + + + + +AUTH_STATS_TIMER_SET statistics timer set to %1 second(s) + +This is a debug message indicating that the statistics timer has been +enabled and that the authoritative server will produce statistics data +at the specified interval. + + + + +AUTH_UNSUPPORTED_OPCODE unsupported opcode: %1 + +This is a debug message, produced when a received DNS packet being +processed by the authoritative server has been found to contain an +unsupported opcode. (The opcode is included in the message.) The server +will return an error code of NOTIMPL to the sender. + + + + +AUTH_XFRIN_CHANNEL_CREATED XFRIN session channel created + +This is a debug message indicating that the authoritative server has +created a channel to the XFRIN (Transfer-in) process. It is issued +during server startup is an indication that the initialization is +proceeding normally. + + + + +AUTH_XFRIN_CHANNEL_ESTABLISHED XFRIN session channel established + +This is a debug message indicating that the authoritative server has +established communication over the previously-created channel to the +XFRIN (Transfer-in) process. It is issued during server startup is an +indication that the initialization is proceeding normally. + + + + +AUTH_ZONEMGR_COMMS error communicating with zone manager: %1 + +This is a debug message output during the processing of a NOTIFY request. +An error (listed in the message) has been encountered whilst communicating +with the zone manager. The NOTIFY request will not be honored. + + + + +AUTH_ZONEMGR_ERROR received error response from zone manager: %1 + +This is a debug message output during the processing of a NOTIFY +request. The zone manager component has been informed of the request, +but has returned an error response (which is included in the message). The +NOTIFY request will not be honored. + + + + +CC_ASYNC_READ_FAILED asynchronous read failed + +This marks a low level error, we tried to read data from the message queue +daemon asynchronously, but the ASIO library returned an error. + + + + +CC_CONN_ERROR error connecting to message queue (%1) + +It is impossible to reach the message queue daemon for the reason given. It +is unlikely there'll be reason for whatever program this currently is to +continue running, as the communication with the rest of BIND 10 is vital +for the components. + + + + +CC_DISCONNECT disconnecting from message queue daemon + +The library is disconnecting from the message queue daemon. This debug message +indicates that the program is trying to shut down gracefully. + + + + +CC_ESTABLISH trying to establish connection with message queue daemon at %1 + +This debug message indicates that the command channel library is about to +connect to the message queue daemon, which should be listening on the UNIX-domain +socket listed in the output. + + + + +CC_ESTABLISHED successfully connected to message queue daemon + +This debug message indicates that the connection was successfully made, this +should follow CC_ESTABLISH. + + + + +CC_GROUP_RECEIVE trying to receive a message + +Debug message, noting that a message is expected to come over the command +channel. + + + + +CC_GROUP_RECEIVED message arrived ('%1', '%2') + +Debug message, noting that we successfully received a message (its envelope and +payload listed). This follows CC_GROUP_RECEIVE, but might happen some time +later, depending if we waited for it or just polled. + + + + +CC_GROUP_SEND sending message '%1' to group '%2' + +Debug message, we're about to send a message over the command channel. + + + + +CC_INVALID_LENGTHS invalid length parameters (%1, %2) + +This happens when garbage comes over the command channel or some kind of +confusion happens in the program. The data received from the socket make no +sense if we interpret it as lengths of message. The first one is total length +of message, the second length of the header. The header and it's length +(2 bytes) is counted in the total length. + + + + +CC_LENGTH_NOT_READY length not ready + +There should be data representing length of message on the socket, but it +is not there. + + + + +CC_NO_MESSAGE no message ready to be received yet + +The program polled for incoming messages, but there was no message waiting. +This is a debug message which may happen only after CC_GROUP_RECEIVE. + + + + +CC_NO_MSGQ unable to connect to message queue (%1) + +It isn't possible to connect to the message queue daemon, for reason listed. +It is unlikely any program will be able continue without the communication. + + + + +CC_READ_ERROR error reading data from command channel (%1) + +A low level error happened when the library tried to read data from the +command channel socket. The reason is listed. + + + + +CC_READ_EXCEPTION error reading data from command channel (%1) + +We received an exception while trying to read data from the command +channel socket. The reason is listed. + + + + +CC_REPLY replying to message from '%1' with '%2' + +Debug message, noting we're sending a response to the original message +with the given envelope. + + + + +CC_SET_TIMEOUT setting timeout to %1ms + +Debug message. A timeout for which the program is willing to wait for a reply +is being set. + + + + +CC_START_READ starting asynchronous read + +Debug message. From now on, when a message (or command) comes, it'll wake the +program and the library will automatically pass it over to correct place. + + + + +CC_SUBSCRIBE subscribing to communication group %1 + +Debug message. The program wants to receive messages addressed to this group. + + + + +CC_TIMEOUT timeout reading data from command channel + +The program waited too long for data from the command channel (usually when it +sent a query to different program and it didn't answer for whatever reason). + + + + +CC_UNSUBSCRIBE unsubscribing from communication group %1 + +Debug message. The program no longer wants to receive messages addressed to +this group. + + + + +CC_WRITE_ERROR error writing data to command channel (%1) + +A low level error happened when the library tried to write data to the command +channel socket. + + + + +CC_ZERO_LENGTH invalid message length (0) + +The library received a message length being zero, which makes no sense, since +all messages must contain at least the envelope. + + + + +CFGMGR_AUTOMATIC_CONFIG_DATABASE_UPDATE Updating configuration database from version %1 to %2 + +An older version of the configuration database has been found, from which +there was an automatic upgrade path to the current version. These changes +are now applied, and no action from the administrator is necessary. + + + + +CFGMGR_CC_SESSION_ERROR Error connecting to command channel: %1 + +The configuration manager daemon was unable to connect to the messaging +system. The most likely cause is that msgq is not running. + + + + +CFGMGR_DATA_READ_ERROR error reading configuration database from disk: %1 + +There was a problem reading the persistent configuration data as stored +on disk. The file may be corrupted, or it is of a version from where +there is no automatic upgrade path. The file needs to be repaired or +removed. The configuration manager daemon will now shut down. + + + + +CFGMGR_IOERROR_WHILE_WRITING_CONFIGURATION Unable to write configuration file; configuration not stored: %1 + +There was an IO error from the system while the configuration manager +was trying to write the configuration database to disk. The specific +error is given. The most likely cause is that the directory where +the file is stored does not exist, or is not writable. The updated +configuration is not stored. + + + + +CFGMGR_OSERROR_WHILE_WRITING_CONFIGURATION Unable to write configuration file; configuration not stored: %1 + +There was an OS error from the system while the configuration manager +was trying to write the configuration database to disk. The specific +error is given. The most likely cause is that the system does not have +write access to the configuration database file. The updated +configuration is not stored. + + + + +CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down + +There was a keyboard interrupt signal to stop the cfgmgr daemon. The +daemon will now shut down. @@ -148,32 +808,18 @@ The message itself is ignored by this module. CONFIG_CCSESSION_MSG_INTERNAL error handling CC session message: %1 -There was an internal problem handling an incoming message on the -command and control channel. An unexpected exception was thrown. This -most likely points to an internal inconsistency in the module code. The -exception message is appended to the log error, and the module will -continue to run, but will not send back an answer. +There was an internal problem handling an incoming message on the command +and control channel. An unexpected exception was thrown, details of +which are appended to the message. The module will continue to run, +but will not send back an answer. + +The most likely cause of this error is a programming error. Please raise +a bug report. - -CONFIG_FOPEN_ERR error opening %1: %2 - -There was an error opening the given file. - - - - -CONFIG_JSON_PARSE JSON parse error in %1: %2 - -There was a parse error in 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_MANAGER_CONFIG error getting configuration from cfgmgr: %1 + +CONFIG_GET_FAIL error getting configuration from cfgmgr: %1 The configuration manager returned an error when this module requested the configuration. The full error message answer from the configuration @@ -183,23 +829,40 @@ running configuration manager. - -CONFIG_MANAGER_MOD_SPEC module specification not accepted by cfgmgr: %1 + +CONFIG_JSON_PARSE JSON parse error in %1: %2 -The module specification file for this module was rejected by the -configuration manager. The full error message answer from the -configuration manager is appended to the log error. The most likely -cause is that the module is of a different (specification file) version -than the running configuration manager. +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_MODULE_SPEC module specification error in %1: %2 + +CONFIG_MOD_SPEC_FORMAT module specification error in %1: %2 -The given file does not appear to be a valid specification file. Please -verify that the filename is correct and that its contents are a valid -BIND10 module specification. +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 +and that its contents are a valid BIND10 module specification. + + + + +CONFIG_MOD_SPEC_REJECT module specification rejected by cfgmgr: %1 + +The specification file for this module was rejected by the configuration +manager. The full error message answer from the configuration manager is +appended to the log error. The most likely cause is that the module is of +a different (specification file) version than the running configuration +manager. + + + + +CONFIG_OPEN_FAIL error opening %1: %2 + +There was an error opening the given file. The reason for the failure +is included in the message. @@ -349,7 +1012,7 @@ returning the CNAME instead. DATASRC_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1' This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the -other way around -- adding some outher data to CNAME. +other way around -- adding some other data to CNAME. @@ -401,7 +1064,7 @@ Debug information. A DNAME was found instead of the requested information. -DATASRC_MEM_DNAME_NS dNAME and NS can't coexist in non-apex domain '%1' +DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1' It was requested for DNAME and NS records to be put into the same domain which is not the apex (the top of the zone). This is forbidden by RFC @@ -544,7 +1207,7 @@ behaviour is specified by RFC 1034, section 4.3.3 -DATASRC_MEM_WILDCARD_DNAME dNAME record in wildcard domain '%1' +DATASRC_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1' The software refuses to load DNAME records into a wildcard domain. It isn't explicitly forbidden, but the protocol is ambiguous about how this should @@ -554,7 +1217,7 @@ different tools. -DATASRC_MEM_WILDCARD_NS nS record in wildcard domain '%1' +DATASRC_MEM_WILDCARD_NS NS record in wildcard domain '%1' The software refuses to load NS records into a wildcard domain. It isn't explicitly forbidden, but the protocol is ambiguous about how this should @@ -666,7 +1329,7 @@ way down to the given domain. -DATASRC_QUERY_EMPTY_CNAME cNAME at '%1' is empty +DATASRC_QUERY_EMPTY_CNAME CNAME at '%1' is empty There was an CNAME and it was being followed. But it contains no records, so there's nowhere to go. There will be no answer. This indicates a problem @@ -905,7 +1568,7 @@ already. The code is 1 for error, 2 for not implemented. -DATASRC_QUERY_TOO_MANY_CNAMES cNAME chain limit exceeded at '%1' +DATASRC_QUERY_TOO_MANY_CNAMES CNAME chain limit exceeded at '%1' A CNAME led to another CNAME and it led to another, and so on. After 16 CNAMEs, the software gave up. Long CNAME chains are discouraged, and this @@ -962,14 +1625,14 @@ Debug information. The SQLite data source is closing the database file. -DATASRC_SQLITE_CREATE sQLite data source created +DATASRC_SQLITE_CREATE SQLite data source created Debug information. An instance of SQLite data source is being created. -DATASRC_SQLITE_DESTROY sQLite data source destroyed +DATASRC_SQLITE_DESTROY SQLite data source destroyed Debug information. An instance of SQLite data source is being destroyed. @@ -978,7 +1641,7 @@ Debug information. An instance of SQLite data source is being destroyed. DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1' -Debug information. The SQLite data source is trying to identify, which zone +Debug information. The SQLite data source is trying to identify which zone should hold this domain. @@ -986,7 +1649,7 @@ should hold this domain. DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it -Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's +Debug information. The last SQLITE_ENCLOSURE query was unsuccessful; there's no such zone in our data. @@ -1050,7 +1713,7 @@ a referral and where it goes. DATASRC_SQLITE_FINDREF_BAD_CLASS class mismatch looking for referral ('%1' and '%2') -The SQLite data source was trying to identify, if there's a referral. But +The SQLite data source was trying to identify if there's a referral. But it contains different class than the query was for. @@ -1143,294 +1806,325 @@ generated. - -LOGIMPL_ABOVEDBGMAX debug level of %1 is too high and will be set to the maximum of %2 + +LOGIMPL_ABOVE_MAX_DEBUG debug level of %1 is too high and will be set to the maximum of %2 -A message from the underlying logger implementation code, the debug level -(as set by the string DEBGUGn) is above the maximum allowed value and has -been reduced to that value. +A message from the interface to the underlying logger implementation reporting +that the debug level (as set by an internally-created string DEBUGn, where n +is an integer, e.g. DEBUG22) is above the maximum allowed value and has +been reduced to that value. The appearance of this message may indicate +a programming error - please submit a bug report. - -LOGIMPL_BADDEBUG debug string is '%1': must be of the form DEBUGn + +LOGIMPL_BAD_DEBUG_STRING debug string '%1' has invalid format -The string indicating the extended logging level (used by the underlying -logger implementation code) is not of the stated form. In particular, -it starts DEBUG but does not end with an integer. +A message from the interface to the underlying logger implementation +reporting that an internally-created string used to set the debug level +is not of the correct format (it should be of the form DEBUGn, where n +is an integer, e.g. DEBUG22). The appearance of this message indicates +a programming error - please submit a bug report. - -LOGIMPL_BELOWDBGMIN debug level of %1 is too low and will be set to the minimum of %2 + +LOGIMPL_BELOW_MIN_DEBUG debug level of %1 is too low and will be set to the minimum of %2 -A message from the underlying logger implementation code, the debug level -(as set by the string DEBGUGn) is below the minimum allowed value and has -been increased to that value. +A message from the interface to the underlying logger implementation reporting +that the debug level (as set by an internally-created string DEBUGn, where n +is an integer, e.g. DEBUG22) is below the minimum allowed value and has +been increased to that value. The appearance of this message may indicate +a programming error - please submit a bug report. - -MSG_BADDESTINATION unrecognized log destination: %1 + +LOG_BAD_DESTINATION unrecognized log destination: %1 A logger destination value was given that was not recognized. The destination should be one of "console", "file", or "syslog". - -MSG_BADSEVERITY unrecognized log severity: %1 + +LOG_BAD_SEVERITY unrecognized log severity: %1 A logger severity value was given that was not recognized. The severity should be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL". - -MSG_BADSTREAM bad log console output stream: %1 + +LOG_BAD_STREAM bad log console output stream: %1 -A log console output stream was given that was not recognized. The -output stream should be one of "stdout", or "stderr" +A log console output stream was given that was not recognized. The output +stream should be one of "stdout", or "stderr" - -MSG_DUPLNS line %1: duplicate $NAMESPACE directive found + +LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code -When reading a message file, more than one $NAMESPACE directive was found. In -this version of the code, such a condition is regarded as an error and the -read will be abandoned. +During start-up, BIND10 detected that the given message identification had +been defined multiple times in the BIND10 code. + +This has no ill-effects other than the possibility that an erronous +message may be logged. However, as it is indicative of a programming +error, please log a bug report. - -MSG_DUPMSGID duplicate message ID (%1) in compiled code + +LOG_DUPLICATE_NAMESPACE line %1: duplicate $NAMESPACE directive found -Indicative of a programming error, when it started up, BIND10 detected that -the given message ID had been registered by one or more modules. (All message -IDs should be unique throughout BIND10.) This has no impact on the operation -of the server other that erroneous messages may be logged. (When BIND10 loads -the message IDs (and their associated text), if a duplicate ID is found it is -discarded. However, when the module that supplied the duplicate ID logs that -particular message, the text supplied by the module that added the original -ID will be output - something that may bear no relation to the condition being -logged. +When reading a message file, more than one $NAMESPACE directive was found. +Such a condition is regarded as an error and the read will be abandoned. - -MSG_IDNOTFND could not replace message text for '%1': no such message + +LOG_INPUT_OPEN_FAIL unable to open message file %1 for input: %2 + +The program was not able to open the specified input message file for +the reason given. + + + + +LOG_INVALID_MESSAGE_ID line %1: invalid message identification '%2' + +An invalid message identification (ID) has been found during the read of +a message file. Message IDs should comprise only alphanumeric characters +and the underscore, and should not start with a digit. + + + + +LOG_NAMESPACE_EXTRA_ARGS line %1: $NAMESPACE directive has too many arguments + +The $NAMESPACE directive in a message file takes a single argument, a +namespace in which all the generated symbol names are placed. This error +is generated when the compiler finds a $NAMESPACE directive with more +than one argument. + + + + +LOG_NAMESPACE_INVALID_ARG line %1: $NAMESPACE directive has an invalid argument ('%2') + +The $NAMESPACE argument in a message file should be a valid C++ namespace. +This message is output if the simple check on the syntax of the string +carried out by the reader fails. + + + + +LOG_NAMESPACE_NO_ARGS line %1: no arguments were given to the $NAMESPACE directive + +The $NAMESPACE directive in a message file takes a single argument, +a C++ namespace in which all the generated symbol names are placed. +This error is generated when the compiler finds a $NAMESPACE directive +with no arguments. + + + + +LOG_NO_MESSAGE_ID line %1: message definition line found without a message ID + +Within a message file, message are defined by lines starting with a "%". +The rest of the line should comprise the message ID and text describing +the message. This error indicates the message compiler found a line in +the message file comprising just the "%" and nothing else. + + + + +LOG_NO_MESSAGE_TEXT line %1: line found containing a message ID ('%2') and no text + +Within a message file, message are defined by lines starting with a "%". +The rest of the line should comprise the message ID and text describing +the message. This error indicates the message compiler found a line +in the message file comprising just the "%" and message identification, +but no text. + + + + +LOG_NO_SUCH_MESSAGE could not replace message text for '%1': no such message During start-up a local message file was read. A line with the listed -message identification was found in the file, but the identification is not -one contained in the compiled-in message dictionary. Either the message -identification has been mis-spelled in the file, or the local file was used -for an earlier version of the software and the message with that -identification has been removed. +message identification was found in the file, but the identification is +not one contained in the compiled-in message dictionary. This message +may appear a number of times in the file, once for every such unknown +message identification. -This message may appear a number of times in the file, once for every such -unknown message identification. +There may be several reasons why this message may appear: + +- The message ID has been mis-spelled in the local message file. + +- The program outputting the message may not use that particular message +(e.g. it originates in a module not used by the program.) + +- The local file was written for an earlier version of the BIND10 software +and the later version no longer generates that message. + +Whatever the reason, there is no impact on the operation of BIND10. - -MSG_INVMSGID line %1: invalid message identification '%2' + +LOG_OPEN_OUTPUT_FAIL unable to open %1 for output: %2 -The concatenation of the prefix and the message identification is used as -a symbol in the C++ module; as such it may only contain +Originating within the logging code, the program was not able to open +the specified output file for the reason given. - -MSG_NOMSGID line %1: message definition line found without a message ID + +LOG_PREFIX_EXTRA_ARGS line %1: $PREFIX directive has too many arguments -Message definition lines are lines starting with a "%". The rest of the line -should comprise the message ID and text describing the message. This error -indicates the message compiler found a line in the message file comprising -just the "%" and nothing else. +Within a message file, the $PREFIX directive takes a single argument, +a prefix to be added to the symbol names when a C++ file is created. +This error is generated when the compiler finds a $PREFIX directive with +more than one argument. + +Note: the $PREFIX directive is deprecated and will be removed in a future +version of BIND10. - -MSG_NOMSGTXT line %1: line found containing a message ID ('%2') and no text + +LOG_PREFIX_INVALID_ARG line %1: $PREFIX directive has an invalid argument ('%2') -Message definition lines are lines starting with a "%". The rest of the line -should comprise the message ID and text describing the message. This error -is generated when a line is found in the message file that contains the -leading "%" and the message identification but no text. +Within a message file, the $PREFIX directive takes a single argument, +a prefix to be added to the symbol names when a C++ file is created. +As such, it must adhere to restrictions on C++ symbol names (e.g. may +only contain alphanumeric characters or underscores, and may nor start +with a digit). A $PREFIX directive was found with an argument (given +in the message) that violates those restictions. + +Note: the $PREFIX directive is deprecated and will be removed in a future +version of BIND10. - -MSG_NSEXTRARG line %1: $NAMESPACE directive has too many arguments + +LOG_READING_LOCAL_FILE reading local message file %1 -The $NAMESPACE directive takes a single argument, a namespace in which all the -generated symbol names are placed. This error is generated when the -compiler finds a $NAMESPACE directive with more than one argument. +This is an informational message output by BIND10 when it starts to read +a local message file. (A local message file may replace the text of +one of more messages; the ID of the message will not be changed though.) - -MSG_NSINVARG line %1: $NAMESPACE directive has an invalid argument ('%2') - -The $NAMESPACE argument should be a valid C++ namespace. The reader does a -cursory check on its validity, checking that the characters in the namespace -are correct. The error is generated when the reader finds an invalid -character. (Valid are alphanumeric characters, underscores and colons.) - - - - -MSG_NSNOARG line %1: no arguments were given to the $NAMESPACE directive - -The $NAMESPACE directive takes a single argument, a namespace in which all the -generated symbol names are placed. This error is generated when the -compiler finds a $NAMESPACE directive with no arguments. - - - - -MSG_OPENIN unable to open message file %1 for input: %2 - -The program was not able to open the specified input message file for the -reason given. - - - - -MSG_OPENOUT unable to open %1 for output: %2 - -The program was not able to open the specified output file for the reason -given. - - - - -MSG_PRFEXTRARG line %1: $PREFIX directive has too many arguments - -The $PREFIX directive takes a single argument, a prefix to be added to the -symbol names when a C++ .h file is created. This error is generated when the -compiler finds a $PREFIX directive with more than one argument. - - - - -MSG_PRFINVARG line %1: $PREFIX directive has an invalid argument ('%2') - -The $PREFIX argument is used in a symbol name in a C++ header file. As such, -it must adhere to restrictions on C++ symbol names (e.g. may only contain -alphanumeric characters or underscores, and may nor start with a digit). -A $PREFIX directive was found with an argument (given in the message) that -violates those restictions. - - - - -MSG_RDLOCMES reading local message file %1 - -This is an informational message output by BIND10 when it starts to read a -local message file. (A local message file may replace the text of one of more -messages; the ID of the message will not be changed though.) - - - - -MSG_READERR error reading from message file %1: %2 + +LOG_READ_ERROR error reading from message file %1: %2 The specified error was encountered reading from the named message file. - -MSG_UNRECDIR line %1: unrecognised directive '%2' + +LOG_UNRECOGNISED_DIRECTIVE line %1: unrecognised directive '%2' -A line starting with a dollar symbol was found, but the first word on the line -(shown in the message) was not a recognised message compiler directive. +Within a message file, a line starting with a dollar symbol was found +(indicating the presence of a directive) but the first word on the line +(shown in the message) was not recognised. - -MSG_WRITERR error writing to %1: %2 + +LOG_WRITE_ERROR error writing to %1: %2 -The specified error was encountered by the message compiler when writing to -the named output file. +The specified error was encountered by the message compiler when writing +to the named output file. - -NSAS_INVRESPSTR queried for %1 but got invalid response + +NSAS_FIND_NS_ADDRESS asking resolver to obtain A and AAAA records for %1 -This message indicates an internal error in the nameserver address store -component (NSAS) of the resolver. The NSAS made a query for a RR for the -specified nameserver but received an invalid response. Either the success -function was called without a DNS message or the message was invalid on some -way. (In the latter case, the error should have been picked up elsewhere in -the processing logic, hence the raising of the error here.) +A debug message issued when the NSAS (nameserver address store - part +of the resolver) is making a callback into the resolver to retrieve the +address records for the specified nameserver. - -NSAS_INVRESPTC queried for %1 RR of type/class %2/%3, received response %4/%5 + +NSAS_FOUND_ADDRESS found address %1 for %2 -This message indicates an internal error in the nameserver address store -component (NSAS) of the resolver. The NSAS made a query for the given RR -type and class, but instead received an answer with the given type and class. +A debug message issued when the NSAS (nameserver address store - part +of the resolver) has retrieved the given address for the specified +nameserver through an external query. - -NSAS_LOOKUPCANCEL lookup for zone %1 has been cancelled + +NSAS_INVALID_RESPONSE queried for %1 but got invalid response -A debug message, this is output when a NSAS (nameserver address store - -part of the resolver) lookup for a zone has been cancelled. +The NSAS (nameserver address store - part of the resolver) made a query +for a RR for the specified nameserver but received an invalid response. +Either the success function was called without a DNS message or the +message was invalid on some way. (In the latter case, the error should +have been picked up elsewhere in the processing logic, hence the raising +of the error here.) + +This message indicates an internal error in the NSAS. Please raise a +bug report. - -NSAS_LOOKUPZONE searching NSAS for nameservers for zone %1 + +NSAS_LOOKUP_CANCEL lookup for zone %1 has been canceled -A debug message, this is output when a call is made to the nameserver address -store (part of the resolver) to obtain the nameservers for the specified zone. +A debug message issued when an NSAS (nameserver address store - part of +the resolver) lookup for a zone has been canceled. - -NSAS_NSADDR asking resolver to obtain A and AAAA records for %1 + +NSAS_NS_LOOKUP_FAIL failed to lookup any %1 for %2 -A debug message, the NSAS (nameserver address store - part of the resolver) is -making a callback into the resolver to retrieve the address records for the -specified nameserver. +A debug message issued when the NSAS (nameserver address store - part of +the resolver) has been unable to retrieve the specified resource record +for the specified nameserver. This is not necessarily a problem - the +nameserver may be unreachable, in which case the NSAS will try other +nameservers in the zone. - -NSAS_NSLKUPFAIL failed to lookup any %1 for %2 + +NSAS_SEARCH_ZONE_NS searching NSAS for nameservers for zone %1 -A debug message, the NSAS (nameserver address store - part of the resolver) -has been unable to retrieve the specified resource record for the specified -nameserver. This is not necessarily a problem - the nameserver may be -unreachable, in which case the NSAS will try other nameservers in the zone. +A debug message output when a call is made to the NSAS (nameserver +address store - part of the resolver) to obtain the nameservers for +the specified zone. - -NSAS_NSLKUPSUCC found address %1 for %2 - -A debug message, the NSAS (nameserver address store - part of the resolver) -has retrieved the given address for the specified nameserver through an -external query. - - - - -NSAS_SETRTT reporting RTT for %1 as %2; new value is now %3 + +NSAS_UPDATE_RTT update RTT for %1: was %2 ms, is now %3 ms A NSAS (nameserver address store - part of the resolver) debug message -reporting the round-trip time (RTT) for a query made to the specified -nameserver. The RTT has been updated using the value given and the new RTT is -displayed. (The RTT is subject to a calculation that damps out sudden -changes. As a result, the new RTT is not necessarily equal to the RTT -reported.) +reporting the update of a round-trip time (RTT) for a query made to the +specified nameserver. The RTT has been updated using the value given +and the new RTT is displayed. (The RTT is subject to a calculation that +damps out sudden changes. As a result, the new RTT used by the NSAS in +future decisions of which nameserver to use is not necessarily equal to +the RTT reported.) + + + + +NSAS_WRONG_ANSWER queried for %1 RR of type/class %2/%3, received response %4/%5 + +A NSAS (nameserver address store - part of the resolver) made a query for +a resource record of a particular type and class, but instead received +an answer with a different given type and class. + +This message indicates an internal error in the NSAS. Please raise a +bug report. @@ -1460,16 +2154,16 @@ type> tuple in the cache; instead, the deepest delegation found is indicated. - -RESLIB_FOLLOWCNAME following CNAME chain to <%1> + +RESLIB_FOLLOW_CNAME following CNAME chain to <%1> A debug message, a CNAME response was received and another query is being issued for the <name, class, type> tuple. - -RESLIB_LONGCHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded + +RESLIB_LONG_CHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded A debug message recording that a CNAME response has been received to an upstream query for the specified question (Previous debug messages will have indicated @@ -1479,26 +2173,26 @@ is where on CNAME points to another) and so an error is being returned. - -RESLIB_NONSRRSET no NS RRSet in referral response received to query for <%1> + +RESLIB_NO_NS_RRSET no NS RRSet in referral response received to query for <%1> A debug message, this indicates that a response was received for the specified -query and was categorised as a referral. However, the received message did +query and was categorized as a referral. However, the received message did not contain any NS RRsets. This may indicate a programming error in the response classification code. - -RESLIB_NSASLOOK looking up nameserver for zone %1 in the NSAS + +RESLIB_NSAS_LOOKUP looking up nameserver for zone %1 in the NSAS A debug message, the RunningQuery object is querying the NSAS for the nameservers for the specified zone. - -RESLIB_NXDOMRR NXDOMAIN/NXRRSET received in response to query for <%1> + +RESLIB_NXDOM_NXRR NXDOMAIN/NXRRSET received in response to query for <%1> A debug message recording that either a NXDOMAIN or an NXRRSET response has been received to an upstream query for the specified question. Previous debug @@ -1514,8 +2208,8 @@ are no retries left, an error will be reported. - -RESLIB_PROTOCOLRTRY protocol error in answer for %1: %2 (retries left: %3) + +RESLIB_PROTOCOL_RETRY protocol error in answer for %1: %2 (retries left: %3) A debug message indicating that a protocol error was received and that the resolver is repeating the query to the same nameserver. After this @@ -1523,14 +2217,35 @@ repeated query, there will be the indicated number of retries left. - -RESLIB_RCODERR RCODE indicates error in response to query for <%1> + +RESLIB_RCODE_ERR RCODE indicates error in response to query for <%1> A debug message, the response to the specified query indicated an error that is not covered by a specific code path. A SERVFAIL will be returned. + +RESLIB_RECQ_CACHE_FIND found <%1> in the cache (resolve() instance %2) + +This is a debug message and indicates that a RecursiveQuery object found the +the specified <name, class, type> tuple in the cache. The instance number +at the end of the message indicates which of the two resolve() methods has +been called. + + + + +RESLIB_RECQ_CACHE_NO_FIND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2) + +This is a debug message and indicates that the look in the cache made by the +RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery +object has been created to resolve the question. The instance number at +the end of the message indicates which of the two resolve() methods has +been called. + + + RESLIB_REFERRAL referral received in response to query for <%1> @@ -1540,35 +2255,14 @@ have indicated the server to which the question was sent. - -RESLIB_REFERZONE referred to zone %1 + +RESLIB_REFER_ZONE referred to zone %1 A debug message indicating that the last referral message was to the specified zone. - -RESLIB_RESCAFND found <%1> in the cache (resolve() instance %2) - -This is a debug message and indicates that a RecursiveQuery object found the -the specified <name, class, type> tuple in the cache. The instance number -at the end of the message indicates which of the two resolve() methods has -been called. - - - - -RESLIB_RESCANOTFND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2) - -This is a debug message and indicates that the look in the cache made by the -RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery -object has been created to resolve the question. The instance number at -the end of the message indicates which of the two resolve() methods has -been called. - - - RESLIB_RESOLVE asked to resolve <%1> (resolve() instance %2) @@ -1579,8 +2273,8 @@ message indicates which of the two resolve() methods has been called. - -RESLIB_RRSETFND found single RRset in the cache when querying for <%1> (resolve() instance %2) + +RESLIB_RRSET_FOUND found single RRset in the cache when querying for <%1> (resolve() instance %2) A debug message, indicating that when RecursiveQuery::resolve queried the cache, a single RRset was found which was put in the answer. The instance @@ -1596,16 +2290,16 @@ A debug message giving the round-trip time of the last query and response. - -RESLIB_RUNCAFND found <%1> in the cache + +RESLIB_RUNQ_CACHE_FIND found <%1> in the cache This is a debug message and indicates that a RunningQuery object found the specified <name, class, type> tuple in the cache. - -RESLIB_RUNCALOOK looking up up <%1> in the cache + +RESLIB_RUNQ_CACHE_LOOKUP looking up up <%1> in the cache This is a debug message and indicates that a RunningQuery object has made a call to its doLookup() method to look up the specified <name, class, type> @@ -1613,16 +2307,16 @@ tuple, the first action of which will be to examine the cache. - -RESLIB_RUNQUFAIL failure callback - nameservers are unreachable + +RESLIB_RUNQ_FAIL failure callback - nameservers are unreachable A debug message indicating that a RunningQuery's failure callback has been called because all nameservers for the zone in question are unreachable. - -RESLIB_RUNQUSUCC success callback - sending query to %1 + +RESLIB_RUNQ_SUCCESS success callback - sending query to %1 A debug message indicating that a RunningQuery's success callback has been called because a nameserver has been found, and that a query is being sent @@ -1630,8 +2324,8 @@ to the specified nameserver. - -RESLIB_TESTSERV setting test server to %1(%2) + +RESLIB_TEST_SERVER setting test server to %1(%2) This is an internal debugging message and is only generated in unit tests. It indicates that all upstream queries from the resolver are being routed to @@ -1641,8 +2335,8 @@ operation, it is a warning message instead of a debug message. - -RESLIB_TESTUPSTR sending upstream query for <%1> to test server at %2 + +RESLIB_TEST_UPSTREAM sending upstream query for <%1> to test server at %2 This is a debug message and should only be seen in unit tests. A query for the specified <name, class, type> tuple is being sent to a test nameserver @@ -1658,8 +2352,8 @@ there are no retries left, an error will be reported. - -RESLIB_TIMEOUTRTRY query <%1> to %2 timed out, re-trying (retries left: %3) + +RESLIB_TIMEOUT_RETRY query <%1> to %2 timed out, re-trying (retries left: %3) A debug message indicating that the specified query has timed out and that the resolver is repeating the query to the same nameserver. After this @@ -1685,8 +2379,8 @@ tuple is being sent to a nameserver whose address is given in the message. - -RESOLVER_AXFRTCP AXFR request received over TCP + +RESOLVER_AXFR_TCP AXFR request received over TCP A debug message, the resolver received a NOTIFY message over TCP. The server cannot process it and will return an error message to the sender with the @@ -1694,8 +2388,8 @@ RCODE set to NOTIMP. - -RESOLVER_AXFRUDP AXFR request received over UDP + +RESOLVER_AXFR_UDP AXFR request received over UDP A debug message, the resolver received a NOTIFY message over UDP. The server cannot process it (and in any case, an AXFR request should be sent over TCP) @@ -1703,24 +2397,24 @@ and will return an error message to the sender with the RCODE set to FORMERR. - -RESOLVER_CLTMOSMALL client timeout of %1 is too small + +RESOLVER_CLIENT_TIME_SMALL client timeout of %1 is too small An error indicating that the configuration value specified for the query timeout is too small. - -RESOLVER_CONFIGCHAN configuration channel created + +RESOLVER_CONFIG_CHANNEL configuration channel created A debug message, output when the resolver has successfully established a connection to the configuration channel. - -RESOLVER_CONFIGERR error in configuration: %1 + +RESOLVER_CONFIG_ERROR error in configuration: %1 An error was detected in a configuration update received by the resolver. This may be in the format of the configuration message (in which case this is a @@ -1730,16 +2424,16 @@ will give more details. - -RESOLVER_CONFIGLOAD configuration loaded + +RESOLVER_CONFIG_LOADED configuration loaded A debug message, output when the resolver configuration has been successfully loaded. - -RESOLVER_CONFIGUPD configuration updated: %1 + +RESOLVER_CONFIG_UPDATED configuration updated: %1 A debug message, the configuration has been updated with the specified information. @@ -1753,16 +2447,16 @@ A debug message, output when the Resolver() object has been created. - -RESOLVER_DNSMSGRCVD DNS message received: %1 + +RESOLVER_DNS_MESSAGE_RECEIVED DNS message received: %1 A debug message, this always precedes some other logging message and is the formatted contents of the DNS packet that the other message refers to. - -RESOLVER_DNSMSGSENT DNS message of %1 bytes sent: %2 + +RESOLVER_DNS_MESSAGE_SENT DNS message of %1 bytes sent: %2 A debug message, this contains details of the response sent back to the querying system. @@ -1777,24 +2471,24 @@ resolver. All it can do is to shut down. - -RESOLVER_FWDADDR setting forward address %1(%2) + +RESOLVER_FORWARD_ADDRESS setting forward address %1(%2) This message may appear multiple times during startup, and it lists the forward addresses used by the resolver when running in forwarding mode. - -RESOLVER_FWDQUERY processing forward query + +RESOLVER_FORWARD_QUERY processing forward query The received query has passed all checks and is being forwarded to upstream servers. - -RESOLVER_HDRERR message received, exception when processing header: %1 + +RESOLVER_HEADER_ERROR message received, exception when processing header: %1 A debug message noting that an exception occurred during the processing of a received packet. The packet has been dropped. @@ -1809,40 +2503,34 @@ and will return an error message to the sender with the RCODE set to NOTIMP. - -RESOLVER_LKTMOSMALL lookup timeout of %1 is too small + +RESOLVER_LOOKUP_TIME_SMALL lookup timeout of %1 is too small An error indicating that the configuration value specified for the lookup timeout is too small. - -RESOLVER_NFYNOTAUTH NOTIFY arrived but server is not authoritative + +RESOLVER_MESSAGE_ERROR error parsing received message: %1 - returning %2 -The resolver received a NOTIFY message. As the server is not authoritative it -cannot process it, so it returns an error message to the sender with the RCODE -set to NOTAUTH. +A debug message noting that the resolver received a message and the +parsing of the body of the message failed due to some error (although +the parsing of the header succeeded). The message parameters give a +textual description of the problem and the RCODE returned. - -RESOLVER_NORMQUERY processing normal query + +RESOLVER_NEGATIVE_RETRIES negative number of retries (%1) specified in the configuration -The received query has passed all checks and is being processed by the resolver. +An error message indicating that the resolver configuration has specified a +negative retry count. Only zero or positive values are valid. - -RESOLVER_NOROOTADDR no root addresses available - -A warning message during startup, indicates that no root addresses have been -set. This may be because the resolver will get them from a priming query. - - - - -RESOLVER_NOTIN non-IN class request received, returning REFUSED message + +RESOLVER_NON_IN_PACKET non-IN class request received, returning REFUSED message A debug message, the resolver has received a DNS packet that was not IN class. The resolver cannot handle such packets, so is returning a REFUSED response to @@ -1850,8 +2538,24 @@ the sender. - -RESOLVER_NOTONEQUES query contained %1 questions, exactly one question was expected + +RESOLVER_NORMAL_QUERY processing normal query + +The received query has passed all checks and is being processed by the resolver. + + + + +RESOLVER_NOTIFY_RECEIVED NOTIFY arrived but server is not authoritative + +The resolver received a NOTIFY message. As the server is not authoritative it +cannot process it, so it returns an error message to the sender with the RCODE +set to NOTAUTH. + + + + +RESOLVER_NOT_ONE_QUESTION query contained %1 questions, exactly one question was expected A debug message, the resolver received a query that contained the number of entires in the question section detailed in the message. This is a malformed @@ -1860,17 +2564,16 @@ return a message to the sender with the RCODE set to FORMERR. - -RESOLVER_OPCODEUNS opcode %1 not supported by the resolver + +RESOLVER_NO_ROOT_ADDRESS no root addresses available -A debug message, the resolver received a message with an unsupported opcode -(it can only process QUERY opcodes). It will return a message to the sender -with the RCODE set to NOTIMP. +A warning message during startup, indicates that no root addresses have been +set. This may be because the resolver will get them from a priming query. - -RESOLVER_PARSEERR error parsing received message: %1 - returning %2 + +RESOLVER_PARSE_ERROR error parsing received message: %1 - returning %2 A debug message noting that the resolver received a message and the parsing of the body of the message failed due to some non-protocol related reason @@ -1879,16 +2582,16 @@ a textual description of the problem and the RCODE returned. - -RESOLVER_PRINTMSG print message command, aeguments are: %1 + +RESOLVER_PRINT_COMMAND print message command, arguments are: %1 This message is logged when a "print_message" command is received over the command channel. - -RESOLVER_PROTERR protocol error parsing received message: %1 - returning %2 + +RESOLVER_PROTOCOL_ERROR protocol error parsing received message: %1 - returning %2 A debug message noting that the resolver received a message and the parsing of the body of the message failed due to some protocol error (although the @@ -1897,28 +2600,70 @@ description of the problem and the RCODE returned. - -RESOLVER_QUSETUP query setup + +RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4 + +A debug message that indicates an incoming query is accepted in terms of +the query ACL. The log message shows the query in the form of +<query name>/<query type>/<query class>, and the client that sends the +query in the form of <Source IP address>#<source port>. + + + + +RESOLVER_QUERY_DROPPED query dropped: '%1/%2/%3' from %4 + +An informational message that indicates an incoming query is dropped +in terms of the query ACL. Unlike the RESOLVER_QUERY_REJECTED +case, the server does not return any response. The log message +shows the query in the form of <query name>/<query type>/<query +class>, and the client that sends the query in the form of <Source +IP address>#<source port>. + + + + +RESOLVER_QUERY_REJECTED query rejected: '%1/%2/%3' from %4 + +An informational message that indicates an incoming query is rejected +in terms of the query ACL. This results in a response with an RCODE of +REFUSED. The log message shows the query in the form of <query +name>/<query type>/<query class>, and the client that sends the +query in the form of <Source IP address>#<source port>. + + + + +RESOLVER_QUERY_SETUP query setup A debug message noting that the resolver is creating a RecursiveQuery object. - -RESOLVER_QUSHUT query shutdown + +RESOLVER_QUERY_SHUTDOWN query shutdown A debug message noting that the resolver is destroying a RecursiveQuery object. - -RESOLVER_QUTMOSMALL query timeout of %1 is too small + +RESOLVER_QUERY_TIME_SMALL query timeout of %1 is too small An error indicating that the configuration value specified for the query timeout is too small. + +RESOLVER_RECEIVED_MESSAGE resolver has received a DNS message + +A debug message indicating that the resolver has received a message. Depending +on the debug settings, subsequent log output will indicate the nature of the +message. + + + RESOLVER_RECURSIVE running in recursive mode @@ -1927,43 +2672,18 @@ resolver is running in recursive mode. - -RESOLVER_RECVMSG resolver has received a DNS message - -A debug message indicating that the resolver has received a message. Depending -on the debug settings, subsequent log output will indicate the nature of the -message. - - - - -RESOLVER_RETRYNEG negative number of retries (%1) specified in the configuration - -An error message indicating that the resolver configuration has specified a -negative retry count. Only zero or positive values are valid. - - - - -RESOLVER_ROOTADDR setting root address %1(%2) - -This message may appear multiple times during startup; it lists the root -addresses used by the resolver. - - - - -RESOLVER_SERVICE service object created + +RESOLVER_SERVICE_CREATED service object created A debug message, output when the main service object (which handles the received queries) is created. - -RESOLVER_SETPARAM query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4 + +RESOLVER_SET_PARAMS query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4 -A debug message, lists the parameters associated with the message. These are: +A debug message, lists the parameters being set for the resolver. These are: query timeout: the timeout (in ms) used for queries originated by the resolver to upstream servers. Client timeout: the interval to resolver a query by a client: after this time, the resolver sends back a SERVFAIL to the client @@ -1972,17 +2692,33 @@ resolver gives up trying to resolve a query. Retry count: the number of times the resolver will retry a query to an upstream server if it gets a timeout. The client and lookup timeouts require a bit more explanation. The -resolution of the clent query might require a large number of queries to +resolution of the client query might require a large number of queries to upstream nameservers. Even if none of these queries timeout, the total time taken to perform all the queries may exceed the client timeout. When this happens, a SERVFAIL is returned to the client, but the resolver continues with the resolution process. Data received is added to the cache. However, -there comes a time - the lookup timeout - when even the resolve gives up. +there comes a time - the lookup timeout - when even the resolver gives up. At this point it will wait for pending upstream queries to complete or timeout and drop the query. + +RESOLVER_SET_QUERY_ACL query ACL is configured + +A debug message that appears when a new query ACL is configured for the +resolver. + + + + +RESOLVER_SET_ROOT_ADDRESS setting root address %1(%2) + +This message may appear multiple times during startup; it lists the root +addresses used by the resolver. + + + RESOLVER_SHUTDOWN resolver shutdown complete @@ -2005,12 +2741,385 @@ An informational message, this is output when the resolver starts up. - -RESOLVER_UNEXRESP received unexpected response, ignoring + +RESOLVER_UNEXPECTED_RESPONSE received unexpected response, ignoring A debug message noting that the server has received a response instead of a query and is ignoring it. + + + +RESOLVER_UNSUPPORTED_OPCODE opcode %1 not supported by the resolver + +A debug message, the resolver received a message with an unsupported opcode +(it can only process QUERY opcodes). It will return a message to the sender +with the RCODE set to NOTIMP. + + + + +XFRIN_AXFR_DATABASE_FAILURE AXFR transfer of zone %1 failed: %2 + +The AXFR transfer for the given zone has failed due to a database problem. +The error is shown in the log message. + + + + +XFRIN_AXFR_INTERNAL_FAILURE AXFR transfer of zone %1 failed: %2 + +The AXFR transfer for the given zone has failed due to an internal +problem in the bind10 python wrapper library. +The error is shown in the log message. + + + + +XFRIN_AXFR_TRANSFER_FAILURE AXFR transfer of zone %1 failed: %2 + +The AXFR transfer for the given zone has failed due to a protocol error. +The error is shown in the log message. + + + + +XFRIN_AXFR_TRANSFER_STARTED AXFR transfer of zone %1 started + +A connection to the master server has been made, the serial value in +the SOA record has been checked, and a zone transfer has been started. + + + + +XFRIN_AXFR_TRANSFER_SUCCESS AXFR transfer of zone %1 succeeded + +The AXFR transfer of the given zone was successfully completed. + + + + +XFRIN_BAD_MASTER_ADDR_FORMAT bad format for master address: %1 + +The given master address is not a valid IP address. + + + + +XFRIN_BAD_MASTER_PORT_FORMAT bad format for master port: %1 + +The master port as read from the configuration is not a valid port number. + + + + +XFRIN_BAD_TSIG_KEY_STRING bad TSIG key string: %1 + +The TSIG key string as read from the configuration does not represent +a valid TSIG key. + + + + +XFRIN_BAD_ZONE_CLASS Invalid zone class: %1 + +The zone class as read from the configuration is not a valid DNS class. + + + + +XFRIN_CC_SESSION_ERROR error reading from cc channel: %1 + +There was a problem reading from the command and control channel. The +most likely cause is that xfrin the msgq daemon is not running. + + + + +XFRIN_COMMAND_ERROR error while executing command '%1': %2 + +There was an error while the given command was being processed. The +error is given in the log message. + + + + +XFRIN_CONNECT_MASTER error connecting to master at %1: %2 + +There was an error opening a connection to the master. The error is +shown in the log message. + + + + +XFRIN_IMPORT_DNS error importing python DNS module: %1 + +There was an error importing the python DNS module pydnspp. The most +likely cause is a PYTHONPATH problem. + + + + +XFRIN_MSGQ_SEND_ERROR error while contacting %1 and %2 + +There was a problem sending a message to the xfrout module or the +zone manager. This most likely means that the msgq daemon has quit or +was killed. + + + + +XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER error while contacting %1 + +There was a problem sending a message to the zone manager. This most +likely means that the msgq daemon has quit or was killed. + + + + +XFRIN_RETRANSFER_UNKNOWN_ZONE got notification to retransfer unknown zone %1 + +There was an internal command to retransfer the given zone, but the +zone is not known to the system. This may indicate that the configuration +for xfrin is incomplete, or there was a typographical error in the +zone name in the configuration. + + + + +XFRIN_STARTING starting resolver with command line '%1' + +An informational message, this is output when the resolver starts up. + + + + +XFRIN_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down + +There was a keyboard interrupt signal to stop the xfrin daemon. The +daemon will now shut down. + + + + +XFRIN_UNKNOWN_ERROR unknown error: %1 + +An uncaught exception was raised while running the xfrin daemon. The +exception message is printed in the log message. + + + + +XFROUT_AXFR_TRANSFER_DONE transfer of %1/%2 complete + +The transfer of the given zone has been completed successfully, or was +aborted due to a shutdown event. + + + + +XFROUT_AXFR_TRANSFER_ERROR error transferring zone %1/%2: %3 + +An uncaught exception was encountered while sending the response to +an AXFR query. The error message of the exception is included in the +log message, but this error most likely points to incomplete exception +handling in the code. + + + + +XFROUT_AXFR_TRANSFER_FAILED transfer of %1/%2 failed, rcode: %3 + +A transfer out for the given zone failed. An error response is sent +to the client. The given rcode is the rcode that is set in the error +response. This is either NOTAUTH (we are not authoritative for the +zone), SERVFAIL (our internal database is missing the SOA record for +the zone), or REFUSED (the limit of simultaneous outgoing AXFR +transfers, as specified by the configuration value +Xfrout/max_transfers_out, has been reached). + + + + +XFROUT_AXFR_TRANSFER_STARTED transfer of zone %1/%2 has started + +A transfer out of the given zone has started. + + + + +XFROUT_BAD_TSIG_KEY_STRING bad TSIG key string: %1 + +The TSIG key string as read from the configuration does not represent +a valid TSIG key. + + + + +XFROUT_CC_SESSION_ERROR error reading from cc channel: %1 + +There was a problem reading from the command and control channel. The +most likely cause is that the msgq daemon is not running. + + + + +XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response + +There was a problem reading a response from antoher module over the +command and control channel. The most likely cause is that the +configuration manager b10-cfgmgr is not running. + + + + +XFROUT_FETCH_REQUEST_ERROR socket error while fetching a request from the auth daemon + +There was a socket error while contacting the b10-auth daemon to +fetch a transfer request. The auth daemon may have shutdown. + + + + +XFROUT_HANDLE_QUERY_ERROR error while handling query: %1 + +There was a general error handling an xfrout query. The error is shown +in the message. In principle this error should not appear, and points +to an oversight catching exceptions in the right place. However, to +ensure the daemon keeps running, this error is caught and reported. + + + + +XFROUT_IMPORT error importing python module: %1 + +There was an error importing a python module. One of the modules needed +by xfrout could not be found. This suggests that either some libraries +are missing on the system, or the PYTHONPATH variable is not correct. +The specific place where this library needs to be depends on your +system and your specific installation. + + + + +XFROUT_NEW_CONFIG Update xfrout configuration + +New configuration settings have been sent from the configuration +manager. The xfrout daemon will now apply them. + + + + +XFROUT_NEW_CONFIG_DONE Update xfrout configuration done + +The xfrout daemon is now done reading the new configuration settings +received from the configuration manager. + + + + +XFROUT_NOTIFY_COMMAND received command to send notifies for %1/%2 + +The xfrout daemon received a command on the command channel that +NOTIFY packets should be sent for the given zone. + + + + +XFROUT_PARSE_QUERY_ERROR error parsing query: %1 + +There was a parse error while reading an incoming query. The parse +error is shown in the log message. A remote client sent a packet we +do not understand or support. The xfrout request will be ignored. +In general, this should only occur for unexpected problems like +memory allocation failures, as the query should already have been +parsed by the b10-auth daemon, before it was passed here. + + + + +XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %2 + +There was an error processing a transfer request. The error is included +in the log message, but at this point no specific information other +than that could be given. This points to incomplete exception handling +in the code. + + + + +XFROUT_RECEIVED_SHUTDOWN_COMMAND shutdown command received + +The xfrout daemon received a shutdown command from the command channel +and will now shut down. + + + + +XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection + +There was an error receiving the file descriptor for the transfer +request. Normally, the request is received by b10-auth, and passed on +to the xfrout daemon, so it can answer directly. However, there was a +problem receiving this file descriptor. The request will be ignored. + + + + +XFROUT_REMOVE_OLD_UNIX_SOCKET_FILE_ERROR error removing unix socket file %1: %2 + +The unix socket file xfrout needs for contact with the auth daemon +already exists, and needs to be removed first, but there is a problem +removing it. It is likely that we do not have permission to remove +this file. The specific error is show in the log message. The xfrout +daemon will shut down. + + + + +XFROUT_REMOVE_UNIX_SOCKET_FILE_ERROR error clearing unix socket file %1: %2 + +When shutting down, the xfrout daemon tried to clear the unix socket +file used for communication with the auth daemon. It failed to remove +the file. The reason for the failure is given in the error message. + + + + +XFROUT_SOCKET_SELECT_ERROR error while calling select() on request socket: %1 + +There was an error while calling select() on the socket that informs +the xfrout daemon that a new xfrout request has arrived. This should +be a result of rare local error such as memory allocation failure and +shouldn't happen under normal conditions. The error is included in the +log message. + + + + +XFROUT_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down + +There was a keyboard interrupt signal to stop the xfrout daemon. The +daemon will now shut down. + + + + +XFROUT_STOPPING the xfrout daemon is shutting down + +The current transfer is aborted, as the xfrout daemon is shutting down. + + + + +XFROUT_UNIX_SOCKET_FILE_IN_USE another xfrout process seems to be using the unix socket file %1 + +While starting up, the xfrout daemon tried to clear the unix domain +socket needed for contacting the b10-auth daemon to pass requests +on, but the file is in use. The most likely cause is that another +xfrout daemon process is still running. This xfrout daemon (the one +printing this message) will not start. + From 05164f9d61006869233b498d248486b4307ea8b6 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Tue, 5 Jul 2011 10:35:04 +0100 Subject: [PATCH 090/974] [trac1071] Add liblog to list of libraries to link against --- src/lib/server_common/tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/server_common/tests/Makefile.am b/src/lib/server_common/tests/Makefile.am index 3c061c27c5..5bc6586b50 100644 --- a/src/lib/server_common/tests/Makefile.am +++ b/src/lib/server_common/tests/Makefile.am @@ -38,6 +38,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la +run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la From 88c4d5e241d0dd670eca6f9a4981439a08924704 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Tue, 5 Jul 2011 10:56:53 +0100 Subject: [PATCH 091/974] [trac1071] ChangeLog entry for #1071 --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 90db8148b1..7c2dcf8247 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +267. [func] stephen + Add environment variable to allow redirection of logging output during + unit tests. + (Trac #1071, git 05164f9d61006869233b498d248486b4307ea8b6) + 266. [func] Multiple developers Convert various error messages, debugging and other output to the new logging interface, including for b10-resolver, From 0042b37bdc4e3929faf3d2b7862dd79979d60aa0 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 5 Jul 2011 15:11:21 +0200 Subject: [PATCH 092/974] [trac741] remove newlines + one typo --- src/lib/cache/cache_messages.mes | 35 +------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 31109eaeaa..75dcfb5794 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -15,166 +15,133 @@ $NAMESPACE isc::cache % CACHE_ENTRY_MISSING_RRSET missing RRset to generate message for %1 - The cache tried to generate the complete answer message. It knows the structure of the message, but some of the RRsets to be put there are not in cache (they probably expired already). Therefore it pretends the message was not found. % CACHE_LOCALZONE_FOUND found entry with key %1 in local zone data - Debug message, noting that the requested data was successfully found in the local zone data of the cache. % CACHE_LOCALZONE_UNKNOWN entry with key %1 not found in local zone data - Debug message. The requested data was not found in the local zone data. % CACHE_LOCALZONE_UPDATE updating local zone element at key %1 - Debug message issued when there's update to the local zone section of cache. % CACHE_MESSAGES_DEINIT deinitialized message cache - Debug message. It is issued when the server deinitializes the message cache. % CACHE_MESSAGES_EXPIRED found an expired message entry for %1 in the message cache - Debug message. The requested data was found in the message cache, but it already expired. Therefore the cache removes the entry and pretends it found nothing. % CACHE_MESSAGES_FOUND found a message entry for %1 in the message cache - Debug message. We found the whole message in the cache, so it can be returned to user without any other lookups. % CACHE_MESSAGES_INIT initialized message cache for %1 %2 messages - Debug message issued when a new message cache is issued. It lists the class of messages it can hold and the maximum size of the cache. % CACHE_MESSAGES_REMOVE removing old instance of %1/%2/%3 first - Debug message. This may follow CACHE_MESSAGES_UPDATE and indicates that, while updating, the old instance is being removed prior of inserting a new one. % CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3 - Debug message, noting that the given message can not be cached. This is for some reason described in RFC2308. % CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache - Debug message. The message cache didn't find any entry for the given key. % CACHE_MESSAGES_UPDATE updating message entry %1/%2/%3 - Debug message issued when the message cache is being updated with a new message. Either the old instance is removed or, if none is found, new one is created. % CACHE_RESOLVER_DEEPEST looking up deepest NS for %1/%2 - Debug message. The resolver cache is looking up the deepest known nameserver, so the resolution doesn't have to start from the root. % CACHE_RESOLVER_INIT_INFO initializing resolver cache for class %1 - Debug message, the resolver cache is being created for this given class. The difference from CACHE_RESOLVER_INIT is only in different format of passed information, otherwise it does the same. % CACHE_RESOLVER_INIT initializing resolver cache for class %1 - Debug message. The resolver cache is being created for this given class. % CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data - Debug message. The resolver cache found a complete message for the user query in the zone data. % CACHE_RESOLVER_LOCAL_RRSET RRset for %1/%2 found in local zone data - Debug message. The resolver cache found a requested RRset in the local zone data. % CACHE_RESOLVER_LOOKUP_MSG looking up message in resolver cache for %1/%2 - Debug message. The resolver cache is trying to find a message to answer the user query. % CACHE_RESOLVER_LOOKUP_RRSET looking up RRset in resolver cache for %1/%2 - Debug message. The resolver cache is trying to find an RRset (which usually originates as internally from resolver). % CACHE_RESOLVER_NO_QUESTION answer message for %1/%2 has empty question section - The cache tried to fill in found data into the response message. But it discovered the message contains no question section, which is invalid. This is likely a programmer error, please submit a bug report. % CACHE_RESOLVER_UNKNOWN_CLASS_MSG no cache for class %1 - Debug message. While trying to lookup a message in the resolver cache, it was discovered there's no cache for this class at all. Therefore no message is found. % CACHE_RESOLVER_UNKNOWN_CLASS_RRSET no cache for class %1 - Debug message. While trying to lookup an RRset in the resolver cache, it was discovered there's no cache for this class at all. Therefore no data is found. % CACHE_RESOLVER_UPDATE_MSG updating message for %1/%2/%3 - Debug message. The resolver is updating a message in the cache. % CACHE_RESOLVER_UPDATE_RRSET updating RRset for %1/%2/%3 - Debug message. The resolver is updating an RRset in the cache. % CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG no cache for class %1 - Debug message. While trying to insert a message into the cache, it was discovered that there's no cache for the class of message. Therefore the message will not be cached. % CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET no cache for class %1 - Debug message. While trying to insert an RRset into the cache, it was discovered that there's no cache for the class of the RRset. Therefore the message will not be cached. % CACHE_RRSET_EXPIRED found expired RRset %1/%2 - Debug message. There' the data requested in the RRset cache. However, it is expired, so the cache removed it and is going to pretend nothing was found. % CACHE_RRSET_INIT initializing RRset cache for %2 RRsets of class %1 - Debug message. The RRset cache to hold at most this many RRsets for the given class is being created. % CACHE_RRSET_LOOKUP looking up %1/%2 in RRset cache - Debug message. The resolver is trying to look up data in the RRset cache. % CACHE_RRSET_NOT_FOUND no RRset found for %1/%2 - Debug message which can follow CACHE_RRSET_LOOKUP. This means the data is not in the cache. % CACHE_RRSET_REMOVE_OLD removing old RRset for %1/%2/%3 to make space for new one - Debug message which can follow CACHE_RRSET_UPDATE. During the update, the cache removed an old instance of the RRset to replace it with the new one. % CACHE_RRSET_UNTRUSTED not replacing old RRset for %1/%2/%3, it has higher trust level - Debug message which can follow CACHE_RRSET_UPDATE. The cache already holds the same RRset, but from more trusted source, so the old one is kept and new one ignored. % CACHE_RRSET_UPDATE updating RRset %1/%2/%3 in the cache - -Debug message. The RRset is updating its data with this giver RRset. +Debug message. The RRset is updating its data with this given RRset. From 525d9602da83a5d8ddbfc9ebda282209aa743a70 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 5 Jul 2011 10:58:11 -0500 Subject: [PATCH 093/974] [jreed-docs-2] add some TODO comments some docs need to be written --- src/bin/xfrin/b10-xfrin.xml | 1 + src/bin/xfrout/b10-xfrout.xml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml index ea4c724d23..71fcf931ca 100644 --- a/src/bin/xfrin/b10-xfrin.xml +++ b/src/bin/xfrin/b10-xfrin.xml @@ -103,6 +103,7 @@ in separate zonemgr process. b10-xfrin daemon. The list items are: name (the zone name), + master_addr (the zone master to transfer from), master_port (defaults to 53), and tsig_key (optional TSIG key to use). diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml index ad71fe2bf7..9889b8058e 100644 --- a/src/bin/xfrout/b10-xfrout.xml +++ b/src/bin/xfrout/b10-xfrout.xml @@ -134,6 +134,14 @@ data storage types. + + + The configuration commands are: From f9045d39a58a9b9287f3ece1022391a3b07e88d3 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 5 Jul 2011 11:04:35 -0500 Subject: [PATCH 094/974] [master] added a missing changelog entry I renumbered a new entry because of this. --- ChangeLog | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 7c2dcf8247..0aee22a67f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,15 @@ -267. [func] stephen +268. [func] stephen Add environment variable to allow redirection of logging output during unit tests. (Trac #1071, git 05164f9d61006869233b498d248486b4307ea8b6) +267. [func] tomek + Added a dummy module for DHCP6. This module does not actually + do anything at this point, and BIND 10 has no option for + starting it yet. It is included as a base for further + development. + (Trac #990, git 4a590df96a1b1d373e87f1f56edaceccb95f267d) + 266. [func] Multiple developers Convert various error messages, debugging and other output to the new logging interface, including for b10-resolver, From baf3d8783ad1bc05bbe4db507325e9bfcd8d9be9 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 5 Jul 2011 11:13:04 -0500 Subject: [PATCH 095/974] [master] hack to check for previous isc/log directory This is from patch ticket #1053 from jinmei. http://bind10.isc.org/attachment/ticket/1053/makefile.diff It is a workaround to problem with isc/log name reuse. --- src/lib/python/isc/log/Makefile.am | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib/python/isc/log/Makefile.am b/src/lib/python/isc/log/Makefile.am index b228caf428..5ff2c28a4e 100644 --- a/src/lib/python/isc/log/Makefile.am +++ b/src/lib/python/isc/log/Makefile.am @@ -23,6 +23,15 @@ log_la_LIBADD += $(PYTHON_LIB) # This is not installed, it helps locate the module during tests EXTRA_DIST = __init__.py +# We're going to abuse install-data-local for a pre-install check. +# This is to be considered a short term hack and is expected to be removed +# in a near future version. +install-data-local: + if test -d @pyexecdir@/isc/log; then \ + echo "@pyexecdir@/isc/log is deprecated, and will confuse newer versions. Please (re)move it by hand."; \ + exit 1; \ + fi + pytest: $(SHELL) tests/log_test From 242235d6e7bb4e1893c0ebfc58e7a757dae771f8 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Tue, 5 Jul 2011 18:25:13 +0100 Subject: [PATCH 096/974] [trac1077] Changes to description .cc and .h files regenerated to ensure .cc and .h files are up to date with the .mes file. --- src/lib/log/log_messages.cc | 2 +- src/lib/log/log_messages.h | 2 +- src/lib/log/log_messages.mes | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/log/log_messages.cc b/src/lib/log/log_messages.cc index a515959769..75e3c4f951 100644 --- a/src/lib/log/log_messages.cc +++ b/src/lib/log/log_messages.cc @@ -1,4 +1,4 @@ -// File created from log_messages.mes on Wed Jun 22 11:54:57 2011 +// File created from log_messages.mes on Tue Jul 5 18:12:20 2011 #include #include diff --git a/src/lib/log/log_messages.h b/src/lib/log/log_messages.h index 476f68601b..7d05856230 100644 --- a/src/lib/log/log_messages.h +++ b/src/lib/log/log_messages.h @@ -1,4 +1,4 @@ -// File created from log_messages.mes on Wed Jun 22 11:54:57 2011 +// File created from log_messages.mes on Tue Jul 5 18:12:20 2011 #ifndef __LOG_MESSAGES_H #define __LOG_MESSAGES_H diff --git a/src/lib/log/log_messages.mes b/src/lib/log/log_messages.mes index 697ac924ef..f15c7697e0 100644 --- a/src/lib/log/log_messages.mes +++ b/src/lib/log/log_messages.mes @@ -28,23 +28,23 @@ destination should be one of "console", "file", or "syslog". % LOG_BAD_SEVERITY unrecognized log severity: %1 A logger severity value was given that was not recognized. The severity -should be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL". +should be one of "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". % LOG_BAD_STREAM bad log console output stream: %1 -A log console output stream was given that was not recognized. The output -stream should be one of "stdout", or "stderr" +Logging has been configured so that output is written to the terminal +(console) but the stream on which it is to be written is not recognised. +Allowed values are "stdout" and "stderr" % LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code -During start-up, BIND10 detected that the given message identification had -been defined multiple times in the BIND10 code. - -This has no ill-effects other than the possibility that an erronous -message may be logged. However, as it is indicative of a programming -error, please log a bug report. +During start-up, BIND10 detected that the given message identification +had been defined multiple times in the BIND10 code. This indicates a +programming error; please log a bug report. % LOG_DUPLICATE_NAMESPACE line %1: duplicate $NAMESPACE directive found When reading a message file, more than one $NAMESPACE directive was found. -Such a condition is regarded as an error and the read will be abandoned. +(This directive is used to set a C++ namespace when generating header +files during software development.) Such a condition is regarded as an +error and the read will be abandoned. % LOG_INPUT_OPEN_FAIL unable to open message file %1 for input: %2 The program was not able to open the specified input message file for From 47286f0bb01c6dbe0e48fc080f931d7b93e22063 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 5 Jul 2011 15:31:20 -0700 Subject: [PATCH 097/974] [trac983] make sure libacl is built before python libs as python ACL is going to depend on libacl. --- src/lib/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index f4bef6b45a..5adf150027 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = exceptions util log cryptolink dns cc config python xfr \ +SUBDIRS = exceptions util log cryptolink dns cc config acl python xfr \ bench asiolink asiodns nsas cache resolve testutils datasrc \ - acl server_common + server_common From db143fb8a98a13414f997892449ca2fbb07a0629 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 5 Jul 2011 23:25:05 +0000 Subject: [PATCH 098/974] [master] missing header file to fix build failure for sunstudio okayed on jabber, pushing directly. --- src/lib/acl/tests/sockaddr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/acl/tests/sockaddr.h b/src/lib/acl/tests/sockaddr.h index f004f7ddcb..bd304516ad 100644 --- a/src/lib/acl/tests/sockaddr.h +++ b/src/lib/acl/tests/sockaddr.h @@ -18,6 +18,7 @@ #include #include #include +#include #include From cf5ed0e7c52e8a97ec48525ee2181e31aaa4184a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 5 Jul 2011 17:30:04 -0700 Subject: [PATCH 099/974] [trac983] added a new module function: load_request_acl() --- src/lib/python/isc/acl/Makefile.am | 7 +- src/lib/python/isc/acl/dns.cc | 78 ++++++- .../python/isc/acl/dns_requestacl_python.cc | 221 ++++++++++++++++++ .../python/isc/acl/dns_requestacl_python.h | 48 ++++ src/lib/python/isc/acl/tests/Makefile.am | 2 +- src/lib/python/isc/acl/tests/dns_test.py | 56 ++++- 6 files changed, 401 insertions(+), 11 deletions(-) create mode 100644 src/lib/python/isc/acl/dns_requestacl_python.cc create mode 100644 src/lib/python/isc/acl/dns_requestacl_python.h diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index b6acc53336..04ff614239 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -1,12 +1,11 @@ SUBDIRS = . tests AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib -#Do we need boost? -#AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CXXFLAGS = $(B10_CXXFLAGS) pyexec_LTLIBRARIES = dns.la -dns_la_SOURCES = dns.cc +dns_la_SOURCES = dns.cc dns_requestacl_python.h dns_requestacl_python.cc dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) dns_la_LDFLAGS = $(PYTHON_LDFLAGS) # Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be @@ -16,5 +15,5 @@ acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) # Python prefers .so, while some OSes (specifically MacOS) use a different # suffix for dynamic objects. -module is necessary to work this around. dns_la_LDFLAGS += -module -dns_la_LIBADD = $(top_builddir)/src/lib/acl/libacl.la +dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la dns_la_LIBADD += $(PYTHON_LIB) diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc index d0c0f60da2..b342762f43 100644 --- a/src/lib/python/isc/acl/dns.cc +++ b/src/lib/python/isc/acl/dns.cc @@ -14,17 +14,69 @@ #include +#include +#include + +#include + +#include + +#include +#include + +#include "dns_requestacl_python.h" + +using namespace std; +using boost::shared_ptr; +using namespace isc::util::python; +using namespace isc::data; +using namespace isc::acl::dns; +using namespace isc::acl::dns::python; + namespace { +PyObject* po_dns_LoaderError; + +PyObject* +loadRequestACL(PyObject*, PyObject* args) { + const char* acl_config; + + if (PyArg_ParseTuple(args, "s", &acl_config)) { + try { + shared_ptr acl( + getRequestLoader().load(Element::fromJSON(acl_config))); + s_RequestACL* py_acl = static_cast( + requestacl_type.tp_alloc(&requestacl_type, 0)); + if (py_acl != NULL) { + py_acl->cppobj = acl; + } + return (py_acl); + } catch (const exception& ex) { + PyErr_SetString(po_dns_LoaderError, ex.what()); + return (NULL); + } catch (...) { + PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception"); + return (NULL); + } + } + + return (NULL); +} + +PyMethodDef methods[] = { + { "load_request_acl", loadRequestACL, METH_VARARGS, "TBD" }, + { NULL, NULL, 0, NULL } +}; + PyModuleDef dnsacl = { { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, - "acl", + "isc.acl.dns", "This module provides Python bindings for the C++ classes in the " "isc::acl::dns namespace. Specifically, it defines Python interfaces of " "handling access control lists (ACLs) with DNS related contexts.\n\n" "These bindings are close match to the C++ API, but they are not complete " "(some parts are not needed) and some are done in more python-like ways.", -1, - NULL, + methods, NULL, NULL, NULL, @@ -34,5 +86,25 @@ PyModuleDef dnsacl = { PyMODINIT_FUNC PyInit_dns(void) { - return (PyModule_Create(&dnsacl)); + PyObject* mod = PyModule_Create(&dnsacl); + if (mod == NULL) { + return (NULL); + } + + try { + po_dns_LoaderError = PyErr_NewException("isc.acl.dns.LoaderError", + NULL, NULL); + PyObjectContainer(po_dns_LoaderError).installToModule(mod, + "LoaderError"); + } catch (...) { + Py_DECREF(mod); + return (NULL); + } + + if (!initModulePart_RequestACL(mod)) { + Py_DECREF(mod); + return (NULL); + } + + return (mod); } diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc new file mode 100644 index 0000000000..c55292e1cd --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -0,0 +1,221 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// Enable this if you use s# variants with PyArg_ParseTuple(), see +// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers +//#define PY_SSIZE_T_CLEAN + +// Python.h needs to be placed at the head of the program file, see: +// http://docs.python.org/py3k/extending/extending.html#a-simple-example +#include + +#include +#include + +#include + +#include +#include + +#include "dns_requestacl_python.h" + +using namespace std; +using namespace isc::util::python; +using namespace isc::acl; +using namespace isc::acl::dns; +using namespace isc::acl::dns::python; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// RequestACL +// + +// Trivial constructor. +s_RequestACL::s_RequestACL() {} + +namespace { +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer RequestACLContainer; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +// General creation and destruction +int RequestACL_init(s_RequestACL* self, PyObject* args); +void RequestACL_destroy(s_RequestACL* self); + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef RequestACL_methods[] = { + { NULL, NULL, 0, NULL } +}; + +// This is a template of typical code logic of python class initialization +// with C++ backend. You'll need to adjust it according to details of the +// actual C++ class. +int +RequestACL_init(s_RequestACL* self, PyObject* /*args*/) { + // maybe we should prohibit direct creation of the ACL + try { +#ifdef notyet + if (PyArg_ParseTuple(args, "REPLACE ME")) { + // YOU'LL NEED SOME VALIDATION, PREPARATION, ETC, HERE. + self->cppobj = new RequestACL(/*NECESSARY PARAMS*/); + return (0); + } +#endif + self->cppobj.reset(new RequestACL(REJECT)); + return (0); + } catch (const exception& ex) { + const string ex_what = "Failed to construct RequestACL object: " + + string(ex.what()); + //PyErr_SetString(po_IscException, ex_what.c_str()); + PyErr_SetString(PyExc_TypeError, ex_what.c_str()); + return (-1); + } catch (...) { + PyErr_SetString(/*po_IscException*/PyExc_TypeError, + "Unexpected exception in constructing RequestACL"); + return (-1); + } + + PyErr_SetString(PyExc_TypeError, + "Invalid arguments to RequestACL constructor"); + + return (-1); +} + +// This is a template of typical code logic of python object destructor. +// In many cases you can use it without modification, but check that carefully. +void +RequestACL_destroy(s_RequestACL* const self) { + self->cppobj.reset(); + Py_TYPE(self)->tp_free(self); +} +} // end of unnamed namespace + +namespace isc { +namespace acl { +namespace dns { +namespace python { +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_RequestACL +// Most of the functions are not actually implemented and NULL here. +PyTypeObject requestacl_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.RequestACL", + sizeof(s_RequestACL), // tp_basicsize + 0, // tp_itemsize + reinterpret_cast(RequestACL_destroy), // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + NULL, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The RequestACL class objects is...(COMPLETE THIS)", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + RequestACL_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + reinterpret_cast(RequestACL_init), // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +// Module Initialization, all statics are initialized here +bool +initModulePart_RequestACL(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&requestacl_type) < 0) { + return (false); + } + void* p = &requestacl_type; + if (PyModule_AddObject(mod, "RequestACL", static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&requestacl_type); + +#if 0 // we probably don't have any class vars + // The following template is the typical procedure for installing class + // variables. If the class doesn't have a class variable, remove the + // entire try-catch clauses. + try { + // Constant class variables + installClassVariable(requestacl_type, "REPLACE_ME", + Py_BuildValue("REPLACE ME")); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure in RequestACL initialization: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (false); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure in RequestACL initialization"); + return (false); + } +#endif + + return (true); +} +} // namespace python +} // namespace dns +} // namespace acl +} // namespace isc diff --git a/src/lib/python/isc/acl/dns_requestacl_python.h b/src/lib/python/isc/acl/dns_requestacl_python.h new file mode 100644 index 0000000000..e8ad0d3b87 --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestacl_python.h @@ -0,0 +1,48 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_REQUESTACL_H +#define __PYTHON_REQUESTACL_H 1 + +#include + +#include + +#include + +namespace isc { +namespace acl { +namespace dns { +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_RequestACL : public PyObject { +public: + s_RequestACL(); + boost::shared_ptr cppobj; +}; + +extern PyTypeObject requestacl_type; + +bool initModulePart_RequestACL(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace acl +} // namespace isc +#endif // __PYTHON_REQUESTACL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am index 1b9e9dfe09..e185e5e264 100644 --- a/src/lib/python/isc/acl/tests/Makefile.am +++ b/src/lib/python/isc/acl/tests/Makefile.am @@ -7,7 +7,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index 2954a610d7..ce6635a896 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -16,10 +16,60 @@ import unittest from isc.acl.dns import * -class DNSACLTest(unittest.TestCase): +class RequestACLTest(unittest.TestCase): - def test_placeholder(self): - pass + def test_request_loader(self): + # these shouldn't raise an exception + load_request_acl('[{"action": "DROP"}]') + load_request_acl('[{"action": "DROP", "from": "192.0.2.1"}]') + + # Invalid types + self.assertRaises(TypeError, load_request_acl, 1) + self.assertRaises(TypeError, load_request_acl, []) + + # Incorrect number of arguments + self.assertRaises(TypeError, load_request_acl, + '[{"action": "DROP"}]', 0) + + def test_bad_acl_syntax(self): + # this test is derived from loader_test.cc + self.assertRaises(LoaderError, load_request_acl, '{}'); + self.assertRaises(LoaderError, load_request_acl, '42'); + self.assertRaises(LoaderError, load_request_acl, 'true'); + self.assertRaises(LoaderError, load_request_acl, 'null'); + self.assertRaises(LoaderError, load_request_acl, '"hello"'); + self.assertRaises(LoaderError, load_request_acl, '[42]'); + self.assertRaises(LoaderError, load_request_acl, '["hello"]'); + self.assertRaises(LoaderError, load_request_acl, '[[]]'); + self.assertRaises(LoaderError, load_request_acl, '[true]'); + self.assertRaises(LoaderError, load_request_acl, '[null]'); + self.assertRaises(LoaderError, load_request_acl, '[{}]'); + + def test_bad_acl_ipsyntax(self): + # this test is derived from ip_check_unittest.cc + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "192.0.2.43/-1"}]') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "192.0.2.43//1"') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "192.0.2.43/1/"') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "/192.0.2.43/1"') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "2001:db8::/xxxx"') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "2001:db8::/32/s"') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "1/"') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "/1"') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "192.0.2.0/33"') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "DROP", "from": "::1/129"') + + def test_construct(self): + RequestACL() if __name__ == '__main__': unittest.main() From 7819ba75f5c170afa06a5a27b8c64e13ae094b74 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 5 Jul 2011 17:55:09 -0700 Subject: [PATCH 100/974] [trac983] moved LoaderError to a separate (sub) module --- src/lib/python/isc/acl/Makefile.am | 14 +++++- src/lib/python/isc/acl/__init__.py | 4 ++ src/lib/python/isc/acl/acl.cc | 63 ++++++++++++++++++++++++ src/lib/python/isc/acl/acl.h | 34 +++++++++++++ src/lib/python/isc/acl/acl.py | 33 +++++++++++++ src/lib/python/isc/acl/dns.cc | 15 +----- src/lib/python/isc/acl/tests/dns_test.py | 1 + 7 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 src/lib/python/isc/acl/acl.cc create mode 100644 src/lib/python/isc/acl/acl.h create mode 100644 src/lib/python/isc/acl/acl.py diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index 04ff614239..b8c599690e 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -4,16 +4,26 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CXXFLAGS = $(B10_CXXFLAGS) -pyexec_LTLIBRARIES = dns.la +pyexec_LTLIBRARIES = acl.la dns.la + +acl_la_SOURCES = acl.cc +acl_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) +acl_la_LDFLAGS = $(PYTHON_LDFLAGS) +acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) + dns_la_SOURCES = dns.cc dns_requestacl_python.h dns_requestacl_python.cc dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) dns_la_LDFLAGS = $(PYTHON_LDFLAGS) # Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be # placed after -Wextra defined in AM_CXXFLAGS -acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) +dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) # Python prefers .so, while some OSes (specifically MacOS) use a different # suffix for dynamic objects. -module is necessary to work this around. +acl_la_LDFLAGS += -module +acl_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la +acl_la_LIBADD += $(PYTHON_LIB) + dns_la_LDFLAGS += -module dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la dns_la_LIBADD += $(PYTHON_LIB) diff --git a/src/lib/python/isc/acl/__init__.py b/src/lib/python/isc/acl/__init__.py index 4b61e6a849..e83a101a20 100644 --- a/src/lib/python/isc/acl/__init__.py +++ b/src/lib/python/isc/acl/__init__.py @@ -1,3 +1,7 @@ """ Here are function and classes for manipulating access control lists. """ + +# Other ACL modules highly depends on the main acl sub module, so it's +# explicitly imported here. +import isc.acl.acl diff --git a/src/lib/python/isc/acl/acl.cc b/src/lib/python/isc/acl/acl.cc new file mode 100644 index 0000000000..85bd280418 --- /dev/null +++ b/src/lib/python/isc/acl/acl.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include + +#include "acl.h" + +using namespace isc::util::python; +using namespace isc::acl::python; + +namespace isc { +namespace acl { +namespace python { +PyObject* po_LoaderError; +} +} +} + +namespace { +PyModuleDef acl = { + { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, + "isc.acl", + "This module provides Python bindings for the C++ classes in the " + "isc::acl namespace", + -1, + NULL, + NULL, + NULL, + NULL, + NULL +}; +} // end of unnamed namespace + +PyMODINIT_FUNC +PyInit_acl(void) { + PyObject* mod = PyModule_Create(&acl); + if (mod == NULL) { + return (NULL); + } + + try { + po_LoaderError = PyErr_NewException("isc.acl.LoaderError", NULL, NULL); + PyObjectContainer(po_LoaderError).installToModule(mod, "LoaderError"); + } catch (...) { + Py_DECREF(mod); + return (NULL); + } + + return (mod); +} diff --git a/src/lib/python/isc/acl/acl.h b/src/lib/python/isc/acl/acl.h new file mode 100644 index 0000000000..688925dad2 --- /dev/null +++ b/src/lib/python/isc/acl/acl.h @@ -0,0 +1,34 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_ACL_H +#define __PYTHON_ACL_H 1 + +#include + +namespace isc { +namespace acl { +namespace python { + +extern PyObject* po_LoaderError; + +} // namespace python +} // namespace acl +} // namespace isc + +#endif // __PYTHON_ACL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/acl/acl.py b/src/lib/python/isc/acl/acl.py new file mode 100644 index 0000000000..ac97ca109e --- /dev/null +++ b/src/lib/python/isc/acl/acl.py @@ -0,0 +1,33 @@ +# 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 file is not installed. The log.so is installed into the right place. +# It is only to find it in the .libs directory when we run as a test or +# from the build directory. +# But as nobody gives us the builddir explicitly (and we can't use generation +# from .in file, as it would put us into the builddir and we wouldn't be found) +# we guess from current directory. Any idea for something better? This should +# be enough for the tests, but would it work for B10_FROM_SOURCE as well? +# Should we look there? Or define something in bind10_config? + +import os +import sys + +for base in sys.path[:]: + bindingdir = os.path.join(base, 'isc/acl/.libs') + if os.path.exists(bindingdir): + sys.path.insert(0, bindingdir) + +from acl import * diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc index b342762f43..ebf7b59f61 100644 --- a/src/lib/python/isc/acl/dns.cc +++ b/src/lib/python/isc/acl/dns.cc @@ -24,6 +24,7 @@ #include #include +#include "acl.h" #include "dns_requestacl_python.h" using namespace std; @@ -34,8 +35,6 @@ using namespace isc::acl::dns; using namespace isc::acl::dns::python; namespace { -PyObject* po_dns_LoaderError; - PyObject* loadRequestACL(PyObject*, PyObject* args) { const char* acl_config; @@ -51,7 +50,7 @@ loadRequestACL(PyObject*, PyObject* args) { } return (py_acl); } catch (const exception& ex) { - PyErr_SetString(po_dns_LoaderError, ex.what()); + PyErr_SetString(isc::acl::python::po_LoaderError, ex.what()); return (NULL); } catch (...) { PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception"); @@ -91,16 +90,6 @@ PyInit_dns(void) { return (NULL); } - try { - po_dns_LoaderError = PyErr_NewException("isc.acl.dns.LoaderError", - NULL, NULL); - PyObjectContainer(po_dns_LoaderError).installToModule(mod, - "LoaderError"); - } catch (...) { - Py_DECREF(mod); - return (NULL); - } - if (!initModulePart_RequestACL(mod)) { Py_DECREF(mod); return (NULL); diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index ce6635a896..ab3cb82e54 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -14,6 +14,7 @@ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest +from isc.acl.acl import LoaderError from isc.acl.dns import * class RequestACLTest(unittest.TestCase): From 9336279b31c1a5dd9e50fa37d8178c790c4fdef0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 6 Jul 2011 07:11:09 +0000 Subject: [PATCH 101/974] [master] fixed the build failure on g++ 4.3.2/Debian: it required exact type matching for the shared_ptr template parameters in "?:". Note that this fix doesn't loosen the constness, so it doesn't compromise anything. --- src/bin/resolver/resolver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc index a3a7a69892..fb9621b6dd 100644 --- a/src/bin/resolver/resolver.cc +++ b/src/bin/resolver/resolver.cc @@ -598,7 +598,7 @@ Resolver::updateConfig(ConstElementPtr config) { const ConstElementPtr query_acl_cfg(config->get("query_acl")); const shared_ptr query_acl = query_acl_cfg ? acl::dns::getRequestLoader().load(query_acl_cfg) : - shared_ptr(); + shared_ptr(); bool set_timeouts(false); int qtimeout = impl_->query_timeout_; int ctimeout = impl_->client_timeout_; From d1f68168ca58af66f09e51ced1a35334fb5fb825 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 6 Jul 2011 17:39:54 +0900 Subject: [PATCH 102/974] [master] a missing header file "sockaddr.h" in Makefile okayed on jabber, pushing directly. --- src/lib/acl/tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/acl/tests/Makefile.am b/src/lib/acl/tests/Makefile.am index 03b08bbbc5..ce1aec5dcc 100644 --- a/src/lib/acl/tests/Makefile.am +++ b/src/lib/acl/tests/Makefile.am @@ -20,6 +20,7 @@ run_unittests_SOURCES += loader_test.cc run_unittests_SOURCES += logcheck.h run_unittests_SOURCES += creators.h run_unittests_SOURCES += logic_check_test.cc +run_unittests_SOURCES += sockaddr.h run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) From 846493567722add45db6e7296d570f8ecf99837e Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 6 Jul 2011 07:59:02 -0500 Subject: [PATCH 103/974] [trac1077] typo and punctuation fix --- src/lib/log/log_messages.mes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/log/log_messages.mes b/src/lib/log/log_messages.mes index f15c7697e0..b88d5638f7 100644 --- a/src/lib/log/log_messages.mes +++ b/src/lib/log/log_messages.mes @@ -33,7 +33,7 @@ should be one of "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". % LOG_BAD_STREAM bad log console output stream: %1 Logging has been configured so that output is written to the terminal (console) but the stream on which it is to be written is not recognised. -Allowed values are "stdout" and "stderr" +Allowed values are "stdout" and "stderr". % LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code During start-up, BIND10 detected that the given message identification @@ -123,7 +123,7 @@ a prefix to be added to the symbol names when a C++ file is created. As such, it must adhere to restrictions on C++ symbol names (e.g. may only contain alphanumeric characters or underscores, and may nor start with a digit). A $PREFIX directive was found with an argument (given -in the message) that violates those restictions. +in the message) that violates those restrictions. Note: the $PREFIX directive is deprecated and will be removed in a future version of BIND10. From 27c6b8cef44f5daaa149ec72e3b7052e516ebc26 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 6 Jul 2011 08:50:32 -0500 Subject: [PATCH 104/974] [trac1077] add space between "BIND 10" Where is the style guide for .mes messages files? --- src/lib/log/log_messages.mes | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/log/log_messages.mes b/src/lib/log/log_messages.mes index b88d5638f7..6c053152f6 100644 --- a/src/lib/log/log_messages.mes +++ b/src/lib/log/log_messages.mes @@ -36,8 +36,8 @@ Logging has been configured so that output is written to the terminal Allowed values are "stdout" and "stderr". % LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code -During start-up, BIND10 detected that the given message identification -had been defined multiple times in the BIND10 code. This indicates a +During start-up, BIND 10 detected that the given message identification +had been defined multiple times in the BIND 10 code. This indicates a programming error; please log a bug report. % LOG_DUPLICATE_NAMESPACE line %1: duplicate $NAMESPACE directive found @@ -99,10 +99,10 @@ There may be several reasons why this message may appear: - The program outputting the message may not use that particular message (e.g. it originates in a module not used by the program.) -- The local file was written for an earlier version of the BIND10 software +- The local file was written for an earlier version of the BIND 10 software and the later version no longer generates that message. -Whatever the reason, there is no impact on the operation of BIND10. +Whatever the reason, there is no impact on the operation of BIND 10. % LOG_OPEN_OUTPUT_FAIL unable to open %1 for output: %2 Originating within the logging code, the program was not able to open @@ -115,7 +115,7 @@ This error is generated when the compiler finds a $PREFIX directive with more than one argument. Note: the $PREFIX directive is deprecated and will be removed in a future -version of BIND10. +version of BIND 10. % LOG_PREFIX_INVALID_ARG line %1: $PREFIX directive has an invalid argument ('%2') Within a message file, the $PREFIX directive takes a single argument, @@ -126,10 +126,10 @@ with a digit). A $PREFIX directive was found with an argument (given in the message) that violates those restrictions. Note: the $PREFIX directive is deprecated and will be removed in a future -version of BIND10. +version of BIND 10. % LOG_READING_LOCAL_FILE reading local message file %1 -This is an informational message output by BIND10 when it starts to read +This is an informational message output by BIND 10 when it starts to read a local message file. (A local message file may replace the text of one of more messages; the ID of the message will not be changed though.) From 91e59cdd3ffa76c041f774c4ff61cd865299ab75 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 6 Jul 2011 16:35:29 +0100 Subject: [PATCH 105/974] [trac1078] Update message descriptions in line with comments in ticket --- src/bin/resolver/resolver_messages.mes | 241 ++++++++++++++----------- src/lib/resolve/resolve_messages.mes | 14 +- 2 files changed, 140 insertions(+), 115 deletions(-) diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes index 05b9a8b548..e6602d323a 100644 --- a/src/bin/resolver/resolver_messages.mes +++ b/src/bin/resolver/resolver_messages.mes @@ -16,151 +16,174 @@ # along with the resolver methods. % RESOLVER_AXFR_TCP AXFR request received over TCP -A debug message, the resolver received a NOTIFY message over TCP. The server -cannot process it and will return an error message to the sender with the -RCODE set to NOTIMP. +This is a debug message output when the resolver received a request for +an AXFR (full transfer of a zone) over TCP. Only authoritative servers +are able to handle AXFR requests, so the resolver will return an error +message to the sender with the RCODE set to NOTIMP. % RESOLVER_AXFR_UDP AXFR request received over UDP -A debug message, the resolver received a NOTIFY message over UDP. The server -cannot process it (and in any case, an AXFR request should be sent over TCP) -and will return an error message to the sender with the RCODE set to FORMERR. +This is a debug message output when the resolver received a request for +an AXFR (full transfer of a zone) over TCP. Only authoritative servers +are able to handle AXFR requests (and in any case, an AXFR request should +be sent over TCP), so the resolver will return an error message to the +sender with the RCODE set to NOTIMP. % RESOLVER_CLIENT_TIME_SMALL client timeout of %1 is too small -An error indicating that the configuration value specified for the query -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the client timeout was found to be too small. The configuration +update was abandoned and the parameters were not changed. % RESOLVER_CONFIG_CHANNEL configuration channel created -A debug message, output when the resolver has successfully established a -connection to the configuration channel. +This is a debug message output when the resolver has successfully +established a connection to the configuration channel. % RESOLVER_CONFIG_ERROR error in configuration: %1 -An error was detected in a configuration update received by the resolver. This -may be in the format of the configuration message (in which case this is a -programming error) or it may be in the data supplied (in which case it is -a user error). The reason for the error, given as a parameter in the message, -will give more details. +An error was detected in a configuration update received by the +resolver. This may be in the format of the configuration message (in +which case this is a programming error) or it may be in the data supplied +(in which case it is a user error). The reason for the error, included +in the message, will give more details. The configuration update is +not applied and the resolver parameters were not changed. % RESOLVER_CONFIG_LOADED configuration loaded -A debug message, output when the resolver configuration has been successfully -loaded. +This is a debug message output when the resolver configuration has been +successfully loaded. % RESOLVER_CONFIG_UPDATED configuration updated: %1 -A debug message, the configuration has been updated with the specified -information. +This is a debug message output when the resolver configuration is being +updated with the specified information. % RESOLVER_CREATED main resolver object created -A debug message, output when the Resolver() object has been created. +This is a debug message indicating that the main resolver object has +been created. % RESOLVER_DNS_MESSAGE_RECEIVED DNS message received: %1 -A debug message, this always precedes some other logging message and is the -formatted contents of the DNS packet that the other message refers to. +This is a debug message from the resolver listing the contents of a +received DNS message. % RESOLVER_DNS_MESSAGE_SENT DNS message of %1 bytes sent: %2 -A debug message, this contains details of the response sent back to the querying -system. +This is a debug message containing details of the response returned by +the resolver to the querying system. % RESOLVER_FAILED resolver failed, reason: %1 -This is an error message output when an unhandled exception is caught by the -resolver. All it can do is to shut down. +This is an error message output when an unhandled exception is caught +by the resolver. After this, the resolver will shut itself down. +Please submit a bug report. % RESOLVER_FORWARD_ADDRESS setting forward address %1(%2) -This message may appear multiple times during startup, and it lists the -forward addresses used by the resolver when running in forwarding mode. +If the resolver is running in forward mode, this message will appear +during startup to list the forward address. If multiple addresses are +specified, it will appear once for each address. % RESOLVER_FORWARD_QUERY processing forward query -The received query has passed all checks and is being forwarded to upstream +This is a debug message indicating that a query received by the resolver +has passed a set of checks (message is well-formed, it is allowed by the +ACL, it is a supported opcode etc.) and is being forwarded to upstream servers. % RESOLVER_HEADER_ERROR message received, exception when processing header: %1 -A debug message noting that an exception occurred during the processing of -a received packet. The packet has been dropped. +This is a debug message from the resolver noting that an exception +occurred during the processing of a received packet. The packet has +been dropped. % RESOLVER_IXFR IXFR request received -The resolver received a NOTIFY message over TCP. The server cannot process it -and will return an error message to the sender with the RCODE set to NOTIMP. +This is a debug message indicating that the resolver received a request +for an IXFR (incremental transfer of a zone). Only authoritative servers +are able to handle IXFR requests, so the resolver will return an error +message to the sender with the RCODE set to NOTIMP. % RESOLVER_LOOKUP_TIME_SMALL lookup timeout of %1 is too small -An error indicating that the configuration value specified for the lookup -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the lookup timeout was found to be too small. The configuration +update will not be applied. % RESOLVER_MESSAGE_ERROR error parsing received message: %1 - returning %2 -A debug message noting that the resolver received a message and the -parsing of the body of the message failed due to some error (although -the parsing of the header succeeded). The message parameters give a -textual description of the problem and the RCODE returned. +This is a debug message noting that parsing of the body of a received +message by the resolver failed due to some error (although the parsing of +the header succeeded). The message parameters give a textual description +of the problem and the RCODE returned. % RESOLVER_NEGATIVE_RETRIES negative number of retries (%1) specified in the configuration -An error message indicating that the resolver configuration has specified a -negative retry count. Only zero or positive values are valid. +This error is issued when a resolver configuration update has specified +a negative retry count: only zero or positive values are valid. The +configuration update was abandoned and the parameters were not changed. % RESOLVER_NON_IN_PACKET non-IN class request received, returning REFUSED message -A debug message, the resolver has received a DNS packet that was not IN class. -The resolver cannot handle such packets, so is returning a REFUSED response to -the sender. +This debug message is issued when resolver has received a DNS packet that +was not IN (Internet) class. The resolver cannot handle such packets, +so is returning a REFUSED response to the sender. % RESOLVER_NORMAL_QUERY processing normal query -The received query has passed all checks and is being processed by the resolver. +This is a debug message indicating that the query received by the resolver +has passed a set of checks (message is well-formed, it is allowed by the +ACL, it is a supported opcode etc.) and is being processed the resolver. % RESOLVER_NOTIFY_RECEIVED NOTIFY arrived but server is not authoritative -The resolver received a NOTIFY message. As the server is not authoritative it -cannot process it, so it returns an error message to the sender with the RCODE -set to NOTAUTH. +The resolver has received a NOTIFY message. As the server is not +authoritative it cannot process it, so it returns an error message to +the sender with the RCODE set to NOTAUTH. % RESOLVER_NOT_ONE_QUESTION query contained %1 questions, exactly one question was expected -A debug message, the resolver received a query that contained the number of -entires in the question section detailed in the message. This is a malformed -message, as a DNS query must contain only one question. The resolver will -return a message to the sender with the RCODE set to FORMERR. +This debug message indicates that the resolver received a query that +contained the number of entries in the question section detailed in +the message. This is a malformed message, as a DNS query must contain +only one question. The resolver will return a message to the sender +with the RCODE set to FORMERR. % RESOLVER_NO_ROOT_ADDRESS no root addresses available -A warning message during startup, indicates that no root addresses have been -set. This may be because the resolver will get them from a priming query. +A warning message issued during resolver startup, this indicates that +no root addresses have been set. This may be because the resolver will +get them from a priming query. % RESOLVER_PARSE_ERROR error parsing received message: %1 - returning %2 -A debug message noting that the resolver received a message and the parsing -of the body of the message failed due to some non-protocol related reason -(although the parsing of the header succeeded). The message parameters give -a textual description of the problem and the RCODE returned. +This is a debug message noting that the resolver received a message and +the parsing of the body of the message failed due to some non-protocol +related reason (although the parsing of the header succeeded). +The message parameters give a textual description of the problem and +the RCODE returned. % RESOLVER_PRINT_COMMAND print message command, arguments are: %1 -This message is logged when a "print_message" command is received over the -command channel. +This debug message is logged when a "print_message" command is received +by the resolver over the command channel. % RESOLVER_PROTOCOL_ERROR protocol error parsing received message: %1 - returning %2 -A debug message noting that the resolver received a message and the parsing -of the body of the message failed due to some protocol error (although the -parsing of the header succeeded). The message parameters give a textual -description of the problem and the RCODE returned. +This is a debug message noting that the resolver received a message and +the parsing of the body of the message failed due to some protocol error +(although the parsing of the header succeeded). The message parameters +give a textual description of the problem and the RCODE returned. % RESOLVER_QUERY_SETUP query setup -A debug message noting that the resolver is creating a RecursiveQuery object. +This is a debug message noting that the resolver is creating a +RecursiveQuery object. % RESOLVER_QUERY_SHUTDOWN query shutdown -A debug message noting that the resolver is destroying a RecursiveQuery object. +This is a debug message noting that the resolver is destroying a +RecursiveQuery object. % RESOLVER_QUERY_TIME_SMALL query timeout of %1 is too small -An error indicating that the configuration value specified for the query -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the query timeout was found to be too small. The configuration +parameters were not changed. % RESOLVER_RECEIVED_MESSAGE resolver has received a DNS message -A debug message indicating that the resolver has received a message. Depending -on the debug settings, subsequent log output will indicate the nature of the -message. +This is a debug message indicating that the resolver has received a +DNS message. Depending on the debug settings, subsequent log output +will indicate the nature of the message. % RESOLVER_RECURSIVE running in recursive mode -This is an informational message that appears at startup noting that the -resolver is running in recursive mode. +This is an informational message that appears at startup noting that +the resolver is running in recursive mode. % RESOLVER_SERVICE_CREATED service object created -A debug message, output when the main service object (which handles the -received queries) is created. +This debug message is output when resolver creates the main service object +(which handles the received queries). % RESOLVER_SET_PARAMS query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4 -A debug message, lists the parameters being set for the resolver. These are: +This debug message lists the parameters being set for the resolver. These are: query timeout: the timeout (in ms) used for queries originated by the resolver -to upstream servers. Client timeout: the interval to resolver a query by +to upstream servers. Client timeout: the interval to resolve a query by a client: after this time, the resolver sends back a SERVFAIL to the client -whilst continuing to resolver the query. Lookup timeout: the time at which the +whilst continuing to resolve the query. Lookup timeout: the time at which the resolver gives up trying to resolve a query. Retry count: the number of times the resolver will retry a query to an upstream server if it gets a timeout. @@ -169,17 +192,18 @@ resolution of the client query might require a large number of queries to upstream nameservers. Even if none of these queries timeout, the total time taken to perform all the queries may exceed the client timeout. When this happens, a SERVFAIL is returned to the client, but the resolver continues -with the resolution process. Data received is added to the cache. However, +with the resolution process; data received is added to the cache. However, there comes a time - the lookup timeout - when even the resolver gives up. At this point it will wait for pending upstream queries to complete or timeout and drop the query. % RESOLVER_SET_ROOT_ADDRESS setting root address %1(%2) -This message may appear multiple times during startup; it lists the root -addresses used by the resolver. +This message gives the address of one of the root servers used by the +resolver. It is output during startup and may appear multiple times, +oncee for each root server address. % RESOLVER_SHUTDOWN resolver shutdown complete -This information message is output when the resolver has shut down. +This informational message is output when the resolver has shut down. % RESOLVER_STARTED resolver started This informational message is output by the resolver when all initialization @@ -189,35 +213,36 @@ has been completed and it is entering its main loop. An informational message, this is output when the resolver starts up. % RESOLVER_UNEXPECTED_RESPONSE received unexpected response, ignoring -A debug message noting that the server has received a response instead of a -query and is ignoring it. +This is a debug message noting that the resolver received a DNS response +packet on the port on which is it listening for queries. The packet +has been ignored. % RESOLVER_UNSUPPORTED_OPCODE opcode %1 not supported by the resolver -A debug message, the resolver received a message with an unsupported opcode -(it can only process QUERY opcodes). It will return a message to the sender -with the RCODE set to NOTIMP. +This is debug message output when the resolver received a message with an +unsupported opcode (it can only process QUERY opcodes). It will return +a message to the sender with the RCODE set to NOTIMP. -% RESOLVER_SET_QUERY_ACL query ACL is configured -A debug message that appears when a new query ACL is configured for the -resolver. +% RESOLVER_SET_QUERY_ACL query ACL is configured +This debug message is generated when a new query ACL is configured for +the resolver. -% RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4 -A debug message that indicates an incoming query is accepted in terms of -the query ACL. The log message shows the query in the form of -//, and the client that sends the -query in the form of #. +% RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4 +This debug message is produced by the resolver when an incoming query +is accepted in terms of the query ACL. The log message shows the query +in the form of //, and the client +that sends the query in the form of #. -% RESOLVER_QUERY_REJECTED query rejected: '%1/%2/%3' from %4 -An informational message that indicates an incoming query is rejected -in terms of the query ACL. This results in a response with an RCODE of -REFUSED. The log message shows the query in the form of //, and the client that sends the -query in the form of #. +% RESOLVER_QUERY_REJECTED query rejected: '%1/%2/%3' from %4 +This is an informational message that indicates an incoming query has +been rejected by the resolver because of the query ACL. This results +in a response with an RCODE of REFUSED. The log message shows the query +in the form of //, and the client +that sends the query in the form of #. -% RESOLVER_QUERY_DROPPED query dropped: '%1/%2/%3' from %4 -An informational message that indicates an incoming query is dropped -in terms of the query ACL. Unlike the RESOLVER_QUERY_REJECTED -case, the server does not return any response. The log message -shows the query in the form of //, and the client that sends the query in the form of #. +% RESOLVER_QUERY_DROPPED query dropped: '%1/%2/%3' from %4 +This is an informational message that indicates an incoming query has +been dropped by the resolver because of the query ACL. Unlike the +RESOLVER_QUERY_REJECTED case, the server does not return any response. +The log message shows the query in the form of //, and the client that sends the query in the form of +#. diff --git a/src/lib/resolve/resolve_messages.mes b/src/lib/resolve/resolve_messages.mes index 97c4d908cb..fd5ec3115a 100644 --- a/src/lib/resolve/resolve_messages.mes +++ b/src/lib/resolve/resolve_messages.mes @@ -123,11 +123,11 @@ called because a nameserver has been found, and that a query is being sent to the specified nameserver. % RESLIB_TEST_SERVER setting test server to %1(%2) -This is an internal debugging message and is only generated in unit tests. -It indicates that all upstream queries from the resolver are being routed to -the specified server, regardless of the address of the nameserver to which -the query would normally be routed. As it should never be seen in normal -operation, it is a warning message instead of a debug message. +This is a warning message only generated in unit tests. It indicates +that all upstream queries from the resolver are being routed to the +specified server, regardless of the address of the nameserver to which +the query would normally be routed. If seen during normal operation, +please log a bug report. % RESLIB_TEST_UPSTREAM sending upstream query for <%1> to test server at %2 This is a debug message and should only be seen in unit tests. A query for @@ -135,8 +135,8 @@ the specified tuple is being sent to a test nameserver whose address is given in the message. % RESLIB_TIMEOUT query <%1> to %2 timed out -A debug message indicating that the specified query has timed out and as -there are no retries left, an error will be reported. +A debug message indicating that the specified upstream query has timed out and +there are no retries left. % RESLIB_TIMEOUT_RETRY query <%1> to %2 timed out, re-trying (retries left: %3) A debug message indicating that the specified query has timed out and that From 025f40af1c7877ac0ab84e0ee159806a57285c3b Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 6 Jul 2011 11:05:35 -0500 Subject: [PATCH 106/974] [master] add empty TODO This file is known by automake and included automatically in release distribution. Since we have empty NEWS I decided to do the same for this file too. On accident, I had some TODO file that was copied into previous four tarballs. I don't know how. --- TODO | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000000..e69de29bb2 From 78763269e5388263ad29b02049fa61c62829dbe8 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 6 Jul 2011 12:26:14 -0700 Subject: [PATCH 107/974] [master] fixed a trivial typo in test names. pushing directly. --- src/bin/auth/tests/command_unittest.cc | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index 3fdd08601e..2fc8052196 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -48,9 +48,9 @@ using namespace isc::datasrc; using namespace isc::config; namespace { -class AuthConmmandTest : public ::testing::Test { +class AuthCommandTest : public ::testing::Test { protected: - AuthConmmandTest() : server(false, xfrout), rcode(-1) { + AuthCommandTest() : server(false, xfrout), rcode(-1) { server.setStatisticsSession(&statistics_session); } void checkAnswer(const int expected_code) { @@ -67,14 +67,14 @@ public: void stopServer(); // need to be public for boost::bind }; -TEST_F(AuthConmmandTest, unknownCommand) { +TEST_F(AuthCommandTest, unknownCommand) { result = execAuthServerCommand(server, "no_such_command", ConstElementPtr()); parseAnswer(rcode, result); EXPECT_EQ(1, rcode); } -TEST_F(AuthConmmandTest, DISABLED_unexpectedException) { +TEST_F(AuthCommandTest, DISABLED_unexpectedException) { // execAuthServerCommand() won't catch standard exceptions. // Skip this test for now: ModuleCCSession doesn't seem to validate // commands. @@ -83,7 +83,7 @@ TEST_F(AuthConmmandTest, DISABLED_unexpectedException) { runtime_error); } -TEST_F(AuthConmmandTest, sendStatistics) { +TEST_F(AuthCommandTest, sendStatistics) { result = execAuthServerCommand(server, "sendstats", ConstElementPtr()); // Just check some message has been sent. Detailed tests specific to // statistics are done in its own tests. @@ -92,15 +92,15 @@ TEST_F(AuthConmmandTest, sendStatistics) { } void -AuthConmmandTest::stopServer() { +AuthCommandTest::stopServer() { result = execAuthServerCommand(server, "shutdown", ConstElementPtr()); parseAnswer(rcode, result); assert(rcode == 0); // make sure the test stops when something is wrong } -TEST_F(AuthConmmandTest, shutdown) { +TEST_F(AuthCommandTest, shutdown) { isc::asiolink::IntervalTimer itimer(server.getIOService()); - itimer.setup(boost::bind(&AuthConmmandTest::stopServer, this), 1); + itimer.setup(boost::bind(&AuthCommandTest::stopServer, this), 1); server.getIOService().run(); EXPECT_EQ(0, rcode); } @@ -165,7 +165,7 @@ newZoneChecks(AuthSrv& server) { find(Name("ns.test2.example"), RRType::AAAA()).code); } -TEST_F(AuthConmmandTest, loadZone) { +TEST_F(AuthCommandTest, loadZone) { configureZones(server); ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR @@ -182,7 +182,7 @@ TEST_F(AuthConmmandTest, loadZone) { newZoneChecks(server); } -TEST_F(AuthConmmandTest, loadBrokenZone) { +TEST_F(AuthCommandTest, loadBrokenZone) { configureZones(server); ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR @@ -195,7 +195,7 @@ TEST_F(AuthConmmandTest, loadBrokenZone) { zoneChecks(server); // zone shouldn't be replaced } -TEST_F(AuthConmmandTest, loadUnreadableZone) { +TEST_F(AuthCommandTest, loadUnreadableZone) { configureZones(server); // install the zone file as unreadable @@ -209,7 +209,7 @@ TEST_F(AuthConmmandTest, loadUnreadableZone) { zoneChecks(server); // zone shouldn't be replaced } -TEST_F(AuthConmmandTest, loadZoneWithoutDataSrc) { +TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) { // try to execute load command without configuring the zone beforehand. // it should fail. result = execAuthServerCommand(server, "loadzone", @@ -218,7 +218,7 @@ TEST_F(AuthConmmandTest, loadZoneWithoutDataSrc) { checkAnswer(1); } -TEST_F(AuthConmmandTest, loadSqlite3DataSrc) { +TEST_F(AuthCommandTest, loadSqlite3DataSrc) { // For sqlite3 data source we don't have to do anything (the data source // (re)loads itself automatically) result = execAuthServerCommand(server, "loadzone", @@ -228,7 +228,7 @@ TEST_F(AuthConmmandTest, loadSqlite3DataSrc) { checkAnswer(0); } -TEST_F(AuthConmmandTest, loadZoneInvalidParams) { +TEST_F(AuthCommandTest, loadZoneInvalidParams) { configureZones(server); // null arg From 2bfafa08c054715e6163a91da334e1e4fa780740 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 6 Jul 2011 18:34:25 -0700 Subject: [PATCH 108/974] [trac983] implemented the framework for python version of RequestContext. added constructor and __str__() with tests for the constructor. --- src/lib/python/isc/acl/Makefile.am | 1 + src/lib/python/isc/acl/acl.cc | 4 + src/lib/python/isc/acl/acl.h | 1 + src/lib/python/isc/acl/dns.cc | 5 + .../python/isc/acl/dns_requestacl_python.cc | 2 +- .../isc/acl/dns_requestcontext_python.cc | 318 ++++++++++++++++++ .../isc/acl/dns_requestcontext_python.h | 54 +++ src/lib/python/isc/acl/tests/dns_test.py | 57 +++- 8 files changed, 440 insertions(+), 2 deletions(-) create mode 100644 src/lib/python/isc/acl/dns_requestcontext_python.cc create mode 100644 src/lib/python/isc/acl/dns_requestcontext_python.h diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index b8c599690e..5156429810 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -12,6 +12,7 @@ acl_la_LDFLAGS = $(PYTHON_LDFLAGS) acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) dns_la_SOURCES = dns.cc dns_requestacl_python.h dns_requestacl_python.cc +dns_la_SOURCES += dns_requestcontext_python.h dns_requestcontext_python.cc dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) dns_la_LDFLAGS = $(PYTHON_LDFLAGS) # Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be diff --git a/src/lib/python/isc/acl/acl.cc b/src/lib/python/isc/acl/acl.cc index 85bd280418..e274ccdfaf 100644 --- a/src/lib/python/isc/acl/acl.cc +++ b/src/lib/python/isc/acl/acl.cc @@ -24,6 +24,7 @@ using namespace isc::acl::python; namespace isc { namespace acl { namespace python { +PyObject* po_ACLError; PyObject* po_LoaderError; } } @@ -52,6 +53,9 @@ PyInit_acl(void) { } try { + po_ACLError = PyErr_NewException("isc.acl.Error", NULL, NULL); + PyObjectContainer(po_ACLError).installToModule(mod, "Error"); + po_LoaderError = PyErr_NewException("isc.acl.LoaderError", NULL, NULL); PyObjectContainer(po_LoaderError).installToModule(mod, "LoaderError"); } catch (...) { diff --git a/src/lib/python/isc/acl/acl.h b/src/lib/python/isc/acl/acl.h index 688925dad2..59e083f991 100644 --- a/src/lib/python/isc/acl/acl.h +++ b/src/lib/python/isc/acl/acl.h @@ -21,6 +21,7 @@ namespace isc { namespace acl { namespace python { +extern PyObject* po_ACLError; extern PyObject* po_LoaderError; } // namespace python diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc index ebf7b59f61..68190cd2b9 100644 --- a/src/lib/python/isc/acl/dns.cc +++ b/src/lib/python/isc/acl/dns.cc @@ -25,6 +25,7 @@ #include #include "acl.h" +#include "dns_requestcontext_python.h" #include "dns_requestacl_python.h" using namespace std; @@ -90,6 +91,10 @@ PyInit_dns(void) { return (NULL); } + if (!initModulePart_RequestContext(mod)) { + Py_DECREF(mod); + return (NULL); + } if (!initModulePart_RequestACL(mod)) { Py_DECREF(mod); return (NULL); diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc index c55292e1cd..4f3ca203e1 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.cc +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -129,7 +129,7 @@ namespace python { // Most of the functions are not actually implemented and NULL here. PyTypeObject requestacl_type = { PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.RequestACL", + "isc.acl.dns.RequestACL", sizeof(s_RequestACL), // tp_basicsize 0, // tp_itemsize reinterpret_cast(RequestACL_destroy), // tp_dealloc diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.cc b/src/lib/python/isc/acl/dns_requestcontext_python.cc new file mode 100644 index 0000000000..9b724cfcc7 --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestcontext_python.cc @@ -0,0 +1,318 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// Enable this if you use s# variants with PyArg_ParseTuple(), see +// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers +//#define PY_SSIZE_T_CLEAN + +// Python.h needs to be placed at the head of the program file, see: +// http://docs.python.org/py3k/extending/extending.html#a-simple-example +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include + +#include "acl.h" +#include "dns_requestcontext_python.h" + +using namespace std; +using boost::scoped_ptr; +using boost::lexical_cast; +using namespace isc; +using namespace isc::util::python; +using namespace isc::acl::dns; +using namespace isc::acl::python; +using namespace isc::acl::dns::python; + +namespace isc { +namespace acl { +namespace dns { +namespace python { + +struct s_RequestContext::Data { + Data(const char* const remote_addr, const unsigned short remote_port) { + struct addrinfo hints, *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + const int error(getaddrinfo(remote_addr, + lexical_cast(remote_port).c_str(), + &hints, &res)); + if (error != 0) { + isc_throw(InvalidParameter, "Failed to convert [" << remote_addr + << "]:" << remote_port << ", " << gai_strerror(error)); + } + assert(sizeof(remote_ss) > res->ai_addrlen); + memcpy(&remote_ss, res->ai_addr, res->ai_addrlen); + remote_salen = res->ai_addrlen; + freeaddrinfo(res); + + remote_ipaddr.reset(new IPAddress(getRemoteSockaddr())); + } + + const struct sockaddr& getRemoteSockaddr() const { + const void* p = &remote_ss; + return (*static_cast(p)); + } + + scoped_ptr remote_ipaddr; + socklen_t remote_salen; + +private: + struct sockaddr_storage remote_ss; +}; + +} // namespace python +} // namespace dns +} // namespace acl +} // namespace isc + + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// RequestContext +// + +// Trivial constructor. +s_RequestContext::s_RequestContext() : cppobj(NULL), data_(NULL) { +} + +namespace { +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer RequestContextContainer; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef RequestContext_methods[] = { + { NULL, NULL, 0, NULL } +}; + +int +RequestContext_init(PyObject* po_self, PyObject* args, PyObject*) { + s_RequestContext* const self = static_cast(po_self); + + try { + // In this initial implementation, the constructor is simply: It + // takes a single parameter, which should be a Python socket address + // object. For IPv4, it's ('address test', numeric_port); for IPv6, + // it's ('address text', num_port, num_flowid, num_zoneid). + // Below, we parse the argument in the most straightforward way. + // As the constructor becomes more complicated, we should probably + // make it more structural (for example, we should first retrieve + // the socket address as a PyObject, and parse it recursively) + + const char* remote_addr; + unsigned short remote_port; + unsigned int remote_flowinfo; // IPv6 only, unused here + unsigned int remote_zoneid; // IPv6 only, unused here + + if (PyArg_ParseTuple(args, "(sH)", &remote_addr, &remote_port) || + PyArg_ParseTuple(args, "(sHII)", &remote_addr, &remote_port, + &remote_flowinfo, &remote_zoneid)) + { + // We need to clear the error in case the first call to PareTuple + // fails. + PyErr_Clear(); + + auto_ptr dataptr( + new s_RequestContext::Data(remote_addr, remote_port)); + self->cppobj = new RequestContext(*dataptr->remote_ipaddr); + self->data_ = dataptr.release(); + return (0); + } + } catch (const exception& ex) { + const string ex_what = "Failed to construct RequestContext object: " + + string(ex.what()); + PyErr_SetString(po_ACLError, ex_what.c_str()); + return (-1); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, + "Unexpected exception in constructing RequestContext"); + return (-1); + } + + PyErr_SetString(PyExc_TypeError, + "Invalid arguments to RequestContext constructor"); + + return (-1); +} + +void +RequestContext_destroy(PyObject* po_self) { + s_RequestContext* const self = static_cast(po_self); + + delete self->cppobj; + delete self->data_; + Py_TYPE(self)->tp_free(self); +} + +// A helper function for __str()__ +string +sockaddrToText(const struct sockaddr& sa, socklen_t sa_len) { + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + if (getnameinfo(&sa, sa_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), + NI_NUMERICHOST | NI_NUMERICSERV)) { + // In this context this should never fail. + isc_throw(Unexpected, "Unexpected failure in getnameinfo"); + } + + return ("[" + string(hbuf) + "]:" + string(sbuf)); +} + +// for the __str__() method. This method is provided mainly for internal +// testing. +PyObject* +RequestContext_str(PyObject* po_self) { + const s_RequestContext* const self = + static_cast(po_self); + + try { + stringstream objss; + objss << "<" << requestcontext_type.tp_name << " object, " + << "remote_addr=" + << sockaddrToText(self->data_->getRemoteSockaddr(), + self->data_->remote_salen) << ">"; + return (Py_BuildValue("s", objss.str().c_str())); + } catch (const exception& ex) { + const string ex_what = + "Failed to convert RequestContext object to text: " + + string(ex.what()); + PyErr_SetString(PyExc_RuntimeError, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, "Unexpected failure in " + "converting RequestContext object to text"); + } + return (NULL); +} +} // end of unnamed namespace + +namespace isc { +namespace acl { +namespace dns { +namespace python { +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_RequestContext +// Most of the functions are not actually implemented and NULL here. +PyTypeObject requestcontext_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "isc.acl.dns.RequestContext", + sizeof(s_RequestContext), // tp_basicsize + 0, // tp_itemsize + RequestContext_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + RequestContext_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The RequestContext class objects is...(COMPLETE THIS)", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + RequestContext_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + RequestContext_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +// Module Initialization, all statics are initialized here +bool +initModulePart_RequestContext(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&requestcontext_type) < 0) { + return (false); + } + void* p = &requestcontext_type; + if (PyModule_AddObject(mod, "RequestContext", + static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&requestcontext_type); + + return (true); +} +} // namespace python +} // namespace dns +} // namespace acl +} // namespace isc diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.h b/src/lib/python/isc/acl/dns_requestcontext_python.h new file mode 100644 index 0000000000..766133b38d --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestcontext_python.h @@ -0,0 +1,54 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_REQUESTCONTEXT_H +#define __PYTHON_REQUESTCONTEXT_H 1 + +#include + +#include + +namespace isc { +namespace acl { +namespace dns { +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_RequestContext : public PyObject { +public: + s_RequestContext(); + RequestContext* cppobj; + + // This object needs to maintain some source data to construct the + // underlying RequestContext object throughout its lifetime. + // These are "public" so that it can be accessed in the python wrapper + // implementation, but essentially they should be private, and the + // implementation details are hidden. + struct Data; + Data* data_; +}; + +extern PyTypeObject requestcontext_type; + +bool initModulePart_RequestContext(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace acl +} // namespace isc +#endif // __PYTHON_REQUESTCONTEXT_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index ab3cb82e54..e05e1a8e2b 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -14,9 +14,64 @@ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest -from isc.acl.acl import LoaderError +import socket +from isc.acl.acl import LoaderError, Error from isc.acl.dns import * +def get_sockaddr(address, port): + '''This is a simple shortcut wrapper for getaddrinfo''' + ai = socket.getaddrinfo(address, port, 0, 0, 0, socket.AI_NUMERICHOST)[0] + return ai[4] + +class RequestContextTest(unittest.TestCase): + + def test_construct(self): + # Construct the context from IPv4/IPv6 addresses, check the object + # by printing it. + self.assertEqual('', + RequestContext(('192.0.2.1', 53001)).__str__()) + self.assertEqual('', + RequestContext(('2001:db8::1234', 53006, + 0, 0)).__str__()) + + # Unusual case: port number overflows (this constructor allows that, + # although it should be rare anyway; the socket address should + # normally come from the Python socket module. + self.assertEqual('', + RequestContext(('192.0.2.1', 65536)).__str__()) + + # same test using socket.getaddrinfo() to ensure it accepts the sock + # address representation used in the Python socket module. + self.assertEqual('', + RequestContext(get_sockaddr('192.0.2.1', + 53001)).__str__()) + self.assertEqual('', + RequestContext(get_sockaddr('2001:db8::1234', + 53006)).__str__()) + + # + # Invalid parameters (in our expected usage this should not happen + # because the sockaddr would come from the Python socket module, but + # validation should still be performed correctly) + # + # not a tuple + self.assertRaises(TypeError, RequestContext, 1) + # invalid number of parameters + self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 53), 0) + # tuple is not in the form of sockaddr + self.assertRaises(TypeError, RequestContext, (0, 53)) + self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 'http')) + self.assertRaises(TypeError, RequestContext, ('::', 0, 'flow', 0)) + # invalid address + self.assertRaises(Error, RequestContext, ('example.com', 5300)) + self.assertRaises(Error, RequestContext, ('192.0.2.1.1', 5300)) + self.assertRaises(Error, RequestContext, ('2001:db8:::1', 5300)) + class RequestACLTest(unittest.TestCase): def test_request_loader(self): From 159caa607fc11e4b7c1b5efcbb28d0ebf5e99903 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 6 Jul 2011 18:55:11 -0700 Subject: [PATCH 109/974] [trac983] added basic action constants --- src/lib/python/isc/acl/acl.cc | 12 ++++++++++++ src/lib/python/isc/acl/tests/Makefile.am | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/acl/acl.cc b/src/lib/python/isc/acl/acl.cc index e274ccdfaf..64279064a9 100644 --- a/src/lib/python/isc/acl/acl.cc +++ b/src/lib/python/isc/acl/acl.cc @@ -16,6 +16,8 @@ #include +#include + #include "acl.h" using namespace isc::util::python; @@ -58,6 +60,16 @@ PyInit_acl(void) { po_LoaderError = PyErr_NewException("isc.acl.LoaderError", NULL, NULL); PyObjectContainer(po_LoaderError).installToModule(mod, "LoaderError"); + + // Install module constants. Note that we can release our own + // references to these objects because we don't have corresponding + // C++ variables. + PyObjectContainer(Py_BuildValue("I", isc::acl::ACCEPT)). + installToModule(mod, "ACCEPT", false); + PyObjectContainer(Py_BuildValue("I", isc::acl::REJECT)). + installToModule(mod, "REJECT", false); + PyObjectContainer(Py_BuildValue("I", isc::acl::DROP)). + installToModule(mod, "DROP", false); } catch (...) { Py_DECREF(mod); return (NULL); diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am index e185e5e264..9bd20da754 100644 --- a/src/lib/python/isc/acl/tests/Makefile.am +++ b/src/lib/python/isc/acl/tests/Makefile.am @@ -1,5 +1,5 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ -PYTESTS = dns_test.py +PYTESTS = acl_test.py dns_test.py EXTRA_DIST = $(PYTESTS) From 8faa21b81fde5c30ca1df72739b9a0dd27005402 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 6 Jul 2011 18:56:40 -0700 Subject: [PATCH 110/974] [trac983] forgot to add tests --- src/lib/python/isc/acl/tests/acl_test.py | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/lib/python/isc/acl/tests/acl_test.py diff --git a/src/lib/python/isc/acl/tests/acl_test.py b/src/lib/python/isc/acl/tests/acl_test.py new file mode 100644 index 0000000000..24a0c94f25 --- /dev/null +++ b/src/lib/python/isc/acl/tests/acl_test.py @@ -0,0 +1,29 @@ +# 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. + +import unittest +from isc.acl.acl import * + +class ACLTest(unittest.TestCase): + + def test_actions(self): + # These are simple tests just checking the pre defined actions have + # different values + self.assertTrue(ACCEPT != REJECT) + self.assertTrue(REJECT != DROP) + self.assertTrue(DROP != ACCEPT) + +if __name__ == '__main__': + unittest.main() From 6ec7cbb9976f68a0ca265e72dadfbb867d59581f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 6 Jul 2011 23:46:40 -0700 Subject: [PATCH 111/974] [trac983] implemented RequestACL.execute() --- src/lib/acl/acl.h | 3 + src/lib/python/isc/acl/dns_requestacl_inc.cc | 52 +++++++++++++ .../python/isc/acl/dns_requestacl_python.cc | 58 +++++++++----- src/lib/python/isc/acl/tests/dns_test.py | 75 ++++++++++++++++++- 4 files changed, 166 insertions(+), 22 deletions(-) create mode 100644 src/lib/python/isc/acl/dns_requestacl_inc.cc diff --git a/src/lib/acl/acl.h b/src/lib/acl/acl.h index 998b2b0634..76039c9338 100644 --- a/src/lib/acl/acl.h +++ b/src/lib/acl/acl.h @@ -88,8 +88,11 @@ public: * the context against conditions and if it matches, returns the * action that belongs to the first matched entry or default action * if nothing matches. + * * \param context The thing that should be checked. It is directly * passed to the checks. + * + * \return The action for the ACL entry that first matches the context. */ const Action& execute(const Context& context) const { const typename Entries::const_iterator end(entries_.end()); diff --git a/src/lib/python/isc/acl/dns_requestacl_inc.cc b/src/lib/python/isc/acl/dns_requestacl_inc.cc new file mode 100644 index 0000000000..1ff369d4d6 --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestacl_inc.cc @@ -0,0 +1,52 @@ +namespace { +const char* const RequestACL_doc = "\ +The ACL itself.\n\ +\n\ +It holds bunch of ordered entries, each one consisting of a check ( of\n\ +any kind, it might be even compound) and an action that is returned\n\ +whenever the action matches. They are tested in the order and first\n\ +match counts.\n\ +\n\ +This is non-copyable. It seems that there's no need to copy them (even\n\ +when it would be technically possible), so we forbid it just to\n\ +prevent copying it by accident. If there really is legitimate use,\n\ +this restriction can be removed.\n\ +\n\ +The class is template. It is possible to specify on which context the\n\ +checks match and which actions it returns. The actions must be\n\ +copyable for this to work and it is expected to be something small,\n\ +usually an enum (but other objects are also possible).\n\ +\n\ +There are protected functions. In fact, you should consider them\n\ +private, they are protected so tests can get inside. This class is not\n\ +expected to be subclassed in real applications.\n\ +\n\ +ACL(default_action)\n\ +\n\ + Constructor.\n\ +\n\ + Parameters:\n\ + default_action It is the action that is returned when the\n\ + checked things \"falls off\" the end of the list\n\ + (when no rule matched).\n\ +\n\ +"; + +const char* const RequestACL_execute_doc = "\ +execute(context) -> Action \n\ +\n\ +The actual main function that decides.\n\ +\n\ +This is the function that takes the entries one by one, checks the\n\ +context against conditions and if it matches, returns the action that\n\ +belongs to the first matched entry or default action if nothing\n\ +matches.\n\ +\n\ +Parameters:\n\ + context The thing that should be checked. It is directly passed\n\ + to the checks.\n\ +\n\ +Return Value(s): The action for the ACL entry that first matches the\n\ +context.\n\ +"; +} // unnamed namespace diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc index 4f3ca203e1..353c615c1b 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.cc +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -28,11 +28,14 @@ #include #include +#include "acl.h" #include "dns_requestacl_python.h" +#include "dns_requestcontext_python.h" using namespace std; using namespace isc::util::python; using namespace isc::acl; +using namespace isc::acl::python; using namespace isc::acl::dns; using namespace isc::acl::dns::python; @@ -51,6 +54,9 @@ using namespace isc::acl::dns::python; // Trivial constructor. s_RequestACL::s_RequestACL() {} +// Import pydoc text +#include "dns_requestacl_inc.cc" + namespace { // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer RequestACLContainer; @@ -60,26 +66,9 @@ typedef CPPPyObjectContainer RequestACLContainer; // the type definition of the object, since both can use the other // -// General creation and destruction -int RequestACL_init(s_RequestACL* self, PyObject* args); -void RequestACL_destroy(s_RequestACL* self); - // These are the functions we export // For a minimal support, we don't need them. -// This list contains the actual set of functions we have in -// python. Each entry has -// 1. Python method name -// 2. Our static function here -// 3. Argument type -// 4. Documentation -PyMethodDef RequestACL_methods[] = { - { NULL, NULL, 0, NULL } -}; - -// This is a template of typical code logic of python class initialization -// with C++ backend. You'll need to adjust it according to details of the -// actual C++ class. int RequestACL_init(s_RequestACL* self, PyObject* /*args*/) { // maybe we should prohibit direct creation of the ACL @@ -118,6 +107,39 @@ RequestACL_destroy(s_RequestACL* const self) { self->cppobj.reset(); Py_TYPE(self)->tp_free(self); } + +PyObject* +RequestACL_execute(PyObject* po_self, PyObject* args) { + s_RequestACL* const self = static_cast(po_self); + + try { + const s_RequestContext* po_context; + if (PyArg_ParseTuple(args, "O!", &requestcontext_type, &po_context)) { + const BasicAction action = + self->cppobj->execute(*po_context->cppobj); + return (Py_BuildValue("I", action)); + } + } catch (const exception& ex) { + const string ex_what = "Failed to execute ACL: " + string(ex.what()); + PyErr_SetString(po_ACLError, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, + "Unexpected exception in executing ACL"); + } + + return (NULL); +} + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef RequestACL_methods[] = { + { "execute", RequestACL_execute, METH_VARARGS, RequestACL_execute_doc }, + { NULL, NULL, 0, NULL } +}; } // end of unnamed namespace namespace isc { @@ -148,7 +170,7 @@ PyTypeObject requestacl_type = { NULL, // tp_setattro NULL, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - "The RequestACL class objects is...(COMPLETE THIS)", + RequestACL_doc, NULL, // tp_traverse NULL, // tp_clear NULL, // tp_richcompare diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index e05e1a8e2b..95a3e9c4c9 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -15,7 +15,7 @@ import unittest import socket -from isc.acl.acl import LoaderError, Error +from isc.acl.acl import LoaderError, Error, ACCEPT, REJECT, DROP from isc.acl.dns import * def get_sockaddr(address, port): @@ -23,6 +23,24 @@ def get_sockaddr(address, port): ai = socket.getaddrinfo(address, port, 0, 0, 0, socket.AI_NUMERICHOST)[0] return ai[4] +def get_acl(prefix): + '''This is a simple shortcut for creating an ACL containing single rule + that accepts addresses for the given IP prefix (and reject any others + by default) + ''' + return load_request_acl('[{"action": "ACCEPT", "from": "' + prefix + '"}]') + +def get_context(address): + '''This is a simple shortcut wrapper for creating a RequestContext + object with a given IP address. Port number doesn't matter in the test + (as of the initial implementation), so it's fixed for simplicity. + ''' + return RequestContext(get_sockaddr(address, 53000)) + +# These are commonly used RequestContext object +CONTEXT4 = get_context('192.0.2.1') +CONTEXT6 = get_context('2001:db8::1') + class RequestContextTest(unittest.TestCase): def test_construct(self): @@ -74,6 +92,9 @@ class RequestContextTest(unittest.TestCase): class RequestACLTest(unittest.TestCase): + def test_direct_construct(self): + acl = RequestACL() + def test_request_loader(self): # these shouldn't raise an exception load_request_acl('[{"action": "DROP"}]') @@ -88,7 +109,7 @@ class RequestACLTest(unittest.TestCase): '[{"action": "DROP"}]', 0) def test_bad_acl_syntax(self): - # this test is derived from loader_test.cc + # the following are derived from loader_test.cc self.assertRaises(LoaderError, load_request_acl, '{}'); self.assertRaises(LoaderError, load_request_acl, '42'); self.assertRaises(LoaderError, load_request_acl, 'true'); @@ -101,6 +122,18 @@ class RequestACLTest(unittest.TestCase): self.assertRaises(LoaderError, load_request_acl, '[null]'); self.assertRaises(LoaderError, load_request_acl, '[{}]'); + # the following are derived from dns_test.cc + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "ACCEPT", "bad": "192.0.2.1"}]') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "ACCEPT", "from": 4}]') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "ACCEPT", "from": []}]') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "ACCEPT", "from": "bad"}]') + self.assertRaises(LoaderError, load_request_acl, + '[{"action": "ACCEPT", "from": null}]') + def test_bad_acl_ipsyntax(self): # this test is derived from ip_check_unittest.cc self.assertRaises(LoaderError, load_request_acl, @@ -124,8 +157,42 @@ class RequestACLTest(unittest.TestCase): self.assertRaises(LoaderError, load_request_acl, '[{"action": "DROP", "from": "::1/129"') - def test_construct(self): - RequestACL() + def test_execute(self): + # tests derived from dns_test.cc. We don't directly expose checks + # in the python wrapper, so we test it via execute(). + self.assertEqual(ACCEPT, get_acl('192.0.2.1').execute(CONTEXT4)) + self.assertEqual(REJECT, get_acl('192.0.2.53').execute(CONTEXT4)) + self.assertEqual(ACCEPT, get_acl('192.0.2.0/24').execute(CONTEXT4)) + self.assertEqual(REJECT, get_acl('192.0.1.0/24').execute(CONTEXT4)) + self.assertEqual(REJECT, get_acl('192.0.1.0/24').execute(CONTEXT4)) + + self.assertEqual(ACCEPT, get_acl('2001:db8::1').execute(CONTEXT6)) + self.assertEqual(REJECT, get_acl('2001:db8::53').execute(CONTEXT6)) + self.assertEqual(ACCEPT, get_acl('2001:db8::/64').execute(CONTEXT6)) + self.assertEqual(REJECT, get_acl('2001:db8:1::/64').execute(CONTEXT6)) + self.assertEqual(REJECT, get_acl('32.1.13.184').execute(CONTEXT6)) + + # A bit more complicated example, derived from resolver_config_unittest + acl = load_request_acl('[ {"action": "ACCEPT", ' + + ' "from": "192.0.2.1"},' + + ' {"action": "REJECT",' + + ' "from": "192.0.2.0/24"},' + + ' {"action": "DROP",' + + ' "from": "2001:db8::1"},' + + '] }') + self.assertEqual(ACCEPT, acl.execute(CONTEXT4)) + self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2'))) + self.assertEqual(DROP, acl.execute(get_context('2001:db8::1'))) + self.assertEqual(REJECT, acl.execute(get_context('2001:db8::2'))) + + def test_bad_execute(self): + acl = get_acl('192.0.2.1') + # missing parameter + self.assertRaises(TypeError, acl.execute) + # too many parameters + self.assertRaises(TypeError, acl.execute, get_context('192.0.2.2'), 0) + # type mismatch + self.assertRaises(TypeError, acl.execute, 'bad parameter') if __name__ == '__main__': unittest.main() From 026699b978f21466cdd20b09dba3fe0448e0592f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 7 Jul 2011 00:01:14 -0700 Subject: [PATCH 112/974] [trac983] prohibited direct construction of RequestACL. also did some cleanups. --- .../python/isc/acl/dns_requestacl_python.cc | 69 ++----------------- src/lib/python/isc/acl/tests/dns_test.py | 2 +- 2 files changed, 7 insertions(+), 64 deletions(-) diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc index 353c615c1b..27dc6e0010 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.cc +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -58,52 +58,16 @@ s_RequestACL::s_RequestACL() {} #include "dns_requestacl_inc.cc" namespace { -// Shortcut type which would be convenient for adding class variables safely. -typedef CPPPyObjectContainer RequestACLContainer; - -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// - -// These are the functions we export -// For a minimal support, we don't need them. - int -RequestACL_init(s_RequestACL* self, PyObject* /*args*/) { - // maybe we should prohibit direct creation of the ACL - try { -#ifdef notyet - if (PyArg_ParseTuple(args, "REPLACE ME")) { - // YOU'LL NEED SOME VALIDATION, PREPARATION, ETC, HERE. - self->cppobj = new RequestACL(/*NECESSARY PARAMS*/); - return (0); - } -#endif - self->cppobj.reset(new RequestACL(REJECT)); - return (0); - } catch (const exception& ex) { - const string ex_what = "Failed to construct RequestACL object: " + - string(ex.what()); - //PyErr_SetString(po_IscException, ex_what.c_str()); - PyErr_SetString(PyExc_TypeError, ex_what.c_str()); - return (-1); - } catch (...) { - PyErr_SetString(/*po_IscException*/PyExc_TypeError, - "Unexpected exception in constructing RequestACL"); - return (-1); - } - +RequestACL_init(PyObject*, PyObject*, PyObject*) { PyErr_SetString(PyExc_TypeError, - "Invalid arguments to RequestACL constructor"); - + "RequestACL cannot be directly constructed"); return (-1); } -// This is a template of typical code logic of python object destructor. -// In many cases you can use it without modification, but check that carefully. void -RequestACL_destroy(s_RequestACL* const self) { +RequestACL_destroy(PyObject* const po_self) { + s_RequestACL* const self = static_cast(po_self); self->cppobj.reset(); Py_TYPE(self)->tp_free(self); } @@ -154,7 +118,7 @@ PyTypeObject requestacl_type = { "isc.acl.dns.RequestACL", sizeof(s_RequestACL), // tp_basicsize 0, // tp_itemsize - reinterpret_cast(RequestACL_destroy), // tp_dealloc + RequestACL_destroy, // tp_dealloc NULL, // tp_print NULL, // tp_getattr NULL, // tp_setattr @@ -185,7 +149,7 @@ PyTypeObject requestacl_type = { NULL, // tp_descr_get NULL, // tp_descr_set 0, // tp_dictoffset - reinterpret_cast(RequestACL_init), // tp_init + RequestACL_init, // tp_init NULL, // tp_alloc PyType_GenericNew, // tp_new NULL, // tp_free @@ -214,27 +178,6 @@ initModulePart_RequestACL(PyObject* mod) { } Py_INCREF(&requestacl_type); -#if 0 // we probably don't have any class vars - // The following template is the typical procedure for installing class - // variables. If the class doesn't have a class variable, remove the - // entire try-catch clauses. - try { - // Constant class variables - installClassVariable(requestacl_type, "REPLACE_ME", - Py_BuildValue("REPLACE ME")); - } catch (const exception& ex) { - const string ex_what = - "Unexpected failure in RequestACL initialization: " + - string(ex.what()); - PyErr_SetString(po_IscException, ex_what.c_str()); - return (false); - } catch (...) { - PyErr_SetString(PyExc_SystemError, - "Unexpected failure in RequestACL initialization"); - return (false); - } -#endif - return (true); } } // namespace python diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index 95a3e9c4c9..c3a68ba151 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -93,7 +93,7 @@ class RequestContextTest(unittest.TestCase): class RequestACLTest(unittest.TestCase): def test_direct_construct(self): - acl = RequestACL() + self.assertRaises(TypeError, RequestACL) def test_request_loader(self): # these shouldn't raise an exception From 0252f1b276eaf8e72d42510546f594b9d0703a58 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 7 Jul 2011 00:51:39 -0700 Subject: [PATCH 113/974] [trac983] overall documentation updates. more cleanup. --- src/lib/python/isc/acl/Makefile.am | 2 + src/lib/python/isc/acl/dns.cc | 4 +- src/lib/python/isc/acl/dns_requestacl_inc.cc | 41 +++++-------------- .../python/isc/acl/dns_requestacl_python.cc | 1 - .../python/isc/acl/dns_requestcontext_inc.cc | 30 ++++++++++++++ .../isc/acl/dns_requestcontext_python.cc | 17 ++------ src/lib/python/isc/acl/dnsacl_inc.cc | 21 ++++++++++ 7 files changed, 71 insertions(+), 45 deletions(-) create mode 100644 src/lib/python/isc/acl/dns_requestcontext_inc.cc create mode 100644 src/lib/python/isc/acl/dnsacl_inc.cc diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index 5156429810..02b3460a35 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -28,3 +28,5 @@ acl_la_LIBADD += $(PYTHON_LIB) dns_la_LDFLAGS += -module dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la dns_la_LIBADD += $(PYTHON_LIB) + +EXTRA_DIST = dnsacl_inc.cc dns_requestacl_inc.cc dns_requestcontext_inc.cc diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc index 68190cd2b9..83d7642d2a 100644 --- a/src/lib/python/isc/acl/dns.cc +++ b/src/lib/python/isc/acl/dns.cc @@ -35,6 +35,8 @@ using namespace isc::data; using namespace isc::acl::dns; using namespace isc::acl::dns::python; +#include "dnsacl_inc.cc" + namespace { PyObject* loadRequestACL(PyObject*, PyObject* args) { @@ -63,7 +65,7 @@ loadRequestACL(PyObject*, PyObject* args) { } PyMethodDef methods[] = { - { "load_request_acl", loadRequestACL, METH_VARARGS, "TBD" }, + { "load_request_acl", loadRequestACL, METH_VARARGS, load_request_acl_doc }, { NULL, NULL, 0, NULL } }; diff --git a/src/lib/python/isc/acl/dns_requestacl_inc.cc b/src/lib/python/isc/acl/dns_requestacl_inc.cc index 1ff369d4d6..9637170b30 100644 --- a/src/lib/python/isc/acl/dns_requestacl_inc.cc +++ b/src/lib/python/isc/acl/dns_requestacl_inc.cc @@ -1,43 +1,24 @@ namespace { const char* const RequestACL_doc = "\ -The ACL itself.\n\ +The DNS Request ACL.\n\ \n\ -It holds bunch of ordered entries, each one consisting of a check ( of\n\ -any kind, it might be even compound) and an action that is returned\n\ -whenever the action matches. They are tested in the order and first\n\ -match counts.\n\ +It holds bunch of ordered entries, each one consisting of a check for\n\ +a given DNS Request context and an action, which is one of ACCEPT,\n\ +REJECT, or DROP, as defined in the isc.acl module.\n\ +The checks are tested in the order and first match counts.\n\ \n\ -This is non-copyable. It seems that there's no need to copy them (even\n\ -when it would be technically possible), so we forbid it just to\n\ -prevent copying it by accident. If there really is legitimate use,\n\ -this restriction can be removed.\n\ -\n\ -The class is template. It is possible to specify on which context the\n\ -checks match and which actions it returns. The actions must be\n\ -copyable for this to work and it is expected to be something small,\n\ -usually an enum (but other objects are also possible).\n\ -\n\ -There are protected functions. In fact, you should consider them\n\ -private, they are protected so tests can get inside. This class is not\n\ -expected to be subclassed in real applications.\n\ -\n\ -ACL(default_action)\n\ -\n\ - Constructor.\n\ -\n\ - Parameters:\n\ - default_action It is the action that is returned when the\n\ - checked things \"falls off\" the end of the list\n\ - (when no rule matched).\n\ +A RequestACL object cannot be constructed directly; an application\n\ +must use isc.acl.dns.load_request_acl() to create a RequestACL object.\n\ \n\ "; const char* const RequestACL_execute_doc = "\ -execute(context) -> Action \n\ +execute(context) -> action \n\ \n\ -The actual main function that decides.\n\ +The returned action is one of ACCEPT, REJECT or DROP as defined in\n\ +the isc.acl module.\n\ \n\ -This is the function that takes the entries one by one, checks the\n\ +This is the function that takes the ACL entries one by one, checks the\n\ context against conditions and if it matches, returns the action that\n\ belongs to the first matched entry or default action if nothing\n\ matches.\n\ diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc index 27dc6e0010..06eb068406 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.cc +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -163,7 +163,6 @@ PyTypeObject requestacl_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here bool initModulePart_RequestACL(PyObject* mod) { // We initialize the static description object with PyType_Ready(), diff --git a/src/lib/python/isc/acl/dns_requestcontext_inc.cc b/src/lib/python/isc/acl/dns_requestcontext_inc.cc new file mode 100644 index 0000000000..9e80e1ff3b --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestcontext_inc.cc @@ -0,0 +1,30 @@ +namespace { +const char* const RequestContext_doc = "\ +DNS request to be checked.\n\ +\n\ +This plays the role of ACL context for the RequestACL object.\n\ +\n\ +Based on the minimalist philosophy, the initial implementation only\n\ +maintains the remote (source) IP address of the request. The plan is\n\ +to add more parameters of the request. A scheduled next step is to\n\ +support the TSIG key (if it's included in the request). Other\n\ +possibilities are the local (destination) IP address, the remote and\n\ +local port numbers, various fields of the DNS request (e.g. a\n\ +particular header flag value).\n\ +\n\ +RequestContext(remote_address)\n\ +\n\ + In this initial implementation, the constructor only takes a\n\ + remote IP address in the form of a socket address as used in the\n\ + Python socket module.\n\ +\n\ + Exceptions:\n\ + isc.acl.ACLError Normally shouldn't happen, but still possible\n\ + for unexpected errors such as memory allocation\n\ + failure or an invalid address text being passed.\n\ +\n\ + Parameters:\n\ + remote_address The remote IP address\n\ +\n\ +"; +} // unnamed namespace diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.cc b/src/lib/python/isc/acl/dns_requestcontext_python.cc index 9b724cfcc7..51ff9cd687 100644 --- a/src/lib/python/isc/acl/dns_requestcontext_python.cc +++ b/src/lib/python/isc/acl/dns_requestcontext_python.cc @@ -116,18 +116,10 @@ private: s_RequestContext::s_RequestContext() : cppobj(NULL), data_(NULL) { } +// Import pydoc text +#include "dns_requestcontext_inc.cc" + namespace { -// Shortcut type which would be convenient for adding class variables safely. -typedef CPPPyObjectContainer RequestContextContainer; - -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// - -// These are the functions we export -// For a minimal support, we don't need them. - // This list contains the actual set of functions we have in // python. Each entry has // 1. Python method name @@ -265,7 +257,7 @@ PyTypeObject requestcontext_type = { NULL, // tp_setattro NULL, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - "The RequestContext class objects is...(COMPLETE THIS)", + RequestContext_doc, NULL, // tp_traverse NULL, // tp_clear NULL, // tp_richcompare @@ -294,7 +286,6 @@ PyTypeObject requestcontext_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here bool initModulePart_RequestContext(PyObject* mod) { // We initialize the static description object with PyType_Ready(), diff --git a/src/lib/python/isc/acl/dnsacl_inc.cc b/src/lib/python/isc/acl/dnsacl_inc.cc new file mode 100644 index 0000000000..f68e193c10 --- /dev/null +++ b/src/lib/python/isc/acl/dnsacl_inc.cc @@ -0,0 +1,21 @@ +namespace { +const char* const load_request_acl_doc = "\ +load_request_acl(description) -> RequestACL\n\ +\n\ +Load a DNS ACL.\n\ +\n\ +This parses an ACL list, creates internal data for each rule\n\ +and returns a RequestACl object that contains all given rules.\n\ +\n\ +Exceptions:\n\ + LoaderError Load failed. The most likely cause of this is a syntax\n\ + error in the description. Other internal errors such as\n\ + memory allocation failure is also converted to this\n\ + exception.\n\ +\n\ +Parameters:\n\ + description String representation of the JSON list of ACL.\n\ +\n\ +Return Value(s): The newly created RequestACL object\n\ +"; +} // unnamed namespace From 0529433796c0024e9345edd3c458e22e1aec9043 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 7 Jul 2011 10:26:23 +0200 Subject: [PATCH 114/974] [trac1093] argument order problem also fixed usage output and doxygen description --- src/lib/log/compiler/message.cc | 8 +++++--- src/lib/python/isc/config/Makefile.am | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc index 872921c133..e2807289b7 100644 --- a/src/lib/log/compiler/message.cc +++ b/src/lib/log/compiler/message.cc @@ -55,13 +55,15 @@ static const char* VERSION = "1.0-0"; /// \b Invocation
/// The program is invoked with the command: /// -/// message [-v | -h | \] +/// message [-v | -h | -p | -d | \] /// /// It reads the message file and writes out two files of the same name in the /// default directory but with extensions of .h and .cc. /// /// \-v causes it to print the version number and exit. \-h prints a help -/// message (and exits). +/// message (and exits). -p sets the output to python. -d will make +/// it write the output file(s) to dir instead of current working +/// directory /// \brief Print Version @@ -80,7 +82,7 @@ version() { void usage() { cout << - "Usage: message [-h] [-v] [-p] \n" << + "Usage: message [-h] [-v] [-p] [-d dir] \n" << "\n" << "-h Print this message and exit\n" << "-v Print the program version and exit\n" << diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am index ca9e7e04f2..185d1015d8 100644 --- a/src/lib/python/isc/config/Makefile.am +++ b/src/lib/python/isc/config/Makefile.am @@ -10,7 +10,7 @@ cfgmgr_messages.py: cfgmgr_messages.mes $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes $(top_builddir)/src/lib/python/config_messages.py: config_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/config_messages.mes -d $(top_builddir)/src/lib/python + $(top_builddir)/src/lib/log/compiler/message -p -d $(top_builddir)/src/lib/python $(top_srcdir)/src/lib/python/isc/config/config_messages.mes CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc CLEANFILES += $(top_builddir)/src/lib/python/config_messages.py From 551a2d7f6ed5744170265ea5bc7b99690b58a6f5 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 7 Jul 2011 10:40:58 +0200 Subject: [PATCH 115/974] [trac1093] same argument order thingy for lib/notify --- src/lib/python/isc/notify/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/notify/Makefile.am b/src/lib/python/isc/notify/Makefile.am index a3a7dcfa8d..851d2746cb 100644 --- a/src/lib/python/isc/notify/Makefile.am +++ b/src/lib/python/isc/notify/Makefile.am @@ -6,7 +6,7 @@ pyexec_DATA = $(top_builddir)/src/lib/python/notify_out_messages.py pythondir = $(pyexecdir)/isc/notify $(top_builddir)/src/lib/python/notify_out_messages.py: notify_out_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/notify/notify_out_messages.mes -d $(top_builddir)/src/lib/python + $(top_builddir)/src/lib/log/compiler/message -p -d $(top_builddir)/src/lib/python $(top_srcdir)/src/lib/python/isc/notify/notify_out_messages.mes EXTRA_DIST = notify_out_messages.mes From d77fde1d390e921740df739699dc03b48777f81a Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 7 Jul 2011 11:39:45 +0200 Subject: [PATCH 116/974] [trac741] Address review comments --- src/lib/cache/cache_messages.mes | 15 ++++++++------- src/lib/cache/rrset_cache.cc | 8 ++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 75dcfb5794..513384a507 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -41,7 +41,7 @@ nothing. Debug message. We found the whole message in the cache, so it can be returned to user without any other lookups. -% CACHE_MESSAGES_INIT initialized message cache for %1 %2 messages +% CACHE_MESSAGES_INIT initialized message cache for %1 messages of class %2 Debug message issued when a new message cache is issued. It lists the class of messages it can hold and the maximum size of the cache. @@ -50,8 +50,9 @@ Debug message. This may follow CACHE_MESSAGES_UPDATE and indicates that, while updating, the old instance is being removed prior of inserting a new one. % CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3 -Debug message, noting that the given message can not be cached. This is for -some reason described in RFC2308. +Debug message, noting that the given message can not be cached. This is because +there's no SOA record in the message. See RFG 2308 section 5 for more +information. % CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache Debug message. The message cache didn't find any entry for the given key. @@ -119,18 +120,18 @@ Debug message. While trying to insert an RRset into the cache, it was discovered that there's no cache for the class of the RRset. Therefore the message will not be cached. -% CACHE_RRSET_EXPIRED found expired RRset %1/%2 -Debug message. There' the data requested in the RRset cache. However, it is +% CACHE_RRSET_EXPIRED found expired RRset %1/%2/%3 +Debug message. The requested data was found in the RRset cache. However, it is expired, so the cache removed it and is going to pretend nothing was found. % CACHE_RRSET_INIT initializing RRset cache for %2 RRsets of class %1 Debug message. The RRset cache to hold at most this many RRsets for the given class is being created. -% CACHE_RRSET_LOOKUP looking up %1/%2 in RRset cache +% CACHE_RRSET_LOOKUP looking up %1/%2/%3 in RRset cache Debug message. The resolver is trying to look up data in the RRset cache. -% CACHE_RRSET_NOT_FOUND no RRset found for %1/%2 +% CACHE_RRSET_NOT_FOUND no RRset found for %1/%2/%3 Debug message which can follow CACHE_RRSET_LOOKUP. This means the data is not in the cache. diff --git a/src/lib/cache/rrset_cache.cc b/src/lib/cache/rrset_cache.cc index 4a77d48ed8..1a5fd48dc5 100644 --- a/src/lib/cache/rrset_cache.cc +++ b/src/lib/cache/rrset_cache.cc @@ -36,7 +36,7 @@ RRsetCache::RRsetCache(uint32_t cache_size, new HashDeleter(rrset_table_)) { LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_RRSET_INIT).arg(cache_size). - arg(rrset_class); + arg(RRClass(rrset_class)); } RRsetEntryPtr @@ -44,7 +44,7 @@ RRsetCache::lookup(const isc::dns::Name& qname, const isc::dns::RRType& qtype) { LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_LOOKUP).arg(qname). - arg(qtype); + arg(qtype).arg(RRClass(class_)); const string entry_name = genCacheEntryName(qname, qtype); RRsetEntryPtr entry_ptr = rrset_table_.get(HashKey(entry_name, @@ -56,7 +56,7 @@ RRsetCache::lookup(const isc::dns::Name& qname, return (entry_ptr); } else { LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_EXPIRED).arg(qname). - arg(qtype); + arg(qtype).arg(RRClass(class_)); // the rrset entry has expired, so just remove it from // hash table and lru list. rrset_table_.remove(entry_ptr->hashKey()); @@ -65,7 +65,7 @@ RRsetCache::lookup(const isc::dns::Name& qname, } LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_NOT_FOUND).arg(qname). - arg(qtype); + arg(qtype).arg(RRClass(class_)); return (RRsetEntryPtr()); } From e12a932eadf0b33e26979cfbf387eb6788b97cad Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 7 Jul 2011 11:54:56 +0200 Subject: [PATCH 117/974] [trac981] Rename LogicCheck to LogicOperator Just for consistency with LogicOperator --- src/lib/acl/logic_check.h | 6 +++--- src/lib/acl/tests/logic_check_test.cc | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/acl/logic_check.h b/src/lib/acl/logic_check.h index fe16f7f9be..92441e8969 100644 --- a/src/lib/acl/logic_check.h +++ b/src/lib/acl/logic_check.h @@ -206,14 +206,14 @@ private: * This simply returns the negation of whatever returns the subexpression. */ template -class NotCheck : public CompoundCheck { +class NotOperator : public CompoundCheck { public: /** * \brief Constructor * * \param expr The subexpression to be negated by this NOT. */ - NotCheck(const boost::shared_ptr >& expr) : + NotOperator(const boost::shared_ptr >& expr) : expr_(expr) { } /** @@ -265,7 +265,7 @@ public: const Loader& loader) { - return (boost::shared_ptr >(new NotCheck( + return (boost::shared_ptr >(new NotOperator( loader.loadCheck(definition)))); } /** diff --git a/src/lib/acl/tests/logic_check_test.cc b/src/lib/acl/tests/logic_check_test.cc index 1d1979a612..1c80277a2b 100644 --- a/src/lib/acl/tests/logic_check_test.cc +++ b/src/lib/acl/tests/logic_check_test.cc @@ -244,7 +244,7 @@ TEST_F(LogicCreatorTest, nested) { } void notTest(bool value) { - NotCheck notOp(shared_ptr >(new ConstCheck(value, 0))); + NotOperator notOp(shared_ptr >(new ConstCheck(value, 0))); Log log; // It returns negated value EXPECT_EQ(!value, notOp.matches(log)); @@ -281,9 +281,9 @@ TEST_F(LogicCreatorTest, notInvalid) { } TEST_F(LogicCreatorTest, notValid) { - shared_ptr > notOp(load >("{\"NOT\":" - " {\"logcheck\":" - " [0, true]}}")); + shared_ptr > notOp(load >("{\"NOT\":" + " {\"logcheck\":" + " [0, true]}}")); EXPECT_FALSE(notOp->matches(log_)); log_.checkFirst(1); } From 7c7238ca556654cd2a0483dab5e7478fa7956a88 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 7 Jul 2011 11:38:39 +0100 Subject: [PATCH 118/974] [trac1025] Handle possible bad_lexical_cast exception in Formatter::arg() --- src/lib/log/log_formatter.h | 32 +++++++++++++++++++++++++++++++- src/lib/log/tests/Makefile.am | 2 ++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h index 11dec84332..ca23844f49 100644 --- a/src/lib/log/log_formatter.h +++ b/src/lib/log/log_formatter.h @@ -18,12 +18,28 @@ #include #include #include + +#include #include #include namespace isc { namespace log { +/// \brief Format Failure +/// +/// This exception is used to wrap a bad_lexical_cast exception thrown during +/// formatting an argument. + +class FormatFailure : public isc::Exception { +public: + FormatFailure(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) + {} +}; + + +/// /// \brief The internal replacement routine /// /// This is used internally by the Formatter. Replaces a placeholder @@ -156,7 +172,21 @@ public: /// \param arg The argument to place into the placeholder. template Formatter& arg(const Arg& value) { if (logger_) { - return (arg(boost::lexical_cast(value))); + try { + return (arg(boost::lexical_cast(value))); + } catch (const boost::bad_lexical_cast& ex) { + + // A bad_lexical_cast during a conversion to a string is + // *extremely* unlikely to fail. However, there is nothing + // in the documentation that rules it out, so we need to handle + // it. As it is a potentially very serious problem, throw the + // exception detailing the problem with as much information as + // we can. (Note that this does not include 'value' - + // boost::lexical_cast failed to convert it to a string, so an + // attempt to do so here would probably fail as well.) + isc_throw(FormatFailure, "bad_lexical_cast in call to " + "Formatter::arg(): " << ex.what()); + } } else { return (*this); } diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am index 6159d6303a..069a7b4218 100644 --- a/src/lib/log/tests/Makefile.am +++ b/src/lib/log/tests/Makefile.am @@ -51,6 +51,7 @@ logger_example_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) logger_example_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS) logger_example_LDADD = $(top_builddir)/src/lib/log/liblog.la logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la +logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la check_PROGRAMS += init_logger_test init_logger_test_SOURCES = init_logger_test.cc @@ -58,6 +59,7 @@ init_logger_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) init_logger_test_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS) init_logger_test_LDADD = $(top_builddir)/src/lib/log/liblog.la init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la +init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la noinst_PROGRAMS = $(TESTS) From 6c9695dac3a16574ff3e7d0d310cff3df6d542f6 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 6 Jul 2011 15:38:21 +0200 Subject: [PATCH 119/974] [trac763] extend skeleton lib with lib/log we should really get rid of that complete skeleton lib, not only is it in danger of getting stale, it is also fragile on changes in the libs --- src/bin/stats/stats.py.in | 2 ++ src/bin/stats/tests/b10-stats_test.py | 8 +++--- src/bin/stats/tests/isc/log/__init__.py | 33 +++++++++++++++++++++++ src/bin/stats/tests/isc/log/__init__.pyc | Bin 0 -> 414 bytes 4 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/bin/stats/tests/isc/log/__init__.py create mode 100644 src/bin/stats/tests/isc/log/__init__.pyc diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 969676e13b..9c1de98bdd 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -29,6 +29,8 @@ from isc.cc import Session, SessionError import isc.util.process isc.util.process.rename() +import isc.log + # If B10_FROM_SOURCE is set in the environment, we use data files # from a directory relative to that, otherwise we use the ones # installed on the system diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index eccabdcbb0..6c0b392476 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -31,11 +31,11 @@ stats.gmtime = gmtime from stats import SessionSubject, CCSessionListener, get_timestamp, get_datetime from fake_time import _TEST_TIME_SECS, _TEST_TIME_STRF -# setting Constant -if sys.path[0] == '': - TEST_SPECFILE_LOCATION = "./testdata/stats_test.spec" +if "B10_FROM_SOURCE" in os.environ: + TEST_SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] +\ + "/src/bin/stats/tests/testdata/stats_test.spec" else: - TEST_SPECFILE_LOCATION = sys.path[0] + "/testdata/stats_test.spec" + TEST_SPECFILE_LOCATION = "./testdata/stats_test.spec" class TestStats(unittest.TestCase): diff --git a/src/bin/stats/tests/isc/log/__init__.py b/src/bin/stats/tests/isc/log/__init__.py new file mode 100644 index 0000000000..641cf790c1 --- /dev/null +++ b/src/bin/stats/tests/isc/log/__init__.py @@ -0,0 +1,33 @@ +# 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 file is not installed. The log.so is installed into the right place. +# It is only to find it in the .libs directory when we run as a test or +# from the build directory. +# But as nobody gives us the builddir explicitly (and we can't use generation +# from .in file, as it would put us into the builddir and we wouldn't be found) +# we guess from current directory. Any idea for something better? This should +# be enough for the tests, but would it work for B10_FROM_SOURCE as well? +# Should we look there? Or define something in bind10_config? + +import os +import sys + +for base in sys.path[:]: + loglibdir = os.path.join(base, 'isc/log/.libs') + if os.path.exists(loglibdir): + sys.path.insert(0, loglibdir) + +from log import * diff --git a/src/bin/stats/tests/isc/log/__init__.pyc b/src/bin/stats/tests/isc/log/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab19ab23b1582e21494a677e243d1580a111910c GIT binary patch literal 414 zcmYk1K}!QM5QSf|ZLQLJkb0=UV3E@3U8JDrdQeanWNCK?n`OIOcM@oiQvBbYELIJZ zmoJmdo8)OQJWI0CJj1`y6Q0TBFOghAp#+j&R!9`KZy<$2hf|4fjq&6A1Cj`YkS;o) z2)V{yBk3NKJx~{Wg=X?oU7UmtA*rE0Cy;mzEhChDyom90+>uvUnV9=wK+a5Af0$`$ zmmDj}_#oHuX+SMfvyWuq9s5wbS-4dT%LP*lMQtk|u8WVjv4r6jVAg4Q*;ROIzi)H-?)mpkcRgY#fQDIlehfZDn1WPB%N>7n8en Xo%@pR$HIIrGEf6OLQh?)3w5OrOR7-h literal 0 HcmV?d00001 From 9dec49291c2ccefab6cf97b9146c292897783c5a Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 6 Jul 2011 15:54:51 +0200 Subject: [PATCH 120/974] [(no branch)] [trac763] add messages file --- configure.ac | 1 + src/bin/stats/Makefile.am | 6 ++++++ src/bin/stats/stats_messages.mes | 17 +++++++++++++++++ src/bin/stats/tests/isc/Makefile.am | 2 +- 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/bin/stats/stats_messages.mes diff --git a/configure.ac b/configure.ac index 8bd37a2347..0253e3f25a 100644 --- a/configure.ac +++ b/configure.ac @@ -793,6 +793,7 @@ AC_CONFIG_FILES([Makefile src/bin/stats/tests/isc/cc/Makefile src/bin/stats/tests/isc/config/Makefile src/bin/stats/tests/isc/util/Makefile + src/bin/stats/tests/isc/log/Makefile src/bin/stats/tests/testdata/Makefile src/bin/stats/tests/http/Makefile src/bin/usermgr/Makefile diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index c8b18c9d2e..3751d8e7fe 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -7,14 +7,17 @@ pkglibexec_SCRIPTS = b10-stats b10-stats-httpd b10_statsdir = $(pkgdatadir) b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl +pyexec_DATA = stats_messages.py CLEANFILES = b10-stats stats.pyc CLEANFILES += b10-stats-httpd stats_httpd.pyc +CLEANFILES += stats_messages.py stats_messages.pyc man_MANS = b10-stats.8 b10-stats-httpd.8 EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl +EXTRA_DIST += stats_messages.mes if ENABLE_MAN @@ -26,6 +29,9 @@ b10-stats-httpd.8: b10-stats-httpd.xml endif +stats_messages.py: stats_messages.mes + $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/stats/stats_messages.mes + # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix b10-stats: stats.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats.py >$@ diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes new file mode 100644 index 0000000000..fbe4a4c191 --- /dev/null +++ b/src/bin/stats/stats_messages.mes @@ -0,0 +1,17 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# No namespace declaration - these constants go in the global namespace +# of the stats_messages python module. + diff --git a/src/bin/stats/tests/isc/Makefile.am b/src/bin/stats/tests/isc/Makefile.am index bfad7e306a..d31395d404 100644 --- a/src/bin/stats/tests/isc/Makefile.am +++ b/src/bin/stats/tests/isc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = cc config util +SUBDIRS = cc config util log EXTRA_DIST = __init__.py CLEANFILES = __init__.pyc From a0209aebd72ab6ec63941d5881b58a3c689b943f Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 6 Jul 2011 17:12:26 +0200 Subject: [PATCH 121/974] [(no branch)] [trac763] log messages for stats.py(.in) --- src/bin/stats/stats.py.in | 56 +++++++++++++++++--------------- src/bin/stats/stats_messages.mes | 55 +++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 26 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 9c1de98bdd..13bdab72ad 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -25,12 +25,19 @@ from collections import defaultdict from isc.config.ccsession import ModuleCCSession, create_answer from isc.cc import Session, SessionError +import isc.log +from stats_messages import * + +isc.log.init("b10-stats") +logger = isc.log.Logger("stats") + +# Some constants for debug levels +DBG_STATS_MESSAGING = 30 + # for setproctitle import isc.util.process isc.util.process.rename() -import isc.log - # If B10_FROM_SOURCE is set in the environment, we use data files # from a directory relative to that, otherwise we use the ones # installed on the system @@ -205,8 +212,7 @@ class CCSessionListener(Listener): kwargs = self.initialize_data(cmd["command_args"]) self.add_event(Callback(name=name, callback=callback, args=(), kwargs=kwargs)) except AttributeError as ae: - sys.stderr.write("[b10-stats] Caught undefined command while parsing spec file: " - +str(cmd["command_name"])+"\n") + logger.error(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) def start(self): """ @@ -219,8 +225,7 @@ class CCSessionListener(Listener): self.stats_data['stats.lname'] = self.session.lname self.cc_session.start() # request Bob to send statistics data - if self.verbose: - sys.stdout.write("[b10-stats] request Bob to send statistics data\n") + logger.debug(DBG_STATS_MESSAGING, STATS_SEND_STATS_REQUEST_BOSS) cmd = isc.config.ccsession.create_command("sendstats", None) seq = self.session.group_sendmsg(cmd, 'Boss') self.session.group_recvmsg(True, seq) @@ -241,8 +246,8 @@ class CCSessionListener(Listener): """ handle a configure from the cc channel """ - if self.verbose: - sys.stdout.write("[b10-stats] newconfig received: "+str(new_config)+"\n") + logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_NEW_CONFIG, + new_config) # do nothing currently return create_answer(0) @@ -264,8 +269,7 @@ class CCSessionListener(Listener): """ handle shutdown command """ - if self.verbose: - sys.stdout.write("[b10-stats] 'shutdown' command received\n") + logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND) self.subject.running = False return create_answer(0) @@ -285,13 +289,14 @@ class CCSessionListener(Listener): """ handle remove command """ - if self.verbose: - sys.stdout.write("[b10-stats] 'remove' command received, args: "+str(args)+"\n") # 'args' must be dictionary type if args and args['stats_item_name'] in self.stats_data: stats_item_name = args['stats_item_name'] + logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_REMOVE_COMMAND, + stats_item_name) + # just remove one item self.stats_data.pop(stats_item_name) @@ -301,8 +306,6 @@ class CCSessionListener(Listener): """ handle show command """ - if self.verbose: - sys.stdout.write("[b10-stats] 'show' command received, args: "+str(args)+"\n") # always overwrite 'report_time' and 'stats.timestamp' # if "show" command invoked @@ -312,16 +315,21 @@ class CCSessionListener(Listener): # if with args if args and args['stats_item_name'] in self.stats_data: stats_item_name = args['stats_item_name'] + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOW_NAME_COMMAND, + stats_item_name) return create_answer(0, {stats_item_name: self.stats_data[stats_item_name]}) + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOW_ALL_COMMAND) return create_answer(0, self.stats_data) def command_reset(self, args): """ handle reset command """ - if self.verbose: - sys.stdout.write("[b10-stats] 'reset' command received\n") + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_RESET_COMMAND) # re-initialize internal variables self.stats_data = self.initialize_data(self.stats_spec) @@ -338,8 +346,7 @@ class CCSessionListener(Listener): """ handle status command """ - if self.verbose: - sys.stdout.write("[b10-stats] 'status' command received\n") + logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND) # just return "I'm alive." return create_answer(0, "I'm alive.") @@ -347,9 +354,7 @@ class CCSessionListener(Listener): """ handle an unknown command """ - if self.verbose: - sys.stdout.write("[b10-stats] Unknown command received: '" - + str(command) + "'\n") + logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command) return create_answer(1, "Unknown command: '"+str(command)+"'") @@ -403,13 +408,12 @@ def main(session=None): subject.check() subject.stop() - except OptionValueError: - sys.stderr.write("[b10-stats] Error parsing options\n") + except OptionValueError as ove: + logger.fatal(STATS_BAD_OPTION_VALUE, ove) except SessionError as se: - sys.stderr.write("[b10-stats] Error creating Stats module, " - + "is the command channel daemon running?\n") + logger.fatal(STATS_CC_SESSION_ERROR, se) except KeyboardInterrupt as kie: - sys.stderr.write("[b10-stats] Interrupted, exiting\n") + logger.info(STATS_STOPPED_BY_KEYBOARD) if __name__ == "__main__": main() diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes index fbe4a4c191..5d839c8361 100644 --- a/src/bin/stats/stats_messages.mes +++ b/src/bin/stats/stats_messages.mes @@ -15,3 +15,58 @@ # No namespace declaration - these constants go in the global namespace # of the stats_messages python module. +% STATS_BAD_OPTION_VALUE bad command line argument: %1 +The stats module was called with a bad command-line argument, and will +not start. + +% STATS_RECEIVED_NEW_CONFIG received new configuration: %1 +This debug message is printed when the configuration manager has sent +the stats module a configuration update. + +% STATS_RECEIVED_REMOVE_COMMAND received command to remove %1 +A remove command for the given name was sent to the stats module, and +it will now be removed. + +% STATS_RECEIVED_RESET_COMMAND received command to reset all statistics +The stats module received a command to reset all collected statistics. + +% STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics +The stats module received a command to show all statistics that it has +collected. + +% STATS_RECEIVED_SHOW_NAME_COMMAND received command to show statistics for %1 +The stats module received a command to show the statistics that it has +collected for the given item. + +% STATS_RECEIVED_SHUTDOWN_COMMAND shutdown command received +A shutdown command was sent to the stats module, and it will now shut down. + +% STATS_RECEIVED_STATUS_COMMAND received command to return status +A status command was sent to the stats module, and it will respond with +'I'm alive'. + +% STATS_RECEIVED_UNKNOWN_COMMAND received unknown command: %1 +An unknown command has been sent to the stats module. The stats module +will respond with an error, and the command will be ignored. + +% STATS_SEND_STATS_REQUEST_BOSS requesting boss to send statistics data +This debug message is printed when a request is sent to the boss module +to send statistics data to the stats module. + +% STATS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down +There was a keyboard interrupt signal to stop the stats daemon. The +daemon will now shut down. + +% STATS_CC_SESSION_ERROR error connecting to message bus: %1 +The stats module was unable to connect to the BIND 10 command and +control channel. A likely problem is that the message bus daemon +(b10-msgq) is not running. The stats module will now shut down. + +% STATS_UNKNOWN_COMMAND_IN_SPEC unknown command in specification file: %1 +The specification file for the stats module contains a command that +is unknown in the implementation. The most likely cause is an +installation problem, where the specification file stats.spec is +from a different version of BIND 10 than the stats module itself. +Please check your installation. + + From 469bda3f72a097af3dd1bde56d757d7ea916d996 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 7 Jul 2011 11:09:07 +0200 Subject: [PATCH 122/974] [(no branch)] [trac763] add missing makefile, remove accidental pyc file --- src/bin/stats/tests/isc/log/Makefile.am | 7 +++++++ src/bin/stats/tests/isc/log/__init__.pyc | Bin 414 -> 0 bytes 2 files changed, 7 insertions(+) create mode 100644 src/bin/stats/tests/isc/log/Makefile.am delete mode 100644 src/bin/stats/tests/isc/log/__init__.pyc diff --git a/src/bin/stats/tests/isc/log/Makefile.am b/src/bin/stats/tests/isc/log/Makefile.am new file mode 100644 index 0000000000..457b9de1c2 --- /dev/null +++ b/src/bin/stats/tests/isc/log/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = __init__.py +CLEANFILES = __init__.pyc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/log/__init__.pyc b/src/bin/stats/tests/isc/log/__init__.pyc deleted file mode 100644 index ab19ab23b1582e21494a677e243d1580a111910c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 414 zcmYk1K}!QM5QSf|ZLQLJkb0=UV3E@3U8JDrdQeanWNCK?n`OIOcM@oiQvBbYELIJZ zmoJmdo8)OQJWI0CJj1`y6Q0TBFOghAp#+j&R!9`KZy<$2hf|4fjq&6A1Cj`YkS;o) z2)V{yBk3NKJx~{Wg=X?oU7UmtA*rE0Cy;mzEhChDyom90+>uvUnV9=wK+a5Af0$`$ zmmDj}_#oHuX+SMfvyWuq9s5wbS-4dT%LP*lMQtk|u8WVjv4r6jVAg4Q*;ROIzi)H-?)mpkcRgY#fQDIlehfZDn1WPB%N>7n8en Xo%@pR$HIIrGEf6OLQh?)3w5OrOR7-h From 5599a9aa3b735b42a4a726b79f0c85a0d38eb5da Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 7 Jul 2011 12:30:20 +0200 Subject: [PATCH 123/974] [(no branch)] [trac763] log messages for stats-httpd --- src/bin/stats/Makefile.am | 8 ++- src/bin/stats/stats.py.in | 3 +- src/bin/stats/stats_httpd.py.in | 72 +++++++++++----------- src/bin/stats/stats_httpd_messages.mes | 85 ++++++++++++++++++++++++++ src/bin/stats/stats_messages.mes | 10 +-- 5 files changed, 135 insertions(+), 43 deletions(-) create mode 100644 src/bin/stats/stats_httpd_messages.mes diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index 3751d8e7fe..e830f65d60 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -7,17 +7,18 @@ pkglibexec_SCRIPTS = b10-stats b10-stats-httpd b10_statsdir = $(pkgdatadir) b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl -pyexec_DATA = stats_messages.py +pyexec_DATA = stats_messages.py stats_httpd_messages.py CLEANFILES = b10-stats stats.pyc CLEANFILES += b10-stats-httpd stats_httpd.pyc CLEANFILES += stats_messages.py stats_messages.pyc +CLEANFILES += stats_httpd_messages.py stats_httpd_messages.pyc man_MANS = b10-stats.8 b10-stats-httpd.8 EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl -EXTRA_DIST += stats_messages.mes +EXTRA_DIST += stats_messages.mes stats_httpd_messages.mes if ENABLE_MAN @@ -32,6 +33,9 @@ endif stats_messages.py: stats_messages.mes $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/stats/stats_messages.mes +stats_httpd_messages.py: stats_httpd_messages.mes + $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/stats/stats_httpd_messages.mes + # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix b10-stats: stats.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats.py >$@ diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 13bdab72ad..c415826a2e 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -31,7 +31,8 @@ from stats_messages import * isc.log.init("b10-stats") logger = isc.log.Logger("stats") -# Some constants for debug levels +# Some constants for debug levels, these should be removed when we +# have #1074 DBG_STATS_MESSAGING = 30 # for setproctitle diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index a6fd066788..f0228466d7 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -34,6 +34,17 @@ import isc.cc import isc.config import isc.util.process +import isc.log +from stats_httpd_messages import * + +isc.log.init("b10-stats-httpd") +logger = isc.log.Logger("stats-httpd") + +# Some constants for debug levels, these should be removed when we +# have #1074 +DBG_STATS_HTTPD_INIT = 10 +DBG_STATS_HTTPD_MESSAGING = 30 + # If B10_FROM_SOURCE is set in the environment, we use data files # from a directory relative to that, otherwise we use the ones # installed on the system @@ -169,8 +180,7 @@ class StatsHttpd: def open_mccs(self): """Opens a ModuleCCSession object""" # create ModuleCCSession - if self.verbose: - self.write_log("[b10-stats-httpd] Starting CC Session\n") + logger.debug(DBG_STATS_HTTPD_INIT, STATS_HTTPD_STARTING_CC_SESSION) self.mccs = isc.config.ModuleCCSession( SPECFILE_LOCATION, self.config_handler, self.command_handler) self.cc_session = self.mccs._session @@ -183,8 +193,8 @@ class StatsHttpd: """Closes a ModuleCCSession object""" if self.mccs is None: return - if self.verbose: - self.write_log("[b10-stats-httpd] Closing CC Session\n") + + logger.debug(DBG_STATS_HTTPD_INIT, STATS_HTTPD_CLOSING_CC_SESSION) self.mccs.close() self.mccs = None @@ -233,10 +243,8 @@ class StatsHttpd: (server_address[0], server_address[1], err.__class__.__name__, err)) else: - if self.verbose: - self.write_log( - "[b10-stats-httpd] Started on address %s, port %s\n" % - server_address) + logger.info(STATS_HTTPD_STARTED, server_address[0], + server_address[1]) return httpd def close_httpd(self): @@ -244,11 +252,8 @@ class StatsHttpd: if len(self.httpd) == 0: return for ht in self.httpd: - if self.verbose: - self.write_log( - "[b10-stats-httpd] Closing address %s, port %s\n" % - (ht.server_address[0], ht.server_address[1]) - ) + logger.info(STATS_HTTPD_CLOSING, ht.server_address[0], + ht.server_address[1]) ht.server_close() self.httpd = [] @@ -285,8 +290,7 @@ class StatsHttpd: def stop(self): """Stops the running StatsHttpd objects. Closes CC session and HTTP handling sockets""" - if self.verbose: - self.write_log("[b10-stats-httpd] Shutting down\n") + logger.info(STATS_HTTPD_SHUTDOWN) self.close_httpd() self.close_mccs() @@ -303,13 +307,11 @@ class StatsHttpd: def config_handler(self, new_config): """Config handler for the ModuleCCSession object. It resets addresses and ports to listen HTTP requests on.""" - if self.verbose: - self.write_log("[b10-stats-httpd] Loading config : %s\n" % str(new_config)) + logger.debug(DBG_STATS_HTTPD_MESSAGING, STATS_HTTPD_HANDLE_CONFIG, + new_config) for key in new_config.keys(): if key not in DEFAULT_CONFIG: - if self.verbose: - self.write_log( - "[b10-stats-httpd] Unknown known config: %s" % key) + logger.error(STATS_HTTPD_UNKNOWN_CONFIG_ITEM, key) return isc.config.ccsession.create_answer( 1, "Unknown known config: %s" % key) # backup old config @@ -319,9 +321,7 @@ class StatsHttpd: try: self.open_httpd() except HttpServerError as err: - if self.verbose: - self.write_log("[b10-stats-httpd] %s\n" % err) - self.write_log("[b10-stats-httpd] Restoring old config\n") + logger.error(STATS_HTTPD_SERVER_ERROR, err) # restore old config self.config_handler(old_config) return isc.config.ccsession.create_answer( @@ -333,19 +333,19 @@ class StatsHttpd: """Command handler for the ModuleCCSesson object. It handles "status" and "shutdown" commands.""" if command == "status": - if self.verbose: - self.write_log("[b10-stats-httpd] Received 'status' command\n") + logger.debug(DBG_STATS_HTTPD_MESSAGING, + STATS_HTTPD_RECEIVED_STATUS_COMMAND) return isc.config.ccsession.create_answer( 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")") elif command == "shutdown": - if self.verbose: - self.write_log("[b10-stats-httpd] Received 'shutdown' command\n") + logger.debug(DBG_STATS_HTTPD_MESSAGING, + STATS_HTTPD_RECEIVED_SHUTDOWN_COMMAND) self.running = False return isc.config.ccsession.create_answer( 0, "Stats Httpd is shutting down.") else: - if self.verbose: - self.write_log("[b10-stats-httpd] Received unknown command\n") + logger.debug(DBG_STATS_HTTPD_MESSAGING, + STATS_HTTPD_RECEIVED_UNKNOWN_COMMAND, command) return isc.config.ccsession.create_answer( 1, "Unknown command: " + str(command)) @@ -481,12 +481,14 @@ if __name__ == "__main__": (options, args) = parser.parse_args() stats_httpd = StatsHttpd(verbose=options.verbose) stats_httpd.start() - except OptionValueError: - sys.exit("[b10-stats-httpd] Error parsing options") + except OptionValueError as ove: + logger.fatal(STATS_HTTPD_BAD_OPTION_VALUE, ove) + sys.exit(1) except isc.cc.session.SessionError as se: - sys.exit("[b10-stats-httpd] Error creating module, " - + "is the command channel daemon running?") + logger.fatal(STATS_HTTPD_CC_SESSION_ERROR, se) + sys.exit(1) except HttpServerError as hse: - sys.exit("[b10-stats-httpd] %s" % hse) + logger.fatal(STATS_HTTPD_START_SERVER_ERROR, hse) + sys.exit(1) except KeyboardInterrupt as kie: - sys.exit("[b10-stats-httpd] Interrupted, exiting") + logger.info(STATS_HTTPD_STOPPED_BY_KEYBOARD) diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes new file mode 100644 index 0000000000..b23fbd0d67 --- /dev/null +++ b/src/bin/stats/stats_httpd_messages.mes @@ -0,0 +1,85 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# No namespace declaration - these constants go in the global namespace +# of the stats_messages python module. + +% STATS_HTTPD_BAD_OPTION_VALUE bad command line argument: %1 +The stats-httpd module was called with a bad command-line argument, +and will not start. + +% STATS_HTTPD_CC_SESSION_ERROR error connecting to message bus: %1 +The stats-httpd module was unable to connect to the BIND 10 command +and control channel. A likely problem is that the message bus daemon +(b10-msgq) is not running. The stats-httpd module will now shut down. + +% STATS_HTTPD_CLOSING_CC_SESSION stopping cc session +Debug message indicating that the stats httpd module is disconnecting +from the command and control channel. + +% STATS_HTTPD_CLOSING closing %1#%2 +The stats-httpd daemon will stop listening for requests on te given +address and port number. + +% STATS_HTTPD_HANDLE_CONFIG reading configuration: %1 +The stats-httpd daemon has received new configuration data and will now +process it. The (changed) data is printed. + +% STATS_HTTPD_RECEIVED_SHUTDOWN_COMMAND shutdown command received +A shutdown command was sent to the stats-httpd module, and it will +now shut down. + +% STATS_HTTPD_RECEIVED_STATUS_COMMAND received command to return status +A status command was sent to the stats-httpd module, and it will +respond with 'Stats Httpd is up.' and its PID. + +% STATS_HTTPD_RECEIVED_UNKNOWN_COMMAND received unknown command: %1 +An unknown command has been sent to the stats-httpd module. The +stats-httpd module will respond with an error, and the command will +be ignored. + +% STATS_HTTPD_SERVER_ERROR http server error: %1 +There was a problem initializing the http server in the stats-httpd +module upon receiving new configuration data.The most likely cause is a +port binding problem or a bad configuration value. The specific error +is printed in the message. The new configuration is ignored, and an +error is sent back. + +% STATS_HTTPD_SHUTDOWN shutting down +The stats-httpd daemon will now shut down. + +% STATS_HTTPD_START_SERVER_ERROR http server error: %1 +There was a problem initializing the http server in the stats-httpd +module upon startup. The most likely cause is that it was not able +to bind to the listening port. The specific error is printed, and the +module will shut down. + +% STATS_HTTPD_STARTED listening on %1#%2 +The stats-httpd daemon will now start listening for requests on the +given address and port number. + +% STATS_HTTPD_STARTING_CC_SESSION starting cc session +Debug message indicating that the stats httpd module is connecting to +the command and control channel. + +% STATS_HTTPD_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down +There was a keyboard interrupt signal to stop the stats-httpd +daemon. The daemon will now shut down. + +% STATS_HTTPD_UNKNOWN_CONFIG_ITEM unknown configuration item: %1 +The stats-httpd daemon received new configuration. However, one of the +items in the configuration is unknown. The new configuration is ignored, +and an error is sent back. As possible cause is that there was an +upgrade problem, and the stats-httpd version is out of sync with the +rest of the system. diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes index 5d839c8361..eb896f68ba 100644 --- a/src/bin/stats/stats_messages.mes +++ b/src/bin/stats/stats_messages.mes @@ -19,6 +19,11 @@ The stats module was called with a bad command-line argument, and will not start. +% STATS_CC_SESSION_ERROR error connecting to message bus: %1 +The stats module was unable to connect to the BIND 10 command and +control channel. A likely problem is that the message bus daemon +(b10-msgq) is not running. The stats module will now shut down. + % STATS_RECEIVED_NEW_CONFIG received new configuration: %1 This debug message is printed when the configuration manager has sent the stats module a configuration update. @@ -57,11 +62,6 @@ to send statistics data to the stats module. There was a keyboard interrupt signal to stop the stats daemon. The daemon will now shut down. -% STATS_CC_SESSION_ERROR error connecting to message bus: %1 -The stats module was unable to connect to the BIND 10 command and -control channel. A likely problem is that the message bus daemon -(b10-msgq) is not running. The stats module will now shut down. - % STATS_UNKNOWN_COMMAND_IN_SPEC unknown command in specification file: %1 The specification file for the stats module contains a command that is unknown in the implementation. The most likely cause is an From 63918fd91e570dbcc2c06f39c75083bbae6a2303 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 7 Jul 2011 14:53:44 +0200 Subject: [PATCH 124/974] [(no branch)] [trac763] remove 'verbose' internals and set sev/dbglevel on -v --- src/bin/stats/stats.py.in | 12 ++++---- src/bin/stats/stats_httpd.py.in | 27 +++++----------- src/bin/stats/stats_httpd_messages.mes | 8 ++++- src/bin/stats/tests/b10-stats-httpd_test.py | 34 +++------------------ src/bin/stats/tests/b10-stats_test.py | 14 ++++----- 5 files changed, 32 insertions(+), 63 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index c415826a2e..38d305bf2d 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -153,9 +153,8 @@ class SessionSubject(Subject, metaclass=Singleton): """ A concrete subject class which creates CC session object """ - def __init__(self, session=None, verbose=False): + def __init__(self, session=None): Subject.__init__(self) - self.verbose = verbose self.session=session self.running = False @@ -175,9 +174,8 @@ class CCSessionListener(Listener): A concrete listener class which creates SessionSubject object and ModuleCCSession object """ - def __init__(self, subject, verbose=False): + def __init__(self, subject): Listener.__init__(self, subject) - self.verbose = verbose self.session = subject.session self.boot_time = get_datetime() @@ -402,8 +400,10 @@ def main(session=None): parser.add_option("-v", "--verbose", dest="verbose", action="store_true", help="display more about what is going on") (options, args) = parser.parse_args() - subject = SessionSubject(session=session, verbose=options.verbose) - listener = CCSessionListener(subject, verbose=options.verbose) + if options.verbose: + isc.log.init("b10-stats", "DEBUG", 99) + subject = SessionSubject(session=session) + listener = CCSessionListener(subject) subject.start() while subject.running: subject.check() diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index f0228466d7..e449f59574 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -109,9 +109,7 @@ class HttpHandler(http.server.BaseHTTPRequestHandler): return None except StatsHttpdError as err: self.send_error(500) - if self.server.verbose: - self.server.log_writer( - "[b10-stats-httpd] %s\n" % err) + logger.error(STATS_HTTPD_SERVER_ERROR, err) return None else: self.send_response(200) @@ -120,15 +118,6 @@ class HttpHandler(http.server.BaseHTTPRequestHandler): self.end_headers() return body - def log_message(self, format, *args): - """Change the default log format""" - if self.server.verbose: - self.server.log_writer( - "[b10-stats-httpd] %s - - [%s] %s\n" % - (self.address_string(), - self.log_date_time_string(), - format%args)) - class HttpServerError(Exception): """Exception class for HttpServer class. It is intended to be passed from the HttpServer object to the StatsHttpd object.""" @@ -145,13 +134,12 @@ class HttpServer(http.server.HTTPServer): sys.stderr.write. They are intended to be referred by HttpHandler object.""" def __init__(self, server_address, handler, - xml_handler, xsd_handler, xsl_handler, log_writer, verbose=False): + xml_handler, xsd_handler, xsl_handler, log_writer): self.server_address = server_address self.xml_handler = xml_handler self.xsd_handler = xsd_handler self.xsl_handler = xsl_handler self.log_writer = log_writer - self.verbose = verbose http.server.HTTPServer.__init__(self, server_address, handler) class StatsHttpdError(Exception): @@ -165,8 +153,7 @@ class StatsHttpd: statistics module. It handles HTTP requests, and command channel and config channel CC session. It uses select.select function while waiting for clients requests.""" - def __init__(self, verbose=False): - self.verbose = verbose + def __init__(self): self.running = False self.poll_intval = 0.5 self.write_log = sys.stderr.write @@ -231,7 +218,7 @@ class StatsHttpd: httpd = HttpServer( server_address, HttpHandler, self.xml_handler, self.xsd_handler, self.xsl_handler, - self.write_log, self.verbose) + self.write_log) except (socket.gaierror, socket.error, OverflowError, TypeError) as err: # try IPv4 next @@ -310,7 +297,7 @@ class StatsHttpd: logger.debug(DBG_STATS_HTTPD_MESSAGING, STATS_HTTPD_HANDLE_CONFIG, new_config) for key in new_config.keys(): - if key not in DEFAULT_CONFIG: + if key not in DEFAULT_CONFIG and key != "version": logger.error(STATS_HTTPD_UNKNOWN_CONFIG_ITEM, key) return isc.config.ccsession.create_answer( 1, "Unknown known config: %s" % key) @@ -479,7 +466,9 @@ if __name__ == "__main__": "-v", "--verbose", dest="verbose", action="store_true", help="display more about what is going on") (options, args) = parser.parse_args() - stats_httpd = StatsHttpd(verbose=options.verbose) + if options.verbose: + isc.log.init("b10-stats-httpd", "DEBUG", 99) + stats_httpd = StatsHttpd() stats_httpd.start() except OptionValueError as ove: logger.fatal(STATS_HTTPD_BAD_OPTION_VALUE, ove) diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes index b23fbd0d67..fda87b203f 100644 --- a/src/bin/stats/stats_httpd_messages.mes +++ b/src/bin/stats/stats_httpd_messages.mes @@ -50,6 +50,12 @@ stats-httpd module will respond with an error, and the command will be ignored. % STATS_HTTPD_SERVER_ERROR http server error: %1 +An internal error occurred while handling an http request. A HTTP 500 +response will be sent back, and the specific error is printed. This +is an error condition that should not occur, and likely points to a +module that is not responding correctly to statistic requests. + +% STATS_HTTPD_SERVER_INIT_ERROR http server initialization error: %1 There was a problem initializing the http server in the stats-httpd module upon receiving new configuration data.The most likely cause is a port binding problem or a bad configuration value. The specific error @@ -59,7 +65,7 @@ error is sent back. % STATS_HTTPD_SHUTDOWN shutting down The stats-httpd daemon will now shut down. -% STATS_HTTPD_START_SERVER_ERROR http server error: %1 +% STATS_HTTPD_START_SERVER_INIT_ERROR http server initialization error: %1 There was a problem initializing the http server in the stats-httpd module upon startup. The most likely cause is that it was not able to bind to the listening port. The specific error is printed, and the diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 07999ea552..a7f83a5fa9 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -57,13 +57,9 @@ class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" def setUp(self): - self.verbose = True - self.stats_httpd = stats_httpd.StatsHttpd(self.verbose) + self.stats_httpd = stats_httpd.StatsHttpd() self.assertTrue(type(self.stats_httpd.httpd) is list) self.httpd = self.stats_httpd.httpd - for ht in self.httpd: - self.assertTrue(ht.verbose) - self.stats_httpd.cc_session.verbose = False def test_do_GET(self): for ht in self.httpd: @@ -155,21 +151,6 @@ class TestHttpHandler(unittest.TestCase): handler.do_HEAD() self.assertEqual(handler.response.code, 404) - def test_log_message(self): - for ht in self.httpd: - self._test_log_message(ht._handler) - - def _test_log_message(self, handler): - # switch write_log function - handler.server.log_writer = handler.response._write_log - log_message = 'ABCDEFG' - handler.log_message("%s", log_message) - self.assertEqual(handler.response.log, - "[b10-stats-httpd] %s - - [%s] %s\n" % - (handler.address_string(), - handler.log_date_time_string(), - log_message)) - class TestHttpServerError(unittest.TestCase): """Tests for HttpServerError exception""" @@ -183,12 +164,9 @@ class TestHttpServer(unittest.TestCase): """Tests for HttpServer class""" def test_httpserver(self): - self.verbose = True - self.stats_httpd = stats_httpd.StatsHttpd(self.verbose) - self.stats_httpd.cc_session.verbose = False + self.stats_httpd = stats_httpd.StatsHttpd() for ht in self.stats_httpd.httpd: self.assertTrue(ht.server_address in self.stats_httpd.http_addrs) - self.assertEqual(ht.verbose, self.verbose) self.assertEqual(ht.xml_handler, self.stats_httpd.xml_handler) self.assertEqual(ht.xsd_handler, self.stats_httpd.xsd_handler) self.assertEqual(ht.xsl_handler, self.stats_httpd.xsl_handler) @@ -209,17 +187,14 @@ class TestStatsHttpd(unittest.TestCase): """Tests for StatsHttpd class""" def setUp(self): - self.verbose = True fake_socket._CLOSED = False fake_socket.has_ipv6 = True - self.stats_httpd = stats_httpd.StatsHttpd(self.verbose) - self.stats_httpd.cc_session.verbose = False + self.stats_httpd = stats_httpd.StatsHttpd() def tearDown(self): self.stats_httpd.stop() def test_init(self): - self.assertTrue(self.stats_httpd.verbose) self.assertFalse(self.stats_httpd.mccs.get_socket()._closed) self.assertEqual(self.stats_httpd.mccs.get_socket().fileno(), id(self.stats_httpd.mccs.get_socket())) @@ -317,8 +292,7 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd.cc_session.group_sendmsg( { 'command': [ "shutdown" ] }, "StatsHttpd") self.stats_httpd.start() - self.stats_httpd = stats_httpd.StatsHttpd(self.verbose) - self.stats_httpd.cc_session.verbose = False + self.stats_httpd = stats_httpd.StatsHttpd() self.assertRaises( fake_select.error, self.stats_httpd.start) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 6c0b392476..a42c81d136 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -41,8 +41,8 @@ class TestStats(unittest.TestCase): def setUp(self): self.session = Session() - self.subject = SessionSubject(session=self.session, verbose=True) - self.listener = CCSessionListener(self.subject, verbose=True) + self.subject = SessionSubject(session=self.session) + self.listener = CCSessionListener(self.subject) self.stats_spec = self.listener.cc_session.get_module_spec().get_config_spec() self.module_name = self.listener.cc_session.get_module_spec().get_module_name() self.stats_data = { @@ -516,9 +516,9 @@ class TestStats(unittest.TestCase): class TestStats2(unittest.TestCase): def setUp(self): - self.session = Session(verbose=True) - self.subject = SessionSubject(session=self.session, verbose=True) - self.listener = CCSessionListener(self.subject, verbose=True) + self.session = Session() + self.subject = SessionSubject(session=self.session) + self.listener = CCSessionListener(self.subject) self.module_name = self.listener.cc_session.get_module_spec().get_module_name() # check starting self.assertFalse(self.subject.running) @@ -553,9 +553,9 @@ class TestStats2(unittest.TestCase): stats.SPECFILE_LOCATION = TEST_SPECFILE_LOCATION stats.SCHEMA_SPECFILE_LOCATION = TEST_SPECFILE_LOCATION self.assertEqual(stats.SPECFILE_LOCATION, TEST_SPECFILE_LOCATION) - self.subject = stats.SessionSubject(session=self.session, verbose=True) + self.subject = stats.SessionSubject(session=self.session) self.session = self.subject.session - self.listener = stats.CCSessionListener(self.subject, verbose=True) + self.listener = stats.CCSessionListener(self.subject) self.assertEqual(self.listener.stats_spec, []) self.assertEqual(self.listener.stats_data, {}) From f30f07e5b15e118686f5665c0a6dca430a95abba Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 7 Jul 2011 15:00:30 +0200 Subject: [PATCH 125/974] [(no branch)] [trac763] fix comment --- src/bin/stats/stats_httpd_messages.mes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes index fda87b203f..bbcb058fc0 100644 --- a/src/bin/stats/stats_httpd_messages.mes +++ b/src/bin/stats/stats_httpd_messages.mes @@ -13,7 +13,7 @@ # PERFORMANCE OF THIS SOFTWARE. # No namespace declaration - these constants go in the global namespace -# of the stats_messages python module. +# of the stats_httpd_messages python module. % STATS_HTTPD_BAD_OPTION_VALUE bad command line argument: %1 The stats-httpd module was called with a bad command-line argument, From 3ac41521c2a1cbc43e3b6e0979eee46b6c45fa63 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 7 Jul 2011 15:31:41 +0200 Subject: [PATCH 126/974] [trac741] fix typo --- src/lib/cache/cache_messages.mes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 513384a507..2a68cc23bf 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -51,7 +51,7 @@ updating, the old instance is being removed prior of inserting a new one. % CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3 Debug message, noting that the given message can not be cached. This is because -there's no SOA record in the message. See RFG 2308 section 5 for more +there's no SOA record in the message. See RFC 2308 section 5 for more information. % CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache From b250ca760fe74c901845861fbc2e7292b4349724 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 7 Jul 2011 14:46:23 +0100 Subject: [PATCH 127/974] [trac981] Fix problem when running server_common tests Explicitly link in liblog. --- src/lib/server_common/tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/server_common/tests/Makefile.am b/src/lib/server_common/tests/Makefile.am index 3c061c27c5..caf8070610 100644 --- a/src/lib/server_common/tests/Makefile.am +++ b/src/lib/server_common/tests/Makefile.am @@ -40,6 +40,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la +run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la 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/exceptions/libexceptions.la From 535a401494dd268de77cccfaba68cacbaa1b2a6e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 7 Jul 2011 16:01:22 +0200 Subject: [PATCH 128/974] [trac1016] Trivial editorial cleanups --- src/lib/asiolink/tests/interval_timer_unittest.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/lib/asiolink/tests/interval_timer_unittest.cc b/src/lib/asiolink/tests/interval_timer_unittest.cc index 51c74f26c4..420cb90bb0 100644 --- a/src/lib/asiolink/tests/interval_timer_unittest.cc +++ b/src/lib/asiolink/tests/interval_timer_unittest.cc @@ -28,7 +28,7 @@ const boost::posix_time::time_duration TIMER_MARGIN_MSEC = using namespace isc::asiolink; -// This fixture is for testing IntervalTimer. Some callback functors are +// This fixture is for testing IntervalTimer. Some callback functors are // registered as callback function of the timer to test if they are called // or not. class IntervalTimerTest : public ::testing::Test { @@ -50,7 +50,9 @@ protected: }; class TimerCallBackCounter : public std::unary_function { public: - TimerCallBackCounter(IntervalTimerTest* test_obj) : test_obj_(test_obj) { + TimerCallBackCounter(IntervalTimerTest* test_obj) : + test_obj_(test_obj) + { counter_ = 0; } void operator()() { @@ -169,13 +171,13 @@ TEST_F(IntervalTimerTest, startIntervalTimer) { // delta: difference between elapsed time and 100 milliseconds. boost::posix_time::time_duration test_runtime = boost::posix_time::microsec_clock::universal_time() - start; - EXPECT_FALSE(test_runtime.is_negative()) << - "test duration " << test_runtime << + EXPECT_FALSE(test_runtime.is_negative()) << + "test duration " << test_runtime << " negative - clock skew?"; // Expect TimerCallBack is called; timer_called_ is true EXPECT_TRUE(timer_called_); // Expect test_runtime is 100 milliseconds or longer. - EXPECT_TRUE(test_runtime > boost::posix_time::milliseconds(100)) << + EXPECT_TRUE(test_runtime > boost::posix_time::milliseconds(100)) << "test runtime " << test_runtime.total_milliseconds() << "msec " << ">= 100"; } From 5e1e54cbee0215374fb712152f7906fff960b334 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 7 Jul 2011 15:28:59 +0100 Subject: [PATCH 129/974] [trac1078] Minor changes after review --- src/bin/resolver/resolver_messages.mes | 4 ++-- src/lib/resolve/resolve_messages.mes | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes index e6602d323a..b44115ae91 100644 --- a/src/bin/resolver/resolver_messages.mes +++ b/src/bin/resolver/resolver_messages.mes @@ -23,7 +23,7 @@ message to the sender with the RCODE set to NOTIMP. % RESOLVER_AXFR_UDP AXFR request received over UDP This is a debug message output when the resolver received a request for -an AXFR (full transfer of a zone) over TCP. Only authoritative servers +an AXFR (full transfer of a zone) over UDP. Only authoritative servers are able to handle AXFR requests (and in any case, an AXFR request should be sent over TCP), so the resolver will return an error message to the sender with the RCODE set to NOTIMP. @@ -200,7 +200,7 @@ timeout and drop the query. % RESOLVER_SET_ROOT_ADDRESS setting root address %1(%2) This message gives the address of one of the root servers used by the resolver. It is output during startup and may appear multiple times, -oncee for each root server address. +once for each root server address. % RESOLVER_SHUTDOWN resolver shutdown complete This informational message is output when the resolver has shut down. diff --git a/src/lib/resolve/resolve_messages.mes b/src/lib/resolve/resolve_messages.mes index fd5ec3115a..f702d9b7e8 100644 --- a/src/lib/resolve/resolve_messages.mes +++ b/src/lib/resolve/resolve_messages.mes @@ -127,7 +127,7 @@ This is a warning message only generated in unit tests. It indicates that all upstream queries from the resolver are being routed to the specified server, regardless of the address of the nameserver to which the query would normally be routed. If seen during normal operation, -please log a bug report. +please submit a bug report. % RESLIB_TEST_UPSTREAM sending upstream query for <%1> to test server at %2 This is a debug message and should only be seen in unit tests. A query for From dd356abbe83f7c1275eba42ac855977499e71e44 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 7 Jul 2011 15:32:29 +0100 Subject: [PATCH 130/974] [trac1077] Minor changes to message text after review The .h and .cc files have been updated to keep the creation dates in them in step with the change to the message file. --- src/lib/log/log_messages.cc | 2 +- src/lib/log/log_messages.h | 2 +- src/lib/log/log_messages.mes | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/log/log_messages.cc b/src/lib/log/log_messages.cc index 75e3c4f951..f60898cd85 100644 --- a/src/lib/log/log_messages.cc +++ b/src/lib/log/log_messages.cc @@ -1,4 +1,4 @@ -// File created from log_messages.mes on Tue Jul 5 18:12:20 2011 +// File created from log_messages.mes on Thu Jul 7 15:32:06 2011 #include #include diff --git a/src/lib/log/log_messages.h b/src/lib/log/log_messages.h index 7d05856230..10e150196b 100644 --- a/src/lib/log/log_messages.h +++ b/src/lib/log/log_messages.h @@ -1,4 +1,4 @@ -// File created from log_messages.mes on Tue Jul 5 18:12:20 2011 +// File created from log_messages.mes on Thu Jul 7 15:32:06 2011 #ifndef __LOG_MESSAGES_H #define __LOG_MESSAGES_H diff --git a/src/lib/log/log_messages.mes b/src/lib/log/log_messages.mes index 6c053152f6..f150f3965f 100644 --- a/src/lib/log/log_messages.mes +++ b/src/lib/log/log_messages.mes @@ -38,7 +38,7 @@ Allowed values are "stdout" and "stderr". % LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code During start-up, BIND 10 detected that the given message identification had been defined multiple times in the BIND 10 code. This indicates a -programming error; please log a bug report. +programming error; please submit a bug report. % LOG_DUPLICATE_NAMESPACE line %1: duplicate $NAMESPACE directive found When reading a message file, more than one $NAMESPACE directive was found. From b5bbfb2f868f8f7401018debe275c39fc65a5139 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 7 Jul 2011 16:25:35 +0100 Subject: [PATCH 131/974] [trac1077] Add liblog to list of libraries for linking to cache unit tests --- src/lib/cache/tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am index 39215d9031..f9237af2a5 100644 --- a/src/lib/cache/tests/Makefile.am +++ b/src/lib/cache/tests/Makefile.am @@ -53,6 +53,7 @@ run_unittests_LDADD += -lboost_thread endif run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la +run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la From 0d68ac445710fdb4d9d89ca2055b206c9a06dc94 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 7 Jul 2011 17:49:57 +0100 Subject: [PATCH 132/974] [trac1024] Route unit test logging output to /dev/null by default The change to make DEBUG the default logging severity generates a lot of output. C++ unit test output is now routed to /dev/null by default, with B10_LOGGER_DESTINATION needing to be set to stdout or stderr to make it visible. --- src/lib/log/logger_support.cc | 89 ++++++++++++------------ src/lib/log/tests/init_logger_test.sh.in | 6 +- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc index 2c5ab455e2..36798cc9b6 100644 --- a/src/lib/log/logger_support.cc +++ b/src/lib/log/logger_support.cc @@ -57,60 +57,63 @@ setDestination(const char* root, const isc::log::Severity severity, using namespace isc::log; + // Constants: not declared static as this is function is expected to be + // called once only + const string DEVNULL = "/dev/null"; + const string STDOUT = "stdout"; + const string STDERR = "stderr"; + const string SYSLOG = "syslog"; + const string SYSLOG_COLON = "syslog:"; + + // Get the destination. If not specified, assume /dev/null. (The default + // severity for unit tests is DEBUG, which generates a lot of output. + // Routing the logging to /dev/null will suppress that, whilst still + // ensuring that the code paths are tested.) const char* destination = getenv("B10_LOGGER_DESTINATION"); - if (destination != NULL) { + const string dest((destination == NULL) ? DEVNULL : destination); - // Constants: not declared static as this is function is expected to be - // called once only - const string STDOUT = "stdout"; - const string STDERR = "stderr"; - const string SYSLOG = "syslog"; - const string SYSLOG_COLON = "syslog:"; + // Prepare the objects to define the logging specification + LoggerSpecification spec(root, severity, dbglevel); + OutputOption option; - // Prepare the objects to define the logging specification - LoggerSpecification spec(root, severity, dbglevel); - OutputOption option; + // Set up output option according to destination specification + if (dest == STDOUT) { + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDOUT; - // Set up output option according to destination specification - const string dest = destination; - if (dest == STDOUT) { - option.destination = OutputOption::DEST_CONSOLE; - option.stream = OutputOption::STR_STDOUT; + } else if (dest == STDERR) { + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDERR; - } else if (dest == STDERR) { - option.destination = OutputOption::DEST_CONSOLE; - option.stream = OutputOption::STR_STDERR; + } else if (dest == SYSLOG) { + option.destination = OutputOption::DEST_SYSLOG; + // Use default specified in OutputOption constructor for the + // syslog destination - } else if (dest == SYSLOG) { - option.destination = OutputOption::DEST_SYSLOG; - // Use default specified in OutputOption constructor for the - // syslog destination - - } else if (dest.find(SYSLOG_COLON) == 0) { - option.destination = OutputOption::DEST_SYSLOG; - // Must take account of the string actually being "syslog:" - if (dest == SYSLOG_COLON) { - cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " << - SYSLOG_COLON << " is invalid, " << SYSLOG << - " will be used instead\n"; - // Use default for logging facility - - } else { - // Everything else in the string is the facility name - option.facility = dest.substr(SYSLOG_COLON.size()); - } + } else if (dest.find(SYSLOG_COLON) == 0) { + option.destination = OutputOption::DEST_SYSLOG; + // Must take account of the string actually being "syslog:" + if (dest == SYSLOG_COLON) { + cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " << + SYSLOG_COLON << " is invalid, " << SYSLOG << + " will be used instead\n"; + // Use default for logging facility } else { - // Not a recognised destination, assume a file - option.destination = OutputOption::DEST_FILE; - option.filename = dest; + // Everything else in the string is the facility name + option.facility = dest.substr(SYSLOG_COLON.size()); } - // ... and set the destination - spec.addOutputOption(option); - LoggerManager manager; - manager.process(spec); + } else { + // Not a recognised destination, assume a file. + option.destination = OutputOption::DEST_FILE; + option.filename = dest; } + + // ... and set the destination + spec.addOutputOption(option); + LoggerManager manager; + manager.process(spec); } } // Anonymous namespace diff --git a/src/lib/log/tests/init_logger_test.sh.in b/src/lib/log/tests/init_logger_test.sh.in index d26ca5ddf5..795419bf66 100755 --- a/src/lib/log/tests/init_logger_test.sh.in +++ b/src/lib/log/tests/init_logger_test.sh.in @@ -44,7 +44,7 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID . -B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test 2>&1 | \ +B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test | \ cut -d' ' -f3- | diff $tempfile - passfail $? @@ -57,7 +57,7 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID . -B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test 2>&1 | \ +B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test | \ cut -d' ' -f3- | diff $tempfile - passfail $? @@ -67,7 +67,7 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID . -B10_LOGGER_SEVERITY=WARN ./init_logger_test 2>&1 | \ +B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=WARN ./init_logger_test | \ cut -d' ' -f3- | diff $tempfile - passfail $? From 0cd32b208c9a92e5e773b7874a3f75ee58abd6c0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 7 Jul 2011 11:13:47 -0700 Subject: [PATCH 133/974] [trac764] minor doxygen fixes: add log/compiler to INPUT; make sure the overview will be the documentation for the file (not for a specific function) --- doc/Doxyfile | 4 ++-- src/lib/log/compiler/message.cc | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/Doxyfile b/doc/Doxyfile index b8a4656058..ceb806ff5c 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -570,8 +570,8 @@ WARN_LOGFILE = INPUT = ../src/lib/cc ../src/lib/config \ ../src/lib/cryptolink ../src/lib/dns ../src/lib/datasrc \ - ../src/bin/auth ../src/bin/resolver ../src/lib/bench \ - ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas \ + ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \ + ../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \ ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \ ../src/bin/sockcreator/ ../src/lib/util/ \ ../src/lib/resolve ../src/lib/acl diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc index e2807289b7..2c3e76a6a4 100644 --- a/src/lib/log/compiler/message.cc +++ b/src/lib/log/compiler/message.cc @@ -43,6 +43,7 @@ using namespace isc::util; static const char* VERSION = "1.0-0"; +/// \file log/compiler/message.cc /// \brief Message Compiler /// /// \b Overview
From a837df2e0c4858543c0bd2e420f726b89994a7e2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 7 Jul 2011 11:20:24 -0700 Subject: [PATCH 134/974] [trac764] minor editorial/style fixes: do not rely on conversion from pointer to bool; brace position; parentheses around return; constify a caught exception --- src/lib/log/compiler/message.cc | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc index 2c3e76a6a4..8bec794350 100644 --- a/src/lib/log/compiler/message.cc +++ b/src/lib/log/compiler/message.cc @@ -110,7 +110,7 @@ currentTime() { // Convert to string and strip out the trailing newline string current_time = buffer; - return isc::util::str::trim(current_time); + return (isc::util::str::trim(current_time)); } @@ -131,7 +131,7 @@ sentinel(Filename& file) { string ext = file.extension(); string sentinel_text = "__" + name + "_" + ext.substr(1); isc::util::str::uppercase(sentinel_text); - return sentinel_text; + return (sentinel_text); } @@ -158,7 +158,7 @@ quoteString(const string& instring) { outstring += instring[i]; } - return outstring; + return (outstring); } @@ -181,7 +181,7 @@ sortedIdentifiers(MessageDictionary& dictionary) { } sort(ident.begin(), ident.end()); - return ident; + return (ident); } @@ -211,7 +211,7 @@ splitNamespace(string ns) { // ... and return the vector of namespace components split on the single // colon. - return isc::util::str::tokens(ns, ":"); + return (isc::util::str::tokens(ns, ":")); } @@ -262,10 +262,11 @@ writeClosingNamespace(ostream& output, const vector& ns) { void writePythonFile(const string& file, MessageDictionary& dictionary, - const char* output_directory) { + const char* output_directory) +{ Filename message_file(file); Filename python_file(Filename(message_file.name()).useAsDefault(".py")); - if (output_directory) { + if (output_directory != NULL) { python_file.setDirectory(output_directory); } @@ -312,7 +313,7 @@ writeHeaderFile(const string& file, const vector& ns_components, { Filename message_file(file); Filename header_file(Filename(message_file.name()).useAsDefault(".h")); - if (output_directory) { + if (output_directory != NULL) { header_file.setDirectory(output_directory); } @@ -545,15 +546,15 @@ main(int argc, char* argv[]) { case 'h': usage(); - return 0; + return (0); case 'v': version(); - return 0; + return (0); default: // A message will have already been output about the error. - return 1; + return (1); } } @@ -561,11 +562,11 @@ main(int argc, char* argv[]) { if (optind < (argc - 1)) { cout << "Error: excess arguments in command line\n"; usage(); - return 1; + return (1); } else if (optind >= argc) { cout << "Error: missing message file\n"; usage(); - return 1; + return (1); } string message_file = argv[optind]; @@ -605,7 +606,7 @@ main(int argc, char* argv[]) { // Finally, warn of any duplicates encountered. warnDuplicates(reader); } - catch (MessageException& e) { + catch (const MessageException& e) { // Create an error message from the ID and the text MessageDictionary& global = MessageDictionary::globalDictionary(); string text = e.id(); @@ -619,9 +620,9 @@ main(int argc, char* argv[]) { cerr << text << "\n"; - return 1; + return (1); } - return 0; + return (0); } From b18409a1d6515152d107cd965e25ef58835f9f22 Mon Sep 17 00:00:00 2001 From: Shane Kerr Date: Thu, 7 Jul 2011 23:29:56 +0200 Subject: [PATCH 135/974] [trac1100] Typo in log identifier. --- src/bin/xfrin/xfrin.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in index 64e3563200..d1fbbfef0e 100755 --- a/src/bin/xfrin/xfrin.py.in +++ b/src/bin/xfrin/xfrin.py.in @@ -152,7 +152,7 @@ class XfrinConnection(asyncore.dispatcher): self.connect(self._master_address) return True except socket.error as e: - logger.error(CONNECT_MASTER, self._master_address, str(e)) + logger.error(XFRIN_CONNECT_MASTER, self._master_address, str(e)) return False def _create_query(self, query_type): From b9bc51b44f59c9e93eaa5a21ae7658a320741e08 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 7 Jul 2011 15:00:33 -0700 Subject: [PATCH 136/974] [trac983] addressed installation/distcheck issues. added another tweak to configure.ac to ensure all necessary .py and .so will be installed in the correct place. also added missing files to Makefile.am to fix distcheck failures. --- configure.ac | 10 ++++++++++ src/lib/python/isc/acl/Makefile.am | 16 +++++++++++++--- src/lib/python/isc/acl/acl.py | 12 ++++-------- src/lib/python/isc/acl/dns.py | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 6ec1ea1e31..9eab8f9f43 100644 --- a/configure.ac +++ b/configure.ac @@ -139,6 +139,16 @@ else AC_SUBST(pkgpyexecdir) fi +# We need to store the default pyexecdir in a separate variable so that +# we can specify in Makefile.am the install directory of various BIND 10 +# python scripts and loadable modules; in Makefile.am we cannot replace +# $(pyexecdir) using itself, e.g, this doesn't work: +# pyexecdir = $(pyexecdir)/isc/some_module +# The separate variable makes this setup possible as follows: +# pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/some_module +PYTHON_SITEPKG_DIR=${pyexecdir} +AC_SUBST(PYTHON_SITEPKG_DIR) + # Check for python development environments if test -x ${PYTHON}-config; then PYTHON_INCLUDES=`${PYTHON}-config --includes` diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index 02b3460a35..2f3614f41c 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -4,9 +4,13 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CXXFLAGS = $(B10_CXXFLAGS) -pyexec_LTLIBRARIES = acl.la dns.la +python_PYTHON = __init__.py +pythondir = $(PYTHON_SITEPKG_DIR)/isc/acl -acl_la_SOURCES = acl.cc +pyexec_LTLIBRARIES = acl.la dns.la +pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/acl + +acl_la_SOURCES = acl.h acl.cc acl_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) acl_la_LDFLAGS = $(PYTHON_LDFLAGS) acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) @@ -29,4 +33,10 @@ dns_la_LDFLAGS += -module dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la dns_la_LIBADD += $(PYTHON_LIB) -EXTRA_DIST = dnsacl_inc.cc dns_requestacl_inc.cc dns_requestcontext_inc.cc +EXTRA_DIST = acl.py dns.py +EXTRA_DIST += dnsacl_inc.cc dns_requestacl_inc.cc dns_requestcontext_inc.cc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/lib/python/isc/acl/acl.py b/src/lib/python/isc/acl/acl.py index ac97ca109e..804d78bba9 100644 --- a/src/lib/python/isc/acl/acl.py +++ b/src/lib/python/isc/acl/acl.py @@ -13,14 +13,10 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# This file is not installed. The log.so is installed into the right place. -# It is only to find it in the .libs directory when we run as a test or -# from the build directory. -# But as nobody gives us the builddir explicitly (and we can't use generation -# from .in file, as it would put us into the builddir and we wouldn't be found) -# we guess from current directory. Any idea for something better? This should -# be enough for the tests, but would it work for B10_FROM_SOURCE as well? -# Should we look there? Or define something in bind10_config? +# This file is not installed; The .so version will be installed into the right +# place at installation time. +# This helper script is only to find it in the .libs directory when we run +# as a test or from the build directory. import os import sys diff --git a/src/lib/python/isc/acl/dns.py b/src/lib/python/isc/acl/dns.py index 8070559b0a..9eaf33d1ba 100644 --- a/src/lib/python/isc/acl/dns.py +++ b/src/lib/python/isc/acl/dns.py @@ -24,6 +24,25 @@ import os import sys +# 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 file is not installed; The .so version will be installed into the right +# place at installation time. +# This helper script is only to find it in the .libs directory when we run +# as a test or from the build directory. for base in sys.path[:]: bindingdir = os.path.join(base, 'isc/acl/.libs') From 0e6771fbedb4081dc867e845b541023a673a1da6 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 7 Jul 2011 15:05:12 -0700 Subject: [PATCH 137/974] [trac983] cleanup: removed redundant white spaces --- src/lib/python/isc/acl/dns_requestacl_python.cc | 2 +- src/lib/python/isc/acl/dns_requestcontext_python.cc | 2 +- src/lib/python/isc/acl/tests/Makefile.am | 2 +- src/lib/util/python/wrapper_template.cc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc index 06eb068406..00829ce3e7 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.cc +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -127,7 +127,7 @@ PyTypeObject requestacl_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call NULL, // tp_str NULL, // tp_getattro diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.cc b/src/lib/python/isc/acl/dns_requestcontext_python.cc index 51ff9cd687..c84003622f 100644 --- a/src/lib/python/isc/acl/dns_requestcontext_python.cc +++ b/src/lib/python/isc/acl/dns_requestcontext_python.cc @@ -250,7 +250,7 @@ PyTypeObject requestcontext_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call RequestContext_str, // tp_str NULL, // tp_getattro diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am index 9bd20da754..64737d2c33 100644 --- a/src/lib/python/isc/acl/tests/Makefile.am +++ b/src/lib/python/isc/acl/tests/Makefile.am @@ -13,7 +13,7 @@ endif # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc index 691e4bfb42..3a9282fbba 100644 --- a/src/lib/util/python/wrapper_template.cc +++ b/src/lib/util/python/wrapper_template.cc @@ -222,7 +222,7 @@ PyTypeObject @cppclass@_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call // THIS MAY HAVE TO BE CHANGED TO NULL: @CPPCLASS@_str, // tp_str From bfbd97a0fa52c122c6d0ab5239524b7be58b62be Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 7 Jul 2011 15:15:10 -0700 Subject: [PATCH 138/974] [trac983] added some more documentation --- src/lib/python/isc/acl/dns_requestacl_python.h | 5 +++++ src/lib/python/isc/acl/dns_requestcontext_python.cc | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/acl/dns_requestacl_python.h b/src/lib/python/isc/acl/dns_requestacl_python.h index e8ad0d3b87..8f7ad8a097 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.h +++ b/src/lib/python/isc/acl/dns_requestacl_python.h @@ -30,6 +30,11 @@ namespace python { class s_RequestACL : public PyObject { public: s_RequestACL(); + + // We don't have to use a shared pointer for its original purposes as + // the python object maintains reference counters itself. But the + // underlying C++ API only exposes a shared pointer for the ACL objects, + // so we store it in that form. boost::shared_ptr cppobj; }; diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.cc b/src/lib/python/isc/acl/dns_requestcontext_python.cc index c84003622f..2da0c38302 100644 --- a/src/lib/python/isc/acl/dns_requestcontext_python.cc +++ b/src/lib/python/isc/acl/dns_requestcontext_python.cc @@ -60,6 +60,10 @@ namespace dns { namespace python { struct s_RequestContext::Data { + // The constructor. Currently it only accepts the information of the + // request source address, and contains all necessary logic in the body + // of the constructor. As it's extended we may have refactor it by + // introducing helper methods. Data(const char* const remote_addr, const unsigned short remote_port) { struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); @@ -82,12 +86,19 @@ struct s_RequestContext::Data { remote_ipaddr.reset(new IPAddress(getRemoteSockaddr())); } + // A convenient type converter from sockaddr_storage to sockaddr const struct sockaddr& getRemoteSockaddr() const { const void* p = &remote_ss; return (*static_cast(p)); } + // The remote (source) IP address the request. Note that it needs + // a reference to remote_ss. That's why the latter is stored within + // this structure. scoped_ptr remote_ipaddr; + + // The effective length of remote_ss. It's necessary for getnameinf() + // called from sockaddrToText (__str__ backend). socklen_t remote_salen; private: @@ -189,7 +200,7 @@ RequestContext_destroy(PyObject* po_self) { Py_TYPE(self)->tp_free(self); } -// A helper function for __str()__ +// A helper function for __str__() string sockaddrToText(const struct sockaddr& sa, socklen_t sa_len) { char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; From 6c44ca2eaa94224d60ceac2602ee9c6846fabf18 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 7 Jul 2011 23:14:40 -0700 Subject: [PATCH 139/974] [master] minor and trivial fix to a SCOPED_TRACE message. directly pushing. --- src/lib/dns/tests/tsig_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc index ba17e70b27..cbb12676de 100644 --- a/src/lib/dns/tests/tsig_unittest.cc +++ b/src/lib/dns/tests/tsig_unittest.cc @@ -440,7 +440,7 @@ TEST_F(TSIGTest, signUsingHMACSHA224) { 0xef, 0x33, 0xa2, 0xda, 0xa1, 0x48, 0x71, 0xd3 }; { - SCOPED_TRACE("Sign test using HMAC-SHA1"); + SCOPED_TRACE("Sign test using HMAC-SHA224"); commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx), sha1_qid, 0x4dae7d5f, expected_mac, sizeof(expected_mac), 0, 0, NULL, From e8e411dd27068279b58bc3527d1b60878ed19d0b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 8 Jul 2011 00:13:21 -0700 Subject: [PATCH 140/974] [trac910] pre-work refactoring: precreate an HMAC in the TSIGContextImpl constructor (if possible) so that the expected size of resulting TSIG RR can be calculated immediately before signing. --- src/lib/dns/tsig.cc | 62 +++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc index 714b2a596e..f0b37d35b3 100644 --- a/src/lib/dns/tsig.cc +++ b/src/lib/dns/tsig.cc @@ -58,10 +58,32 @@ getTSIGTime() { } struct TSIGContext::TSIGContextImpl { - TSIGContextImpl(const TSIGKey& key) : - state_(INIT), key_(key), error_(Rcode::NOERROR()), - previous_timesigned_(0) - {} + TSIGContextImpl(const TSIGKey& key, + TSIGError error = TSIGError::NOERROR()) : + state_(INIT), key_(key), error_(error), + previous_timesigned_(0), digest_len_(0) + { + if (error == TSIGError::NOERROR()) { + // In normal (NOERROR) case, the key should be valid, and we + // should be able to pre-create a corresponding HMAC object, + // which will be likely to be used for sign or verify later. + // We do this in the constructor so that we can know the expected + // digest length in advance. The creation should normally succeed, + // but the key information could be still broken, which could + // trigger an exception inside the cryptolink module. We ignore + // it at this moment; a subsequent sign/verify operation will try + // to create the HMAC, which would also fail. + try { + hmac_.reset(CryptoLink::getCryptoLink().createHMAC( + key_.getSecret(), key_.getSecretLength(), + key_.getAlgorithm()), + deleteHMAC); + } catch (const Exception&) { + return; + } + digest_len_ = hmac_->getOutputLength(); + } + } // This helper method is used from verify(). It's expected to be called // just before verify() returns. It updates internal state based on @@ -85,6 +107,21 @@ struct TSIGContext::TSIGContextImpl { return (error); } + // A shortcut method to create an HMAC object for sign/verify. If one + // has been successfully created in the constructor, return it; otherwise + // create a new one and return it. + HMACPtr getHMAC() { + if (hmac_) { + HMACPtr ret = HMACPtr(); + ret.swap(hmac_); + return (ret); + } + return (HMACPtr(CryptoLink::getCryptoLink().createHMAC( + key_.getSecret(), key_.getSecretLength(), + key_.getAlgorithm()), + deleteHMAC)); + } + // The following three are helper methods to compute the digest for // TSIG sign/verify in order to unify the common code logic for sign() // and verify() and to keep these callers concise. @@ -111,6 +148,8 @@ struct TSIGContext::TSIGContextImpl { vector previous_digest_; TSIGError error_; uint64_t previous_timesigned_; // only meaningful for response with BADTIME + size_t digest_len_; + HMACPtr hmac_; }; void @@ -221,8 +260,7 @@ TSIGContext::TSIGContext(const Name& key_name, const Name& algorithm_name, // be used in subsequent response with a TSIG indicating a BADKEY // error. impl_ = new TSIGContextImpl(TSIGKey(key_name, algorithm_name, - NULL, 0)); - impl_->error_ = TSIGError::BAD_KEY(); + NULL, 0), TSIGError::BAD_KEY()); } else { impl_ = new TSIGContextImpl(*result.key); } @@ -276,11 +314,7 @@ TSIGContext::sign(const uint16_t qid, const void* const data, return (tsig); } - HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC( - impl_->key_.getSecret(), - impl_->key_.getSecretLength(), - impl_->key_.getAlgorithm()), - deleteHMAC); + HMACPtr hmac(impl_->getHMAC()); // If the context has previous MAC (either the Request MAC or its own // previous MAC), digest it. @@ -406,11 +440,7 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data, return (impl_->postVerifyUpdate(error, NULL, 0)); } - HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC( - impl_->key_.getSecret(), - impl_->key_.getSecretLength(), - impl_->key_.getAlgorithm()), - deleteHMAC); + HMACPtr hmac(impl_->getHMAC()); // If the context has previous MAC (either the Request MAC or its own // previous MAC), digest it. From bf635ee41af43f357b285ab97f04f72b37e8fb64 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 8 Jul 2011 09:41:27 +0200 Subject: [PATCH 141/974] [trac926] add 'named_map' type in cpp module check --- src/lib/cc/data.cc | 2 ++ src/lib/config/module_spec.cc | 25 +++++++++++++++---- src/lib/config/tests/module_spec_unittests.cc | 10 ++++++++ src/lib/config/tests/testdata/data32_1.data | 3 +++ 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 src/lib/config/tests/testdata/data32_1.data diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index a455d43dc0..5a14de63de 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -511,6 +511,8 @@ Element::nameToType(const std::string& type_name) { return (Element::list); } else if (type_name == "map") { return (Element::map); + } else if (type_name == "named_map") { + return (Element::map); } else if (type_name == "null") { return (Element::null); } else if (type_name == "any") { diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc index 1621fe313f..f84cfca67f 100644 --- a/src/lib/config/module_spec.cc +++ b/src/lib/config/module_spec.cc @@ -67,10 +67,13 @@ check_config_item(ConstElementPtr spec) { check_leaf_item(spec, "list_item_spec", Element::map, true); check_config_item(spec->get("list_item_spec")); } - // todo: add stuff for type map - if (Element::nameToType(spec->get("item_type")->stringValue()) == Element::map) { + + if (spec->get("item_type")->stringValue() == "map") { check_leaf_item(spec, "map_item_spec", Element::list, true); check_config_item_list(spec->get("map_item_spec")); + } else if (spec->get("item_type")->stringValue() == "named_map") { + check_leaf_item(spec, "named_map_item_spec", Element::map, true); + check_config_item(spec->get("named_map_item_spec")); } } @@ -286,7 +289,8 @@ check_type(ConstElementPtr spec, ConstElementPtr element) { return (cur_item_type == "list"); break; case Element::map: - return (cur_item_type == "map"); + return (cur_item_type == "map" || + cur_item_type == "named_map"); break; } return (false); @@ -323,8 +327,19 @@ ModuleSpec::validateItem(ConstElementPtr spec, ConstElementPtr data, } } if (data->getType() == Element::map) { - if (!validateSpecList(spec->get("map_item_spec"), data, full, errors)) { - return (false); + // either a 'normal' map or a 'named' map + if (spec->contains("map_item_spec")) { + if (!validateSpecList(spec->get("map_item_spec"), data, full, errors)) { + return (false); + } + } else { + typedef std::pair maptype; + + BOOST_FOREACH(maptype m, data->mapValue()) { + if (!validateItem(spec->get("named_map_item_spec"), m.second, full, errors)) { + return (false); + } + } } } return (true); diff --git a/src/lib/config/tests/module_spec_unittests.cc b/src/lib/config/tests/module_spec_unittests.cc index 1b43350f6a..46f66e45aa 100644 --- a/src/lib/config/tests/module_spec_unittests.cc +++ b/src/lib/config/tests/module_spec_unittests.cc @@ -211,3 +211,13 @@ TEST(ModuleSpec, CommandValidation) { EXPECT_EQ(errors->get(0)->stringValue(), "Type mismatch"); } + +TEST(ModuleSpec, NamedMapValidation) { + ModuleSpec dd = moduleSpecFromFile(specfile("spec32.spec")); + + ElementPtr errors = Element::createList(); + EXPECT_TRUE(dataTestWithErrors(dd, "data32_1.data", errors)); + std::cout << "[XX] ERRORS: " << *errors << std::endl; + EXPECT_FALSE(dataTest(dd, "data32_2.data")); + EXPECT_FALSE(dataTest(dd, "data32_3.data")); +} diff --git a/src/lib/config/tests/testdata/data32_1.data b/src/lib/config/tests/testdata/data32_1.data new file mode 100644 index 0000000000..670fd52d3b --- /dev/null +++ b/src/lib/config/tests/testdata/data32_1.data @@ -0,0 +1,3 @@ +{ + "named_map_item": { "foo": 1, "bar": 2 } +} From 3b30727c4ae0b4febedb9795752352bf5154730a Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 8 Jul 2011 10:21:06 +0200 Subject: [PATCH 142/974] [trac926] spec check for python --- src/lib/python/isc/config/module_spec.py | 18 +++++++++++++++--- .../isc/config/tests/module_spec_test.py | 3 +++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/config/module_spec.py b/src/lib/python/isc/config/module_spec.py index 61711494de..80c44e59bd 100644 --- a/src/lib/python/isc/config/module_spec.py +++ b/src/lib/python/isc/config/module_spec.py @@ -229,7 +229,7 @@ def _check_item_spec(config_item): item_type = config_item["item_type"] if type(item_type) != str: raise ModuleSpecError("item_type in " + item_name + " is not a string: " + str(type(item_type))) - if item_type not in ["integer", "real", "boolean", "string", "list", "map", "any"]: + if item_type not in ["integer", "real", "boolean", "string", "list", "map", "named_map", "any"]: raise ModuleSpecError("unknown item_type in " + item_name + ": " + item_type) if "item_optional" in config_item: if type(config_item["item_optional"]) != bool: @@ -293,6 +293,10 @@ def _validate_type(spec, value, errors): if errors != None: errors.append(str(value) + " should be a map") return False + elif data_type == "named_map" and type(value) != dict: + if errors != None: + errors.append(str(value) + " should be a map") + return False else: return True @@ -308,8 +312,16 @@ def _validate_item(spec, full, data, errors): if not _validate_item(list_spec, full, data_el, errors): return False elif type(data) == dict: - if not _validate_spec_list(spec['map_item_spec'], full, data, errors): - return False + if 'map_item_spec' in spec: + if not _validate_spec_list(spec['map_item_spec'], full, data, errors): + return False + else: + named_map_spec = spec['named_map_item_spec'] + for data_el in data.values(): + if not _validate_type(named_map_spec, data_el, errors): + return False + if not _validate_item(named_map_spec, full, data_el, errors): + return False return True def _validate_spec(spec, full, data, errors): diff --git a/src/lib/python/isc/config/tests/module_spec_test.py b/src/lib/python/isc/config/tests/module_spec_test.py index a4dcdecd21..be862c5012 100644 --- a/src/lib/python/isc/config/tests/module_spec_test.py +++ b/src/lib/python/isc/config/tests/module_spec_test.py @@ -98,6 +98,9 @@ class TestModuleSpec(unittest.TestCase): self.assertEqual(True, self.validate_data("spec22.spec", "data22_6.data")) self.assertEqual(True, self.validate_data("spec22.spec", "data22_7.data")) self.assertEqual(False, self.validate_data("spec22.spec", "data22_8.data")) + self.assertEqual(True, self.validate_data("spec32.spec", "data32_1.data")) + self.assertEqual(False, self.validate_data("spec32.spec", "data32_2.data")) + self.assertEqual(False, self.validate_data("spec32.spec", "data32_3.data")) def validate_command_params(self, specfile_name, datafile_name, cmd_name): dd = self.read_spec_file(specfile_name); From 7b2691afaea9ccefa2db073f8a717e003f2ad07e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 8 Jul 2011 14:48:33 +0200 Subject: [PATCH 143/974] [trac983] Fix library dependencies --- src/lib/python/isc/acl/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index 2f3614f41c..4a9c5f0ac4 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -26,11 +26,12 @@ dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) # Python prefers .so, while some OSes (specifically MacOS) use a different # suffix for dynamic objects. -module is necessary to work this around. acl_la_LDFLAGS += -module -acl_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la +acl_la_LIBADD = $(top_builddir)/src/lib/acl/libacl.la acl_la_LIBADD += $(PYTHON_LIB) dns_la_LDFLAGS += -module dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la +dns_la_LIBADD += acl.la dns_la_LIBADD += $(PYTHON_LIB) EXTRA_DIST = acl.py dns.py From 9ff2f7bc88be85c09d37209bd0feeb96ca256892 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 8 Jul 2011 17:36:24 +0200 Subject: [PATCH 144/974] [trac764] review comments --- src/lib/log/compiler/message.cc | 9 ++-- src/lib/python/isc/config/Makefile.am | 7 ++- src/lib/python/isc/notify/Makefile.am | 4 +- src/lib/python/isc/notify/notify_out.py | 33 ++++++------ .../python/isc/notify/notify_out_messages.mes | 53 +++++++++++-------- .../isc/notify/tests/notify_out_test.py | 9 ++++ src/lib/util/filename.cc | 18 ++++--- src/lib/util/filename.h | 6 ++- src/lib/util/tests/filename_unittest.cc | 6 +-- 9 files changed, 89 insertions(+), 56 deletions(-) diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc index 8bec794350..2383196ebe 100644 --- a/src/lib/log/compiler/message.cc +++ b/src/lib/log/compiler/message.cc @@ -56,12 +56,13 @@ static const char* VERSION = "1.0-0"; /// \b Invocation
/// The program is invoked with the command: /// -/// message [-v | -h | -p | -d | \] +/// message [-v | -h | -p | -d | ] /// -/// It reads the message file and writes out two files of the same name in the -/// default directory but with extensions of .h and .cc. +/// It reads the message file and writes out two files of the same +/// name in the current working directory (unless -d is used) but +/// with extensions of .h and .cc. /// -/// \-v causes it to print the version number and exit. \-h prints a help +/// -v causes it to print the version number and exit. -h prints a help /// message (and exits). -p sets the output to python. -d will make /// it write the output file(s) to dir instead of current working /// directory diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am index 185d1015d8..312ad3348c 100644 --- a/src/lib/python/isc/config/Makefile.am +++ b/src/lib/python/isc/config/Makefile.am @@ -7,10 +7,13 @@ pythondir = $(pyexecdir)/isc/config # Define rule to build logging source files from message file cfgmgr_messages.py: cfgmgr_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes $(top_builddir)/src/lib/python/config_messages.py: config_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p -d $(top_builddir)/src/lib/python $(top_srcdir)/src/lib/python/isc/config/config_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -p -d $(top_builddir)/src/lib/python \ + $(top_srcdir)/src/lib/python/isc/config/config_messages.mes CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc CLEANFILES += $(top_builddir)/src/lib/python/config_messages.py diff --git a/src/lib/python/isc/notify/Makefile.am b/src/lib/python/isc/notify/Makefile.am index 851d2746cb..a23a1ffb4b 100644 --- a/src/lib/python/isc/notify/Makefile.am +++ b/src/lib/python/isc/notify/Makefile.am @@ -6,7 +6,9 @@ pyexec_DATA = $(top_builddir)/src/lib/python/notify_out_messages.py pythondir = $(pyexecdir)/isc/notify $(top_builddir)/src/lib/python/notify_out_messages.py: notify_out_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p -d $(top_builddir)/src/lib/python $(top_srcdir)/src/lib/python/isc/notify/notify_out_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -p -d $(top_builddir)/src/lib/python \ + $(top_srcdir)/src/lib/python/isc/notify/notify_out_messages.mes EXTRA_DIST = notify_out_messages.mes diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py index fca0fec136..b495714453 100644 --- a/src/lib/python/isc/notify/notify_out.py +++ b/src/lib/python/isc/notify/notify_out.py @@ -30,7 +30,7 @@ logger = isc.log.Logger("notify_out") try: from pydnspp import * except ImportError as e: - logger.error(NOTIFY_OUT_IMPORT_DNS, str(e)) + logger.error(NOTIFY_OUT_IMPORT_ERROR, str(e)) ZONE_NEW_DATA_READY_CMD = 'zone_new_data_ready' _MAX_NOTIFY_NUM = 30 @@ -361,18 +361,19 @@ class NotifyOut: tgt = zone_notify_info.get_current_notify_target() if event_type == _EVENT_READ: reply = self._get_notify_reply(zone_notify_info.get_socket(), tgt) - if reply: - if self._handle_notify_reply(zone_notify_info, reply): + if reply is not None: + if self._handle_notify_reply(zone_notify_info, reply, tgt): self._notify_next_target(zone_notify_info) elif event_type == _EVENT_TIMEOUT and zone_notify_info.notify_try_num > 0: - logger.info(NOTIFY_OUT_TIMEOUT_RETRY, tgt[0], tgt[1]) + logger.info(NOTIFY_OUT_TIMEOUT, tgt[0], tgt[1]) tgt = zone_notify_info.get_current_notify_target() if tgt: zone_notify_info.notify_try_num += 1 if zone_notify_info.notify_try_num > _MAX_NOTIFY_TRY_NUM: - logger.info(NOTIFY_OUT_RETRY_EXCEEDED, tgt[0], tgt[1]) + logger.error(NOTIFY_OUT_RETRY_EXCEEDED, tgt[0], tgt[1], + _MAX_NOTIFY_TRY_NUM) self._notify_next_target(zone_notify_info) else: # set exponential backoff according rfc1996 section 3.6 @@ -452,34 +453,32 @@ class NotifyOut: def _handle_notify_reply(self, zone_notify_info, msg_data, from_addr): '''Parse the notify reply message. - TODO, the error message should be refined properly. rcode will not checked here, If we get the response from the slave, it means the slaves has got the notify.''' msg = Message(Message.PARSE) try: - #errstr = 'notify reply error: ' msg.from_wire(msg_data) if not msg.get_header_flag(Message.HEADERFLAG_QR): - logger.error(NOTIFY_OUT_REPLY_QR_NOT_SET, from_addr[0], - from_addr[1]) + logger.warn(NOTIFY_OUT_REPLY_QR_NOT_SET, from_addr[0], + from_addr[1]) return _BAD_QR if msg.get_qid() != zone_notify_info.notify_msg_id: - logger.error(NOTIFY_OUT_REPLY_BAD_QID, from_addr[0], - from_addr[1], msg.get_qid(), - zone_notify_info.notify_msg_id) + logger.warn(NOTIFY_OUT_REPLY_BAD_QID, from_addr[0], + from_addr[1], msg.get_qid(), + zone_notify_info.notify_msg_id) return _BAD_QUERY_ID question = msg.get_question()[0] if question.get_name() != Name(zone_notify_info.zone_name): - logger.error(NOTIFY_OUT_REPLY_BAD_QUERY_NAME, from_addr[0], - from_addr[1], question.get_name().to_text(), - Name(zone_notify_info.zone_name).to_text()) + logger.warn(NOTIFY_OUT_REPLY_BAD_QUERY_NAME, from_addr[0], + from_addr[1], question.get_name().to_text(), + Name(zone_notify_info.zone_name).to_text()) return _BAD_QUERY_NAME if msg.get_opcode() != Opcode.NOTIFY(): - logger.error(NOTIFY_OUT_REPLY_BAD_OPCODE, from_addr[0], - from_addr[1], msg.get_opcode().to_text()) + logger.warn(NOTIFY_OUT_REPLY_BAD_OPCODE, from_addr[0], + from_addr[1], msg.get_opcode().to_text()) return _BAD_OPCODE except Exception as err: # We don't care what exception, just report it? diff --git a/src/lib/python/isc/notify/notify_out_messages.mes b/src/lib/python/isc/notify/notify_out_messages.mes index af89612b56..797643e67d 100644 --- a/src/lib/python/isc/notify/notify_out_messages.mes +++ b/src/lib/python/isc/notify/notify_out_messages.mes @@ -15,7 +15,7 @@ # No namespace declaration - these constants go in the global namespace # of the notify_out_messages python module. -% NOTIFY_OUT_IMPORT error importing python module: %1 +% NOTIFY_OUT_IMPORT_ERROR error importing python module: %1 There was an error importing a python module. One of the modules needed by notify_out could not be found. This suggests that either some libraries are missing on the system, or the PYTHONPATH variable @@ -25,28 +25,35 @@ depends on your system and your specific installation. % NOTIFY_OUT_INVALID_ADDRESS invalid address %1#%2: %3 The notify_out library tried to send a notify packet to the given address, but it appears to be an invalid address. The configuration -for secondary nameservers might contain a typographic error. +for secondary nameservers might contain a typographic error, or a +different BIND 10 module has forgotten to validate its data before +sending this module a notify command. As such, this should normally +not happen, and points to an oversight in a different module. % NOTIFY_OUT_REPLY_BAD_OPCODE bad opcode in notify reply from %1#%2: %3 -The notify_out library sent a notify packet to the nameserver at the -given address, but the response did not have the opcode set to NOTIFY. -The opcode in the response is printed. +The notify_out library sent a notify message to the nameserver at +the given address, but the response did not have the opcode set to +NOTIFY. The opcode in the response is printed. Since there was a +response, no more notifies will be sent to this server for this +notification event. % NOTIFY_OUT_REPLY_BAD_QID bad QID in notify reply from %1#%2: got %3, should be %4 -The notify_out library sent a notify packet to the nameserver at the -given address, but the query id in the response does not match the one -we sent. +The notify_out library sent a notify message to the nameserver at +the given address, but the query id in the response does not match +the one we sent. Since there was a response, no more notifies will +be sent to this server for this notification event. % NOTIFY_OUT_REPLY_BAD_QUERY_NAME bad query name in notify reply from %1#%2: got %3, should be %4 -The notify_out library sent a notify packet to the nameserver at the -given address, but the query name in the response does not match the one -we sent. +The notify_out library sent a notify message to the nameserver at +the given address, but the query name in the response does not match +the one we sent. Since there was a response, no more notifies will +be sent to this server for this notification event. % NOTIFY_OUT_REPLY_QR_NOT_SET QR flags set to 0 in reply to notify from %1#%2 The notify_out library sent a notify packet to the namesever at the given address, but the reply did not have the QR bit set to one. -% NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries exceeded +% NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries (%3) exceeded The maximum number of retries for the notify target has been exceeded. Either the address of the secondary nameserver is wrong, or it is not responding. @@ -56,22 +63,24 @@ A notify message is sent to the secondary nameserver at the given address. % NOTIFY_OUT_SOCKET_ERROR socket error sending notify to %1#%2: %3 -There was a network connection error while trying to send a notify -packet to the given address. The socket error is printed and should -provide more information. +There was a network error while trying to send a notify packet to +the given address. The address might be unreachable. The socket +error is printed and should provide more information. % NOTIFY_OUT_SOCKET_RECV_ERROR socket error reading notify reply from %1#%2: %3 -There was a network connection error while trying to read a notify reply +There was a network error while trying to read a notify reply packet from the given address. The socket error is printed and should provide more information. -% NOTIFY_OUT_TIMEOUT_RETRY retry notify to %1#%2 +% NOTIFY_OUT_TIMEOUT retry notify to %1#%2 The notify message to the given address (noted as address#port) has -timed out, and the message is sent again. +timed out, and the message will be resent until the max retry limit +is reached. % NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION uncaught exception: %1 There was an uncaught exception in the handling of a notify reply -packet. The error is printed, and notify_out will treat the response -as a bad packet, but this does point to a programming error, since -all exceptions should have been caught explicitely. Please file -a bug report. +packet, either in the packet parser, or while trying to extract data +from the parsed packet. The error is printed, and notify_out will +treat the response as a bad packet, but this does point to a +programming error, since all exceptions should have been caught +explicitely. Please file a bug report. diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py index 8ac9ba7481..83f6d1ae1e 100644 --- a/src/lib/python/isc/notify/tests/notify_out_test.py +++ b/src/lib/python/isc/notify/tests/notify_out_test.py @@ -301,6 +301,15 @@ class TestNotifyOut(unittest.TestCase): self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_NONE) self.assertNotEqual(cur_tgt, example_net_info._notify_current) + cur_tgt = example_net_info._notify_current + example_net_info.create_socket('127.0.0.1') + # dns message, will result in bad_qid, but what we are testing + # here is whether handle_notify_reply is called correctly + example_net_info._sock.remote_end().send(b'\x2f\x18\xa0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01') + self._notify._zone_notify_handler(example_net_info, notify_out._EVENT_READ) + self.assertNotEqual(cur_tgt, example_net_info._notify_current) + + def _example_net_data_reader(self): zone_data = [ ('example.net.', '1000', 'IN', 'SOA', 'a.dns.example.net. mail.example.net. 1 1 1 1 1'), diff --git a/src/lib/util/filename.cc b/src/lib/util/filename.cc index 70e054dd72..d7da9c81d4 100644 --- a/src/lib/util/filename.cc +++ b/src/lib/util/filename.cc @@ -134,14 +134,20 @@ Filename::useAsDefault(const string& name) const { void Filename::setDirectory(const std::string& new_directory) { - directory_ = new_directory; - // append '/' if necessary - size_t sep = directory_.rfind('/'); - if (sep == std::string::npos || sep < directory_.size() - 1) { - directory_ += "/"; + std::string directory(new_directory); + + if (directory.length() > 0) { + // append '/' if necessary + size_t sep = directory.rfind('/'); + if (sep == std::string::npos || sep < directory.size() - 1) { + directory += "/"; + } } // and regenerate the full name - full_name_ = directory_ + name_ + extension_; + std::string full_name = directory + name_ + extension_; + + directory_.swap(directory); + full_name_.swap(full_name); } diff --git a/src/lib/util/filename.h b/src/lib/util/filename.h index 1fcbbc7e4d..c9874ce220 100644 --- a/src/lib/util/filename.h +++ b/src/lib/util/filename.h @@ -86,7 +86,11 @@ public: return (directory_); } - /// \return Set directory for the file + /// \brief Set directory for the file + /// + /// \param new_directory The directory to set. If this is an empty + /// string, the directory this filename object currently + /// has will be removed. void setDirectory(const std::string& new_directory); /// \return Name of Given File Name diff --git a/src/lib/util/tests/filename_unittest.cc b/src/lib/util/tests/filename_unittest.cc index c6706d6970..be29ff18ea 100644 --- a/src/lib/util/tests/filename_unittest.cc +++ b/src/lib/util/tests/filename_unittest.cc @@ -200,9 +200,9 @@ TEST_F(FilenameTest, setDirectory) { EXPECT_EQ("/a.b", fname.expandWithDefault("")); fname.setDirectory(""); - EXPECT_EQ("/", fname.directory()); - EXPECT_EQ("/a.b", fname.fullName()); - EXPECT_EQ("/a.b", fname.expandWithDefault("")); + EXPECT_EQ("", fname.directory()); + EXPECT_EQ("a.b", fname.fullName()); + EXPECT_EQ("a.b", fname.expandWithDefault("")); fname = Filename("/first/a.b"); EXPECT_EQ("/first/", fname.directory()); From 1658417daeac170c4068fbbc6f4e3762ada0e72c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 8 Jul 2011 19:04:35 +0200 Subject: [PATCH 145/974] [trac763] addressed review comments --- src/bin/stats/stats.py.in | 2 +- src/bin/stats/stats_httpd.py.in | 42 +++++++++--------- src/bin/stats/stats_httpd_messages.mes | 60 +++++++++++++------------- src/bin/stats/stats_messages.mes | 27 ++++++------ 4 files changed, 67 insertions(+), 64 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 38d305bf2d..ce3d9f4612 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -224,7 +224,7 @@ class CCSessionListener(Listener): self.stats_data['stats.lname'] = self.session.lname self.cc_session.start() # request Bob to send statistics data - logger.debug(DBG_STATS_MESSAGING, STATS_SEND_STATS_REQUEST_BOSS) + logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) cmd = isc.config.ccsession.create_command("sendstats", None) seq = self.session.group_sendmsg(cmd, 'Boss') self.session.group_recvmsg(True, seq) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index e449f59574..ea68e7d122 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -42,8 +42,8 @@ logger = isc.log.Logger("stats-httpd") # Some constants for debug levels, these should be removed when we # have #1074 -DBG_STATS_HTTPD_INIT = 10 -DBG_STATS_HTTPD_MESSAGING = 30 +DBG_STATHTTPD_INIT = 10 +DBG_STATHTTPD_MESSAGING = 30 # If B10_FROM_SOURCE is set in the environment, we use data files # from a directory relative to that, otherwise we use the ones @@ -109,7 +109,7 @@ class HttpHandler(http.server.BaseHTTPRequestHandler): return None except StatsHttpdError as err: self.send_error(500) - logger.error(STATS_HTTPD_SERVER_ERROR, err) + logger.error(STATHTTPD_SERVER_ERROR, err) return None else: self.send_response(200) @@ -167,7 +167,7 @@ class StatsHttpd: def open_mccs(self): """Opens a ModuleCCSession object""" # create ModuleCCSession - logger.debug(DBG_STATS_HTTPD_INIT, STATS_HTTPD_STARTING_CC_SESSION) + logger.debug(DBG_STATHTTPD_INIT, STATHTTPD_STARTING_CC_SESSION) self.mccs = isc.config.ModuleCCSession( SPECFILE_LOCATION, self.config_handler, self.command_handler) self.cc_session = self.mccs._session @@ -181,7 +181,7 @@ class StatsHttpd: if self.mccs is None: return - logger.debug(DBG_STATS_HTTPD_INIT, STATS_HTTPD_CLOSING_CC_SESSION) + logger.debug(DBG_STATHTTPD_INIT, STATHTTPD_CLOSING_CC_SESSION) self.mccs.close() self.mccs = None @@ -230,7 +230,7 @@ class StatsHttpd: (server_address[0], server_address[1], err.__class__.__name__, err)) else: - logger.info(STATS_HTTPD_STARTED, server_address[0], + logger.info(STATHTTPD_STARTED, server_address[0], server_address[1]) return httpd @@ -239,7 +239,7 @@ class StatsHttpd: if len(self.httpd) == 0: return for ht in self.httpd: - logger.info(STATS_HTTPD_CLOSING, ht.server_address[0], + logger.info(STATHTTPD_CLOSING, ht.server_address[0], ht.server_address[1]) ht.server_close() self.httpd = [] @@ -277,7 +277,7 @@ class StatsHttpd: def stop(self): """Stops the running StatsHttpd objects. Closes CC session and HTTP handling sockets""" - logger.info(STATS_HTTPD_SHUTDOWN) + logger.info(STATHTTPD_SHUTDOWN) self.close_httpd() self.close_mccs() @@ -294,11 +294,11 @@ class StatsHttpd: def config_handler(self, new_config): """Config handler for the ModuleCCSession object. It resets addresses and ports to listen HTTP requests on.""" - logger.debug(DBG_STATS_HTTPD_MESSAGING, STATS_HTTPD_HANDLE_CONFIG, + logger.debug(DBG_STATHTTPD_MESSAGING, STATHTTPD_HANDLE_CONFIG, new_config) for key in new_config.keys(): if key not in DEFAULT_CONFIG and key != "version": - logger.error(STATS_HTTPD_UNKNOWN_CONFIG_ITEM, key) + logger.error(STATHTTPD_UNKNOWN_CONFIG_ITEM, key) return isc.config.ccsession.create_answer( 1, "Unknown known config: %s" % key) # backup old config @@ -308,7 +308,7 @@ class StatsHttpd: try: self.open_httpd() except HttpServerError as err: - logger.error(STATS_HTTPD_SERVER_ERROR, err) + logger.error(STATHTTPD_SERVER_ERROR, err) # restore old config self.config_handler(old_config) return isc.config.ccsession.create_answer( @@ -320,19 +320,19 @@ class StatsHttpd: """Command handler for the ModuleCCSesson object. It handles "status" and "shutdown" commands.""" if command == "status": - logger.debug(DBG_STATS_HTTPD_MESSAGING, - STATS_HTTPD_RECEIVED_STATUS_COMMAND) + logger.debug(DBG_STATHTTPD_MESSAGING, + STATHTTPD_RECEIVED_STATUS_COMMAND) return isc.config.ccsession.create_answer( 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")") elif command == "shutdown": - logger.debug(DBG_STATS_HTTPD_MESSAGING, - STATS_HTTPD_RECEIVED_SHUTDOWN_COMMAND) + logger.debug(DBG_STATHTTPD_MESSAGING, + STATHTTPD_RECEIVED_SHUTDOWN_COMMAND) self.running = False return isc.config.ccsession.create_answer( 0, "Stats Httpd is shutting down.") else: - logger.debug(DBG_STATS_HTTPD_MESSAGING, - STATS_HTTPD_RECEIVED_UNKNOWN_COMMAND, command) + logger.debug(DBG_STATHTTPD_MESSAGING, + STATHTTPD_RECEIVED_UNKNOWN_COMMAND, command) return isc.config.ccsession.create_answer( 1, "Unknown command: " + str(command)) @@ -471,13 +471,13 @@ if __name__ == "__main__": stats_httpd = StatsHttpd() stats_httpd.start() except OptionValueError as ove: - logger.fatal(STATS_HTTPD_BAD_OPTION_VALUE, ove) + logger.fatal(STATHTTPD_BAD_OPTION_VALUE, ove) sys.exit(1) except isc.cc.session.SessionError as se: - logger.fatal(STATS_HTTPD_CC_SESSION_ERROR, se) + logger.fatal(STATHTTPD_CC_SESSION_ERROR, se) sys.exit(1) except HttpServerError as hse: - logger.fatal(STATS_HTTPD_START_SERVER_ERROR, hse) + logger.fatal(STATHTTPD_START_SERVER_ERROR, hse) sys.exit(1) except KeyboardInterrupt as kie: - logger.info(STATS_HTTPD_STOPPED_BY_KEYBOARD) + logger.info(STATHTTPD_STOPPED_BY_KEYBOARD) diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes index bbcb058fc0..100c0e816e 100644 --- a/src/bin/stats/stats_httpd_messages.mes +++ b/src/bin/stats/stats_httpd_messages.mes @@ -15,75 +15,75 @@ # No namespace declaration - these constants go in the global namespace # of the stats_httpd_messages python module. -% STATS_HTTPD_BAD_OPTION_VALUE bad command line argument: %1 -The stats-httpd module was called with a bad command-line argument, +% STATHTTPD_BAD_OPTION_VALUE bad command line argument: %1 +The stats-httpd module was called with a bad command-line argument and will not start. -% STATS_HTTPD_CC_SESSION_ERROR error connecting to message bus: %1 +% STATHTTPD_CC_SESSION_ERROR error connecting to message bus: %1 The stats-httpd module was unable to connect to the BIND 10 command -and control channel. A likely problem is that the message bus daemon +and control bus. A likely problem is that the message bus daemon (b10-msgq) is not running. The stats-httpd module will now shut down. -% STATS_HTTPD_CLOSING_CC_SESSION stopping cc session -Debug message indicating that the stats httpd module is disconnecting -from the command and control channel. +% STATHTTPD_CLOSING_CC_SESSION stopping cc session +Debug message indicating that the stats-httpd module is disconnecting +from the command and control bus. -% STATS_HTTPD_CLOSING closing %1#%2 -The stats-httpd daemon will stop listening for requests on te given +% STATHTTPD_CLOSING closing %1#%2 +The stats-httpd daemon will stop listening for requests on the given address and port number. -% STATS_HTTPD_HANDLE_CONFIG reading configuration: %1 +% STATHTTPD_HANDLE_CONFIG reading configuration: %1 The stats-httpd daemon has received new configuration data and will now process it. The (changed) data is printed. -% STATS_HTTPD_RECEIVED_SHUTDOWN_COMMAND shutdown command received +% STATHTTPD_RECEIVED_SHUTDOWN_COMMAND shutdown command received A shutdown command was sent to the stats-httpd module, and it will now shut down. -% STATS_HTTPD_RECEIVED_STATUS_COMMAND received command to return status +% STATHTTPD_RECEIVED_STATUS_COMMAND received command to return status A status command was sent to the stats-httpd module, and it will respond with 'Stats Httpd is up.' and its PID. -% STATS_HTTPD_RECEIVED_UNKNOWN_COMMAND received unknown command: %1 +% STATHTTPD_RECEIVED_UNKNOWN_COMMAND received unknown command: %1 An unknown command has been sent to the stats-httpd module. The stats-httpd module will respond with an error, and the command will be ignored. -% STATS_HTTPD_SERVER_ERROR http server error: %1 +% STATHTTPD_SERVER_ERROR http server error: %1 An internal error occurred while handling an http request. A HTTP 500 response will be sent back, and the specific error is printed. This -is an error condition that should not occur, and likely points to a -module that is not responding correctly to statistic requests. +is an error condition that likely points to a module that is not +responding correctly to statistic requests. -% STATS_HTTPD_SERVER_INIT_ERROR http server initialization error: %1 +% STATHTTPD_SERVER_INIT_ERROR http server initialization error: %1 There was a problem initializing the http server in the stats-httpd -module upon receiving new configuration data.The most likely cause is a -port binding problem or a bad configuration value. The specific error -is printed in the message. The new configuration is ignored, and an -error is sent back. +module upon receiving its configuration data. The most likely cause +is a port binding problem or a bad configuration value. The specific +error is printed in the message. The new configuration is ignored, +and an error is sent back. -% STATS_HTTPD_SHUTDOWN shutting down -The stats-httpd daemon will now shut down. +% STATHTTPD_SHUTDOWN shutting down +The stats-httpd daemon is shutting down. -% STATS_HTTPD_START_SERVER_INIT_ERROR http server initialization error: %1 +% STATHTTPD_START_SERVER_INIT_ERROR http server initialization error: %1 There was a problem initializing the http server in the stats-httpd module upon startup. The most likely cause is that it was not able to bind to the listening port. The specific error is printed, and the module will shut down. -% STATS_HTTPD_STARTED listening on %1#%2 +% STATHTTPD_STARTED listening on %1#%2 The stats-httpd daemon will now start listening for requests on the given address and port number. -% STATS_HTTPD_STARTING_CC_SESSION starting cc session -Debug message indicating that the stats httpd module is connecting to -the command and control channel. +% STATHTTPD_STARTING_CC_SESSION starting cc session +Debug message indicating that the stats-httpd module is connecting to +the command and control bus. -% STATS_HTTPD_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down +% STATHTTPD_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down There was a keyboard interrupt signal to stop the stats-httpd daemon. The daemon will now shut down. -% STATS_HTTPD_UNKNOWN_CONFIG_ITEM unknown configuration item: %1 +% STATHTTPD_UNKNOWN_CONFIG_ITEM unknown configuration item: %1 The stats-httpd daemon received new configuration. However, one of the items in the configuration is unknown. The new configuration is ignored, and an error is sent back. As possible cause is that there was an diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes index eb896f68ba..8d3c047c0b 100644 --- a/src/bin/stats/stats_messages.mes +++ b/src/bin/stats/stats_messages.mes @@ -16,24 +16,27 @@ # of the stats_messages python module. % STATS_BAD_OPTION_VALUE bad command line argument: %1 -The stats module was called with a bad command-line argument, and will +The stats module was called with a bad command-line argument and will not start. % STATS_CC_SESSION_ERROR error connecting to message bus: %1 The stats module was unable to connect to the BIND 10 command and -control channel. A likely problem is that the message bus daemon +control bus. A likely problem is that the message bus daemon (b10-msgq) is not running. The stats module will now shut down. % STATS_RECEIVED_NEW_CONFIG received new configuration: %1 -This debug message is printed when the configuration manager has sent -the stats module a configuration update. +This debug message is printed when the stats module has received a +configuration update from the configuration manager. % STATS_RECEIVED_REMOVE_COMMAND received command to remove %1 A remove command for the given name was sent to the stats module, and -it will now be removed. +the given statistics value will now be removed. It will not appear in +statistics reports until it appears in a statistics update from a +module again. % STATS_RECEIVED_RESET_COMMAND received command to reset all statistics -The stats module received a command to reset all collected statistics. +The stats module received a command to clear all collected statistics. +The data is cleared until it receives an update from the modules again. % STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics The stats module received a command to show all statistics that it has @@ -44,19 +47,19 @@ The stats module received a command to show the statistics that it has collected for the given item. % STATS_RECEIVED_SHUTDOWN_COMMAND shutdown command received -A shutdown command was sent to the stats module, and it will now shut down. +A shutdown command was sent to the stats module and it will now shut down. % STATS_RECEIVED_STATUS_COMMAND received command to return status -A status command was sent to the stats module, and it will respond with -'I'm alive'. +A status command was sent to the stats module. It will return a +response indicating that it is running normally. % STATS_RECEIVED_UNKNOWN_COMMAND received unknown command: %1 An unknown command has been sent to the stats module. The stats module -will respond with an error, and the command will be ignored. +will respond with an error and the command will be ignored. -% STATS_SEND_STATS_REQUEST_BOSS requesting boss to send statistics data +% STATS_SEND_REQUEST_BOSS requesting boss to send statistics This debug message is printed when a request is sent to the boss module -to send statistics data to the stats module. +to send its data to the stats module. % STATS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down There was a keyboard interrupt signal to stop the stats daemon. The From 933adf250348caf92c04f5249120333e3e300227 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 8 Jul 2011 19:08:37 +0200 Subject: [PATCH 146/974] [trac763] forgot a change when addressing review comments --- src/bin/stats/stats_httpd_messages.mes | 11 ++++++----- src/bin/stats/stats_messages.mes | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes index 100c0e816e..d0f7e2cb3c 100644 --- a/src/bin/stats/stats_httpd_messages.mes +++ b/src/bin/stats/stats_httpd_messages.mes @@ -84,8 +84,9 @@ There was a keyboard interrupt signal to stop the stats-httpd daemon. The daemon will now shut down. % STATHTTPD_UNKNOWN_CONFIG_ITEM unknown configuration item: %1 -The stats-httpd daemon received new configuration. However, one of the -items in the configuration is unknown. The new configuration is ignored, -and an error is sent back. As possible cause is that there was an -upgrade problem, and the stats-httpd version is out of sync with the -rest of the system. +The stats-httpd daemon received a configuration update from the +configuration manager. However, one of the items in the +configuration is unknown. The new configuration is ignored, and an +error is sent back. As possible cause is that there was an upgrade +problem, and the stats-httpd version is out of sync with the rest of +the system. diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes index 8d3c047c0b..9ad07cf493 100644 --- a/src/bin/stats/stats_messages.mes +++ b/src/bin/stats/stats_messages.mes @@ -62,7 +62,7 @@ This debug message is printed when a request is sent to the boss module to send its data to the stats module. % STATS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down -There was a keyboard interrupt signal to stop the stats daemon. The +There was a keyboard interrupt signal to stop the stats module. The daemon will now shut down. % STATS_UNKNOWN_COMMAND_IN_SPEC unknown command in specification file: %1 From 832cd1c032222fec662f9320e6f564f55b75cc8a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 8 Jul 2011 11:03:56 -0700 Subject: [PATCH 147/974] [trac983] removed a garbage copyright notice as pointed out in review. --- src/lib/python/isc/acl/dns.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/lib/python/isc/acl/dns.py b/src/lib/python/isc/acl/dns.py index 9eaf33d1ba..8070559b0a 100644 --- a/src/lib/python/isc/acl/dns.py +++ b/src/lib/python/isc/acl/dns.py @@ -24,25 +24,6 @@ import os import sys -# 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 file is not installed; The .so version will be installed into the right -# place at installation time. -# This helper script is only to find it in the .libs directory when we run -# as a test or from the build directory. for base in sys.path[:]: bindingdir = os.path.join(base, 'isc/acl/.libs') From e090ab50879c15c850df8e8145f01d39dbd6b87b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 8 Jul 2011 12:43:52 -0700 Subject: [PATCH 148/974] [trac983] changed the exception type for the invalid construction of RequestACL as a result of review. --- src/lib/python/isc/acl/dns_requestacl_python.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc index 00829ce3e7..326c7172ef 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.cc +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -60,8 +60,7 @@ s_RequestACL::s_RequestACL() {} namespace { int RequestACL_init(PyObject*, PyObject*, PyObject*) { - PyErr_SetString(PyExc_TypeError, - "RequestACL cannot be directly constructed"); + PyErr_SetString(po_ACLError, "RequestACL cannot be directly constructed"); return (-1); } From 570bbcef51aa6a5bc920faabd850cd6a86c0d421 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 8 Jul 2011 14:58:49 -0700 Subject: [PATCH 149/974] [trac910] added a new method getTSIGLength() to TSIGContext, which will be necessary for TC bit support. --- src/lib/dns/tests/tsig_unittest.cc | 72 ++++++++++++++++++++++++++++++ src/lib/dns/tsig.cc | 39 ++++++++++++++++ src/lib/dns/tsig.h | 4 ++ 3 files changed, 115 insertions(+) diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc index ba17e70b27..ed94055704 100644 --- a/src/lib/dns/tests/tsig_unittest.cc +++ b/src/lib/dns/tests/tsig_unittest.cc @@ -927,4 +927,76 @@ TEST_F(TSIGTest, tooShortMAC) { } } +TEST_F(TSIGTest, getTSIGLength) { + // Check for the most common case with various algorithms + // See the comment in TSIGContext::getTSIGLength() for calculation and + // parameter notation. + // The key name (www.example.com) is the same for most cases, where n1=17 + + // hmac-md5.sig-alg.reg.int.: n2=26, x=16 + EXPECT_EQ(85, tsig_ctx->getTSIGLength()); + + // hmac-sha1: n2=11, x=20 + tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(), + &dummy_data[0], 20))); + EXPECT_EQ(74, tsig_ctx->getTSIGLength()); + + // hmac-sha256: n2=13, x=32 + tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA256_NAME(), + &dummy_data[0], 32))); + EXPECT_EQ(88, tsig_ctx->getTSIGLength()); + + // hmac-sha224: n2=13, x=28 + tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA224_NAME(), + &dummy_data[0], 28))); + EXPECT_EQ(84, tsig_ctx->getTSIGLength()); + + // hmac-sha384: n2=13, x=48 + tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA384_NAME(), + &dummy_data[0], 48))); + EXPECT_EQ(104, tsig_ctx->getTSIGLength()); + + // hmac-sha512: n2=13, x=64 + tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA512_NAME(), + &dummy_data[0], 64))); + EXPECT_EQ(120, tsig_ctx->getTSIGLength()); + + // bad key case: n1=len(badkey.example.com)=20, n2=26, x=0 + tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(), + keyring)); + EXPECT_EQ(72, tsig_ctx->getTSIGLength()); + + // bad sig case: n1=17, n2=26, x=0 + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("message_toWire2.wire"); + tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), + &dummy_data[0], + dummy_data.size()))); + { + SCOPED_TRACE("Verify resulting in BADSIG"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + EXPECT_EQ(69, tsig_ctx->getTSIGLength()); + + // bad time case: n1=17, n2=26, x=16, y=6 + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a - 1000>; + tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), + &dummy_data[0], + dummy_data.size()))); + { + SCOPED_TRACE("Verify resulting in BADTIME"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } + EXPECT_EQ(91, tsig_ctx->getTSIGLength()); +} + } // end namespace diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc index f0b37d35b3..0472e4b8eb 100644 --- a/src/lib/dns/tsig.cc +++ b/src/lib/dns/tsig.cc @@ -270,6 +270,45 @@ TSIGContext::~TSIGContext() { delete impl_; } +size_t +TSIGContext::getTSIGLength() const { + // + // The space required for an TSIG record is: + // + // n1 bytes for the (key) name + // 2 bytes for the type + // 2 bytes for the class + // 4 bytes for the ttl + // 2 bytes for the rdlength + // n2 bytes for the algorithm name + // 6 bytes for the time signed + // 2 bytes for the fudge + // 2 bytes for the MAC size + // x bytes for the MAC + // 2 bytes for the original id + // 2 bytes for the error + // 2 bytes for the other data length + // y bytes for the other data (at most) + // --------------------------------- + // 26 + n1 + n2 + x + y bytes + // + + // Normally the digest length ("x") is the length of the underlying + // hash output. If a key related error occurred, however, the + // corresponding TSIG will be "unsigned", and the digest length will be 0. + const size_t digest_len = + (impl_->error_ == TSIGError::BAD_KEY() || + impl_->error_ == TSIGError::BAD_SIG()) ? 0 : impl_->digest_len_; + + // Other Len ("y") is normally 0; if BAD_TIME error occurred, the + // subsequent TSIG will contain 48 bits of the server current time. + const size_t other_len = (impl_->error_ == TSIGError::BAD_TIME()) ? 6 : 0; + + return (26 + impl_->key_.getKeyName().getLength() + + impl_->key_.getAlgorithmName().getLength() + + digest_len + other_len); +} + TSIGContext::State TSIGContext::getState() const { return (impl_->state_); diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h index bceec25295..80a49de25c 100644 --- a/src/lib/dns/tsig.h +++ b/src/lib/dns/tsig.h @@ -353,6 +353,10 @@ public: TSIGError verify(const TSIGRecord* const record, const void* const data, const size_t data_len); + /// TBD: mostly for internal use. context dependent. + /// won't provide python binding. + size_t getTSIGLength() const; + /// Return the current state of the context /// /// \note From d7d60797272f02e6f3f09b659922c71f2c49ffec Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 8 Jul 2011 17:41:54 -0700 Subject: [PATCH 150/974] [trac910] supported TSIG+TC case, first step: if TC is set even before adding the TSIG, clear everything. --- src/lib/dns/message.cc | 15 +++++++ src/lib/dns/tests/message_unittest.cc | 58 ++++++++++++++++++++++---- src/lib/dns/tests/testdata/Makefile.am | 5 ++- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index bf7ccd52be..cdc62ab289 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -284,6 +284,21 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { } } + // If we're adding a TSIG to a truncated message, clear all RRsets + // from the message except for the question before adding the TSIG. + // If even the question doesn't fit, don't include any question (TBD). + if (tsig_ctx != NULL && renderer.isTruncated()) { + renderer.clear(); + renderer.skip(HEADERLEN); + qdcount = + for_each(questions_.begin(), questions_.end(), + RenderSection(renderer, + false)).getTotalCount(); + ancount = 0; + nscount = 0; + arcount = 0; + } + // Adjust the counter buffer. // XXX: these may not be equal to the number of corresponding entries // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index c79ea2c414..949899152d 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -62,7 +62,6 @@ using namespace isc::dns::rdata; // const uint16_t Message::DEFAULT_MAX_UDPSIZE; -const Name test_name("test.example.com"); namespace isc { namespace util { @@ -79,7 +78,8 @@ const uint16_t TSIGContext::DEFAULT_FUDGE; namespace { class MessageTest : public ::testing::Test { protected: - MessageTest() : obuffer(0), renderer(obuffer), + MessageTest() : test_name("test.example.com"), obuffer(0), + renderer(obuffer), message_parse(Message::PARSE), message_render(Message::RENDER), bogus_section(static_cast( @@ -103,8 +103,9 @@ protected: "FAKEFAKEFAKEFAKE")); rrset_aaaa->addRRsig(rrset_rrsig); } - + static Question factoryFromFile(const char* datafile); + const Name test_name; OutputBuffer obuffer; MessageRenderer renderer; Message message_parse; @@ -114,17 +115,18 @@ protected: RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset TSIGContext tsig_ctx; + vector received_data; vector expected_data; - static void factoryFromFile(Message& message, const char* datafile); + void factoryFromFile(Message& message, const char* datafile); }; void MessageTest::factoryFromFile(Message& message, const char* datafile) { - std::vector data; - UnitTestUtil::readWireData(datafile, data); + received_data.clear(); + UnitTestUtil::readWireData(datafile, received_data); - InputBuffer buffer(&data[0], data.size()); + InputBuffer buffer(&received_data[0], received_data.size()); message.fromWire(buffer); } @@ -624,7 +626,7 @@ commonTSIGToWireCheck(Message& message, MessageRenderer& renderer, { message.setOpcode(Opcode::QUERY()); message.setRcode(Rcode::NOERROR()); - message.setHeaderFlag(Message::HEADERFLAG_RD, true); + message.setHeaderFlag(Message::HEADERFLAG_RD); message.addQuestion(Question(Name("www.example.com"), RRClass::IN(), RRType::A())); @@ -670,6 +672,46 @@ TEST_F(MessageTest, toWireWithEDNSAndTSIG) { } } +const char* const long_txt1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde"; +const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456"; + +// Example output generated by +// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt +// QID: 0x22c2 +// Time Signed: 0x00004e179212 +// Query MAC: 8214b04634e32323d651ac60b08e6388 +// Response MAC: 88adc3811d1d6bec7c684438906fc694 +TEST_F(MessageTest, toWireTSIGTruncation) { + isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; + + factoryFromFile(message_parse, "message_fromWire17.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + // Note: the following should be merged to commonTSIGToWireCheck: + message_render.setQid(0x22c2); + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + message_render.setHeaderFlag(Message::HEADERFLAG_QR); + message_render.setHeaderFlag(Message::HEADERFLAG_RD); + message_render.setHeaderFlag(Message::HEADERFLAG_AA); + message_render.addQuestion(Question(Name("www.example.com"), + RRClass::IN(), RRType::TXT())); + RRsetPtr rrset(new RRset(Name("www.example.com"), RRClass::IN(), + RRType::TXT(), RRTTL(86400))); + rrset->addRdata(generic::TXT(long_txt1)); + rrset->addRdata(generic::TXT(long_txt2)); + message_render.addRRset(Message::SECTION_ANSWER, rrset); + message_render.toWire(renderer, tsig_ctx); + + vector expected_data; + UnitTestUtil::readWireData("message_toWire4.wire", expected_data); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(), + renderer.getLength(), + &expected_data[0], expected_data.size()); +} + TEST_F(MessageTest, toWireWithoutOpcode) { message_render.setRcode(Rcode::NOERROR()); EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation); diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index cb1bb1cad6..f42f0f9f91 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -5,8 +5,9 @@ BUILT_SOURCES += edns_toWire4.wire BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire -BUILT_SOURCES += message_fromWire16.wire +BUILT_SOURCES += message_fromWire16.wire message_fromWire17.wire BUILT_SOURCES += message_toWire2.wire message_toWire3.wire +BUILT_SOURCES += message_toWire4.wire BUILT_SOURCES += message_toText1.wire message_toText2.wire BUILT_SOURCES += message_toText3.wire BUILT_SOURCES += name_toWire5.wire name_toWire6.wire @@ -59,7 +60,9 @@ EXTRA_DIST += message_fromWire9 message_fromWire10.spec EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec +EXTRA_DIST += message_fromWire17.spec EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec +EXTRA_DIST += message_toWire4.wire EXTRA_DIST += message_toText1.txt message_toText1.spec EXTRA_DIST += message_toText2.txt message_toText2.spec EXTRA_DIST += message_toText3.txt message_toText3.spec From 56b188c6e4e36a28b54cab442677e2fa748f0bae Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 8 Jul 2011 18:01:45 -0700 Subject: [PATCH 151/974] [trac910] refactored test code a bit to unify TSIG check tests --- src/lib/dns/tests/message_unittest.cc | 82 +++++++++++++++++++-------- 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 949899152d..add6ab7701 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -620,15 +620,47 @@ testGetTime() { return (NOW); } +// bit-wise constant flags to configure DNS header flags for test +// messages. +const unsigned int QR_FLAG = 0x1; +const unsigned int AA_FLAG = 0x2; +const unsigned int RD_FLAG = 0x4; +const unsigned int TC_FLAG = 0x8; + void commonTSIGToWireCheck(Message& message, MessageRenderer& renderer, - TSIGContext& tsig_ctx, const char* const expected_file) + TSIGContext& tsig_ctx, const char* const expected_file, + unsigned int message_flags = RD_FLAG, + RRType qtype = RRType::A(), + const vector* answer_data = NULL) { message.setOpcode(Opcode::QUERY()); message.setRcode(Rcode::NOERROR()); - message.setHeaderFlag(Message::HEADERFLAG_RD); + if ((message_flags & QR_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_QR); + } + if ((message_flags & AA_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_AA); + } + if ((message_flags & RD_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_RD); + } + if ((message_flags & TC_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_TC); + } message.addQuestion(Question(Name("www.example.com"), RRClass::IN(), - RRType::A())); + qtype)); + + if (answer_data != NULL) { + RRsetPtr ans_rrset(new RRset(Name("www.example.com"), RRClass::IN(), + qtype, RRTTL(86400))); + for (vector::const_iterator it = answer_data->begin(); + it != answer_data->end(); + ++it) { + ans_rrset->addRdata(createRdata(qtype, RRClass::IN(), *it)); + } + message.addRRset(Message::SECTION_ANSWER, ans_rrset); + } message.toWire(renderer, tsig_ctx); vector expected_data; @@ -672,7 +704,18 @@ TEST_F(MessageTest, toWireWithEDNSAndTSIG) { } } +// Some of the following tests involve truncation. We use the query name +// "www.example.com" and some TXT question/answers. The length of the +// header and question will be 33 bytes. If we also try to include a +// TSIG of the same key name (not compressed) with HMAC-MD5, the TSIG RR +// will be 85 bytes. + +// A long TXT RDATA. With a fully compressed owner name, the corresponding +// RR will be 268 bytes. const char* const long_txt1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde"; + +// With a fully compressed owner name, the corresponding RR will be 212 bytes. +// It should result in truncation even without TSIG (33 + 268 + 212 = 513) const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456"; // Example output generated by @@ -684,32 +727,25 @@ const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0 TEST_F(MessageTest, toWireTSIGTruncation) { isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; + // Verify a validly signed query so that we can use the TSIG context + // in a response mode. factoryFromFile(message_parse, "message_fromWire17.wire"); EXPECT_EQ(TSIGError::NOERROR(), tsig_ctx.verify(message_parse.getTSIGRecord(), &received_data[0], received_data.size())); - // Note: the following should be merged to commonTSIGToWireCheck: - message_render.setQid(0x22c2); - message_render.setOpcode(Opcode::QUERY()); - message_render.setRcode(Rcode::NOERROR()); - message_render.setHeaderFlag(Message::HEADERFLAG_QR); - message_render.setHeaderFlag(Message::HEADERFLAG_RD); - message_render.setHeaderFlag(Message::HEADERFLAG_AA); - message_render.addQuestion(Question(Name("www.example.com"), - RRClass::IN(), RRType::TXT())); - RRsetPtr rrset(new RRset(Name("www.example.com"), RRClass::IN(), - RRType::TXT(), RRTTL(86400))); - rrset->addRdata(generic::TXT(long_txt1)); - rrset->addRdata(generic::TXT(long_txt2)); - message_render.addRRset(Message::SECTION_ANSWER, rrset); - message_render.toWire(renderer, tsig_ctx); - vector expected_data; - UnitTestUtil::readWireData("message_toWire4.wire", expected_data); - EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(), - renderer.getLength(), - &expected_data[0], expected_data.size()); + message_render.setQid(0x22c2); + vector answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt2); + { + SCOPED_TRACE("Message sign with TSIG and EDNS"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire4.wire", + QR_FLAG|AA_FLAG|RD_FLAG|TC_FLAG, + RRType::TXT(), &answer_data); + } } TEST_F(MessageTest, toWireWithoutOpcode) { From 5024b68a04ecc7ff1c73299fa986cac740cb3e8b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 8 Jul 2011 18:35:20 -0700 Subject: [PATCH 152/974] [trac910] TSIG + TC support, 2nd part: add the case where TSIG itself triggers truncation. added missing testdata files. --- src/lib/dns/message.cc | 11 ++++++- src/lib/dns/tests/message_unittest.cc | 31 +++++++++++++++++-- .../tests/testdata/message_fromWire17.spec | 22 +++++++++++++ .../dns/tests/testdata/message_toWire4.spec | 27 ++++++++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 src/lib/dns/tests/testdata/message_fromWire17.spec create mode 100644 src/lib/dns/tests/testdata/message_toWire4.spec diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index cdc62ab289..60b08f6ed9 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -239,6 +239,16 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { "Message rendering attempted without Opcode set"); } + // Reserve the space for TSIG (if needed) so that we can handle truncation + // case correctly later when that happens. + const size_t tsig_len = (tsig_ctx != NULL) ? tsig_ctx->getTSIGLength() : 0; + if (tsig_len > 0) { + if (tsig_len > renderer.getLengthLimit()) { + ; // TBD + } + renderer.setLengthLimit(renderer.getLengthLimit() - tsig_len); + } + // reserve room for the header renderer.skip(HEADERLEN); @@ -330,7 +340,6 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { renderer.writeUint16At(arcount, header_pos); // Add TSIG, if necessary, at the end of the message. - // TODO: truncate case consideration if (tsig_ctx != NULL) { tsig_ctx->sign(qid_, renderer.getData(), renderer.getLength())->toWire(renderer); diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index add6ab7701..57dbdf45f2 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -718,12 +718,15 @@ const char* const long_txt1 = "0123456789abcdef0123456789abcdef0123456789abcdef0 // It should result in truncation even without TSIG (33 + 268 + 212 = 513) const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456"; +// With a fully compressed owner name, the corresponding RR will be 127 bytes. +// So, it can fit in the standard 512 bytes with txt1 and without TSIG, but +// adding a TSIG would result in truncation (33 + 268 + 127 + 85 = 513) +const char* const long_txt3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"; + // Example output generated by // "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt // QID: 0x22c2 // Time Signed: 0x00004e179212 -// Query MAC: 8214b04634e32323d651ac60b08e6388 -// Response MAC: 88adc3811d1d6bec7c684438906fc694 TEST_F(MessageTest, toWireTSIGTruncation) { isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; @@ -740,7 +743,29 @@ TEST_F(MessageTest, toWireTSIGTruncation) { answer_data.push_back(long_txt1); answer_data.push_back(long_txt2); { - SCOPED_TRACE("Message sign with TSIG and EDNS"); + SCOPED_TRACE("Message sign with TSIG and TC bit on"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire4.wire", + QR_FLAG|AA_FLAG|RD_FLAG|TC_FLAG, + RRType::TXT(), &answer_data); + } +} + +TEST_F(MessageTest, toWireTSIGTruncation2) { + // Similar to the previous test, but without TSIG it wouldn't cause + // truncation. + isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; + factoryFromFile(message_parse, "message_fromWire17.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + message_render.setQid(0x22c2); + vector answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt3); + { + SCOPED_TRACE("Message sign with TSIG and TC bit on (2)"); commonTSIGToWireCheck(message_render, renderer, tsig_ctx, "message_toWire4.wire", QR_FLAG|AA_FLAG|RD_FLAG|TC_FLAG, diff --git a/src/lib/dns/tests/testdata/message_fromWire17.spec b/src/lib/dns/tests/testdata/message_fromWire17.spec new file mode 100644 index 0000000000..366cf051f1 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire17.spec @@ -0,0 +1,22 @@ +# +# A simple DNS query message with TSIG signed +# + +[custom] +sections: header:question:tsig +[header] +id: 0x22c2 +rd: 1 +arcount: 1 +[question] +name: www.example.com +rrtype: TXT +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4e179212 +mac_size: 16 +mac: 0x8214b04634e32323d651ac60b08e6388 +original_id: 0x22c2 diff --git a/src/lib/dns/tests/testdata/message_toWire4.spec b/src/lib/dns/tests/testdata/message_toWire4.spec new file mode 100644 index 0000000000..aab7e10813 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire4.spec @@ -0,0 +1,27 @@ +# +# Truncated DNS response with TSIG signed +# This is expected to be a response to "fromWire17" +# + +[custom] +sections: header:question:tsig +[header] +id: 0x22c2 +rd: 1 +qr: 1 +aa: 1 +# It's "truncated": +tc: 1 +arcount: 1 +[question] +name: www.example.com +rrtype: TXT +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4e179212 +mac_size: 16 +mac: 0x88adc3811d1d6bec7c684438906fc694 +original_id: 0x22c2 From a29b113e5b418921dffaf9b4cfc562ae887a7960 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 8 Jul 2011 23:19:43 -0700 Subject: [PATCH 153/974] [trac910] handle a boundary condition that doesn't cause truncation. --- src/lib/dns/message.cc | 2 + src/lib/dns/tests/message_unittest.cc | 37 +++++++++++++++---- src/lib/dns/tests/testdata/Makefile.am | 7 ++-- src/lib/dns/tests/testdata/gen-wiredata.py.in | 12 +++--- .../tests/testdata/message_fromWire18.spec | 23 ++++++++++++ .../dns/tests/testdata/message_toWire5.spec | 36 ++++++++++++++++++ 6 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 src/lib/dns/tests/testdata/message_fromWire18.spec create mode 100644 src/lib/dns/tests/testdata/message_toWire5.spec diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index 60b08f6ed9..37fd8f51db 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -341,6 +341,8 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { // Add TSIG, if necessary, at the end of the message. if (tsig_ctx != NULL) { + // Release the reserved space in the renderer. + renderer.setLengthLimit(renderer.getLengthLimit() + tsig_len); tsig_ctx->sign(qid_, renderer.getData(), renderer.getLength())->toWire(renderer); diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 57dbdf45f2..216359be2b 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -625,7 +625,6 @@ testGetTime() { const unsigned int QR_FLAG = 0x1; const unsigned int AA_FLAG = 0x2; const unsigned int RD_FLAG = 0x4; -const unsigned int TC_FLAG = 0x8; void commonTSIGToWireCheck(Message& message, MessageRenderer& renderer, @@ -645,9 +644,6 @@ commonTSIGToWireCheck(Message& message, MessageRenderer& renderer, if ((message_flags & RD_FLAG) != 0) { message.setHeaderFlag(Message::HEADERFLAG_RD); } - if ((message_flags & TC_FLAG) != 0) { - message.setHeaderFlag(Message::HEADERFLAG_TC); - } message.addQuestion(Question(Name("www.example.com"), RRClass::IN(), qtype)); @@ -723,6 +719,10 @@ const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0 // adding a TSIG would result in truncation (33 + 268 + 127 + 85 = 513) const char* const long_txt3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"; +// This is 1 byte shorter than txt3, which will result in a possible longest +// message containing answer RRs and TSIG. +const char* const long_txt4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0"; + // Example output generated by // "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt // QID: 0x22c2 @@ -731,13 +731,12 @@ TEST_F(MessageTest, toWireTSIGTruncation) { isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; // Verify a validly signed query so that we can use the TSIG context - // in a response mode. + factoryFromFile(message_parse, "message_fromWire17.wire"); EXPECT_EQ(TSIGError::NOERROR(), tsig_ctx.verify(message_parse.getTSIGRecord(), &received_data[0], received_data.size())); - message_render.setQid(0x22c2); vector answer_data; answer_data.push_back(long_txt1); @@ -746,7 +745,7 @@ TEST_F(MessageTest, toWireTSIGTruncation) { SCOPED_TRACE("Message sign with TSIG and TC bit on"); commonTSIGToWireCheck(message_render, renderer, tsig_ctx, "message_toWire4.wire", - QR_FLAG|AA_FLAG|RD_FLAG|TC_FLAG, + QR_FLAG|AA_FLAG|RD_FLAG, RRType::TXT(), &answer_data); } } @@ -768,7 +767,29 @@ TEST_F(MessageTest, toWireTSIGTruncation2) { SCOPED_TRACE("Message sign with TSIG and TC bit on (2)"); commonTSIGToWireCheck(message_render, renderer, tsig_ctx, "message_toWire4.wire", - QR_FLAG|AA_FLAG|RD_FLAG|TC_FLAG, + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::TXT(), &answer_data); + } +} + +TEST_F(MessageTest, toWireTSIGNoTruncation) { + // A boundary case that shouldn't cause truncation: the resulting + // response message with a TSIG will be 512 bytes long. + isc::util::detail::gettimeFunction = testGetTime<0x4e17b38d>; + factoryFromFile(message_parse, "message_fromWire18.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + message_render.setQid(0xd6e2); + vector answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt4); + { + SCOPED_TRACE("Message sign with TSIG, no truncation"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire5.wire", + QR_FLAG|AA_FLAG|RD_FLAG, RRType::TXT(), &answer_data); } } diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index f42f0f9f91..257f2f3c9f 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -6,8 +6,9 @@ BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire BUILT_SOURCES += message_fromWire16.wire message_fromWire17.wire +BUILT_SOURCES += message_fromWire18.wire BUILT_SOURCES += message_toWire2.wire message_toWire3.wire -BUILT_SOURCES += message_toWire4.wire +BUILT_SOURCES += message_toWire4.wire message_toWire5.wire BUILT_SOURCES += message_toText1.wire message_toText2.wire BUILT_SOURCES += message_toText3.wire BUILT_SOURCES += name_toWire5.wire name_toWire6.wire @@ -60,9 +61,9 @@ EXTRA_DIST += message_fromWire9 message_fromWire10.spec EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec -EXTRA_DIST += message_fromWire17.spec +EXTRA_DIST += message_fromWire17.spec message_fromWire18.spec EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec -EXTRA_DIST += message_toWire4.wire +EXTRA_DIST += message_toWire4.spec message_toWire5.spec EXTRA_DIST += message_toText1.txt message_toText1.spec EXTRA_DIST += message_toText2.txt message_toText2.spec EXTRA_DIST += message_toText3.txt message_toText3.spec diff --git a/src/lib/dns/tests/testdata/gen-wiredata.py.in b/src/lib/dns/tests/testdata/gen-wiredata.py.in index fd98c6eb4b..818c6e958a 100755 --- a/src/lib/dns/tests/testdata/gen-wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen-wiredata.py.in @@ -307,8 +307,8 @@ class SOA(RR): self.retry, self.expire, self.minimum)) -class TXT: - rdlen = -1 # auto-calculate +class TXT(RR): + rdlen = None # auto-calculate nstring = 1 # number of character-strings stringlen = -1 # default string length, auto-calculate string = 'Test String' # default string @@ -330,11 +330,9 @@ class TXT: stringlen_list.append(self.stringlen) if stringlen_list[-1] < 0: stringlen_list[-1] = int(len(wirestring_list[-1]) / 2) - rdlen = self.rdlen - if rdlen < 0: - rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring - f.write('\n# TXT RDATA (RDLEN=%d)\n' % rdlen) - f.write('%04x\n' % rdlen); + if self.rdlen is None: + self.rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring + self.dump_header(f, self.rdlen) for i in range(0, self.nstring): f.write('# String Len=%d, String=\"%s\"\n' % (stringlen_list[i], string_list[i])) diff --git a/src/lib/dns/tests/testdata/message_fromWire18.spec b/src/lib/dns/tests/testdata/message_fromWire18.spec new file mode 100644 index 0000000000..0b2592a46b --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire18.spec @@ -0,0 +1,23 @@ +# +# Another simple DNS query message with TSIG signed. Only ID and time signed +# (and MAC as a result) are different. +# + +[custom] +sections: header:question:tsig +[header] +id: 0xd6e2 +rd: 1 +arcount: 1 +[question] +name: www.example.com +rrtype: TXT +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4e17b38d +mac_size: 16 +mac: 0x903b5b194a799b03a37718820c2404f2 +original_id: 0xd6e2 diff --git a/src/lib/dns/tests/testdata/message_toWire5.spec b/src/lib/dns/tests/testdata/message_toWire5.spec new file mode 100644 index 0000000000..e97fb43ce0 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire5.spec @@ -0,0 +1,36 @@ +# +# A longest possible (without EDNS) DNS response with TSIG, i.e. totatl +# length should be 512 bytes. +# + +[custom] +sections: header:question:txt/1:txt/2:tsig +[header] +id: 0xd6e2 +rd: 1 +qr: 1 +aa: 1 +ancount: 2 +arcount: 1 +[question] +name: www.example.com +rrtype: TXT +[txt/1] +as_rr: True +# QNAME is fully compressed +rr_name: ptr=12 +string: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde +[txt/2] +as_rr: True +# QNAME is fully compressed +rr_name: ptr=12 +string: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0 +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4e17b38d +mac_size: 16 +mac: 0xbe2ba477373d2496891e2fda240ee4ec +original_id: 0xd6e2 From d81a47d3366b6c6ed14edff69188b60ed3655f28 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 9 Jul 2011 00:12:10 -0700 Subject: [PATCH 154/974] [trac910] supported various uncommon or very rare cases --- src/lib/dns/message.cc | 19 ++++++++--- src/lib/dns/message.h | 11 ++++++ src/lib/dns/tests/message_unittest.cc | 48 +++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index 37fd8f51db..c6e93d097c 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -244,12 +244,18 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { const size_t tsig_len = (tsig_ctx != NULL) ? tsig_ctx->getTSIGLength() : 0; if (tsig_len > 0) { if (tsig_len > renderer.getLengthLimit()) { - ; // TBD + isc_throw(InvalidParameter, "Failed to render DNS message: " + "too small limit for a TSIG (" << + renderer.getLengthLimit() << ")"); } renderer.setLengthLimit(renderer.getLengthLimit() - tsig_len); } // reserve room for the header + if (renderer.getLengthLimit() < HEADERLEN) { + isc_throw(InvalidParameter, "Failed to render DNS message: " + "too small limit for a Header"); + } renderer.skip(HEADERLEN); uint16_t qdcount = @@ -296,7 +302,7 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { // If we're adding a TSIG to a truncated message, clear all RRsets // from the message except for the question before adding the TSIG. - // If even the question doesn't fit, don't include any question (TBD). + // TODO: If even the question doesn't fit, don't include any question. if (tsig_ctx != NULL && renderer.isTruncated()) { renderer.clear(); renderer.skip(HEADERLEN); @@ -343,8 +349,13 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { if (tsig_ctx != NULL) { // Release the reserved space in the renderer. renderer.setLengthLimit(renderer.getLengthLimit() + tsig_len); - tsig_ctx->sign(qid_, renderer.getData(), - renderer.getLength())->toWire(renderer); + + const int tsig_count = + tsig_ctx->sign(qid_, renderer.getData(), + renderer.getLength())->toWire(renderer); + if (tsig_count != 1) { + isc_throw(Unexpected, "Failed to render a TSIG RR"); + } // update the ARCOUNT for the TSIG RR. Note that for a sane DNS // message arcount should never overflow to 0. diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h index fcc53e92a0..6a8bf9f1c9 100644 --- a/src/lib/dns/message.h +++ b/src/lib/dns/message.h @@ -565,6 +565,17 @@ public: /// \c tsig_ctx will be updated based on the fact it was used for signing /// and with the latest MAC. /// + /// \exception InvalidMessageOperation The message is not in the Render + /// mode, or either Rcode or Opcode is not set. + /// \exception InvalidParameter The allowable limit of \c renderer is too + /// small for a TSIG or the Header section. Note that this shouldn't + /// happen with parameters as defined in the standard protocols, + /// so it's more likely a program bug. + /// \exception Unexpected Rendering the TSIG RR fails. The implementation + /// internally makes sure this doesn't happen, so if that ever occurs + /// it should mean a bug either in the TSIG context or in the renderer + /// implementation. + /// /// \param renderer See the other version /// \param tsig_ctx A TSIG context that is to be used for signing the /// message diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 216359be2b..2a3d988840 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -794,6 +794,54 @@ TEST_F(MessageTest, toWireTSIGNoTruncation) { } } +// This is a buggy renderer for testing. It behaves like the straightforward +// MessageRenderer, but once its internal buffer reaches the length for +// the header and a question for www.example.com (33 bytes), its +// getLengthLimit() returns a faked value, which would make TSIG RR rendering +// fail unexpectedly in the test that follows. +class BadRenderer : public MessageRenderer { +public: + BadRenderer(isc::util::OutputBuffer& buffer) : + MessageRenderer(buffer) + {} + virtual size_t getLengthLimit() const { + if (MessageRenderer::getLength() >= 33) { + return (0); + } + return (MessageRenderer::getLengthLimit()); + } +}; + +TEST_F(MessageTest, toWireTSIGLengthErrors) { + // specify an unusual short limit that wouldn't be able to hold + // the TSIG. + renderer.setLengthLimit(tsig_ctx.getTSIGLength() - 1); + // Use commonTSIGToWireCheck() only to call toWire() with otherwise valid + // conditions. The checks inside it don't matter because we expect an + // exception before any of the checks. + EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"), + InvalidParameter); + + // This one is large enough for TSIG, but the remaining limit isn't + // even enough for the Header section. + renderer.clear(); + message_render.clear(Message::RENDER); + renderer.setLengthLimit(tsig_ctx.getTSIGLength() + 1); + EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"), + InvalidParameter); + + // Trying to render a message with TSIG using a buggy renderer. + obuffer.clear(); + BadRenderer bad_renderer(obuffer); + bad_renderer.setLengthLimit(512); + message_render.clear(Message::RENDER); + EXPECT_THROW(commonTSIGToWireCheck(message_render, bad_renderer, tsig_ctx, + "message_toWire2.wire"), + Unexpected); +} + TEST_F(MessageTest, toWireWithoutOpcode) { message_render.setRcode(Rcode::NOERROR()); EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation); From 829edd5488aa90324ddc4036dbaf4f2578be9e76 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 9 Jul 2011 00:24:27 -0700 Subject: [PATCH 155/974] [trac910] refactor the python test regarding TSIG: now we have fix_current_time(), we don't have to strip mutable TSIG fields. also generalized __common_tsigquery_setup() for later extensions. --- .../dns/python/tests/message_python_test.py | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index 41b9a67903..3727ae7438 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -21,6 +21,7 @@ import unittest import os from pydnspp import * from testutil import * +from pyunittests_util import fix_current_time # helper functions for tests taken from c++ unittests if "TESTDATA_PATH" in os.environ: @@ -62,16 +63,12 @@ def create_message(): message_render.add_rrset(Message.SECTION_ANSWER, rrset) return message_render -def strip_mutable_tsig_data(data): - # Unfortunately we cannot easily compare TSIG RR because we can't tweak - # current time. As a work around this helper function strips off the time - # dependent part of TSIG RDATA, i.e., the MAC (assuming HMAC-MD5) and - # Time Signed. - return data[0:-32] + data[-26:-22] + data[-6:] - class MessageTest(unittest.TestCase): def setUp(self): + # make sure we don't use faked time unless explicitly do so in tests + fix_current_time(None) + self.p = Message(Message.PARSE) self.r = Message(Message.RENDER) @@ -90,6 +87,10 @@ class MessageTest(unittest.TestCase): self.tsig_key = TSIGKey("www.example.com:SFuWd/q99SzF8Yzd1QbB9g==") self.tsig_ctx = TSIGContext(self.tsig_key) + def tearDown(self): + # reset any faked current time setting (it would affect other tests) + fix_current_time(None) + def test_init(self): self.assertRaises(TypeError, Message, -1) self.assertRaises(TypeError, Message, 3) @@ -285,26 +286,28 @@ class MessageTest(unittest.TestCase): self.assertRaises(InvalidMessageOperation, self.r.to_wire, MessageRenderer()) - def __common_tsigquery_setup(self): + def __common_tsigquery_setup(self, flags=[Message.HEADERFLAG_RD], + rrtype=RRType("A")): self.r.set_opcode(Opcode.QUERY()) self.r.set_rcode(Rcode.NOERROR()) - self.r.set_header_flag(Message.HEADERFLAG_RD) + for flag in flags: + self.r.set_header_flag(flag) self.r.add_question(Question(Name("www.example.com"), - RRClass("IN"), RRType("A"))) + RRClass("IN"), rrtype)) def __common_tsig_checks(self, expected_file): renderer = MessageRenderer() self.r.to_wire(renderer, self.tsig_ctx) - actual_wire = strip_mutable_tsig_data(renderer.get_data()) - expected_wire = strip_mutable_tsig_data(read_wire_data(expected_file)) - self.assertEqual(expected_wire, actual_wire) + self.assertEqual(read_wire_data(expected_file), renderer.get_data()) def test_to_wire_with_tsig(self): + fix_current_time(0x4da8877a) self.r.set_qid(0x2d65) self.__common_tsigquery_setup() self.__common_tsig_checks("message_toWire2.wire") def test_to_wire_with_edns_tsig(self): + fix_current_time(0x4db60d1f) self.r.set_qid(0x6cd) self.__common_tsigquery_setup() edns = EDNS() From d4dce83017319569f35e617dae47af9041166239 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 11 Jul 2011 09:51:27 +0200 Subject: [PATCH 156/974] [trac926] forgot to git add test files --- src/lib/config/tests/testdata/data32_2.data | 3 +++ src/lib/config/tests/testdata/data32_3.data | 3 +++ src/lib/config/tests/testdata/spec32.spec | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 src/lib/config/tests/testdata/data32_2.data create mode 100644 src/lib/config/tests/testdata/data32_3.data create mode 100644 src/lib/config/tests/testdata/spec32.spec diff --git a/src/lib/config/tests/testdata/data32_2.data b/src/lib/config/tests/testdata/data32_2.data new file mode 100644 index 0000000000..0af728eab7 --- /dev/null +++ b/src/lib/config/tests/testdata/data32_2.data @@ -0,0 +1,3 @@ +{ + "named_map_item": { "foo": "wrongtype", "bar": 2 } +} diff --git a/src/lib/config/tests/testdata/data32_3.data b/src/lib/config/tests/testdata/data32_3.data new file mode 100644 index 0000000000..5246afa783 --- /dev/null +++ b/src/lib/config/tests/testdata/data32_3.data @@ -0,0 +1,3 @@ +{ + "named_map_item": [] +} diff --git a/src/lib/config/tests/testdata/spec32.spec b/src/lib/config/tests/testdata/spec32.spec new file mode 100644 index 0000000000..46c29c053b --- /dev/null +++ b/src/lib/config/tests/testdata/spec32.spec @@ -0,0 +1,19 @@ +{ + "module_spec": { + "module_name": "Spec32", + "config_data": [ + { "item_name": "named_map_item", + "item_type": "named_map", + "item_optional": false, + "item_default": { "a": 1, "b": 2 }, + "named_map_item_spec": { + "item_name": "named_map_element", + "item_type": "integer", + "item_optional": false, + "item_default": 3 + } + } + ] + } +} + From 84a16612dd45bcaca490715039b1bec235e0dfef Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 11 Jul 2011 10:22:23 +0200 Subject: [PATCH 157/974] [trac926] add basic handling and some temp config spec --- src/bin/bindctl/bindcmd.py | 5 +-- src/bin/xfrin/xfrin.spec | 32 +++++++++++++++++ src/lib/python/isc/config/ccsession.py | 50 ++++++++++++++++++-------- 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index 8973aa5d64..6c3e1abf6a 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -398,8 +398,9 @@ class BindCmdInterpreter(Cmd): print("Error: " + str(dte)) except isc.cc.data.DataNotFoundError as dnfe: print("Error: " + str(dnfe)) - except KeyError as ke: - print("Error: missing " + str(ke)) + # [XX] TODO: add back + #except KeyError as ke: + # print("Error: missing " + str(ke)) else: self.apply_cmd(cmd) diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec index a3e62cefc4..23fbfd2e0e 100644 --- a/src/bin/xfrin/xfrin.spec +++ b/src/bin/xfrin/xfrin.spec @@ -46,6 +46,38 @@ } ] } + }, + { "item_name": "new_zones", + "item_type": "named_map", + "item_optional": false, + "item_default": {}, + "named_map_item_spec": { + "item_name": "zone", + "item_type": "map", + "item_default": {}, + "map_item_spec": [ + { "item_name": "class", + "item_type": "string", + "item_optional": false, + "item_default": "IN" + }, + { + "item_name": "master_addr", + "item_type": "string", + "item_optional": false, + "item_default": "" + }, + { "item_name": "master_port", + "item_type": "integer", + "item_optional": false, + "item_default": 53 + }, + { "item_name": "tsig_key", + "item_type": "string", + "item_optional": true + } + ] + } } ], "commands": [ diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index bff4f58c84..37c34edc7e 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -426,6 +426,31 @@ class UIModuleCCSession(MultiConfigData): raise ModuleCCSessionError("Bad config version") self._set_current_config(config) + def _add_value_to_list(self, identifier, value): + cur_list, status = self.get_value(identifier) + if not cur_list: + cur_list = [] + + if value is None: + if "item_default" in module_spec["list_item_spec"]: + value = module_spec["list_item_spec"]["item_default"] + + if value is None: + raise isc.cc.data.DataNotFoundError("No value given and no default for " + str(identifier)) + + if value not in cur_list: + cur_list.append(value) + self.set_value(identifier, cur_list) + + def _add_value_to_named_map(self, identifier, value): + if value is None: + raise isc.cc.data.DataNotFoundError("Need a name to add a new item to named_map " + str(identifier)) + + cur_map, status = self.get_value(identifier) + if not cur_map: + cur_map = {} + cur_map[value] = {} + self.set_value(identifier, cur_map) def add_value(self, identifier, value_str = None): """Add a value to a configuration list. Raises a DataTypeError @@ -434,27 +459,22 @@ class UIModuleCCSession(MultiConfigData): not given, we add the default as specified by the .spec file.""" module_spec = self.find_spec_part(identifier) - if (type(module_spec) != dict or "list_item_spec" not in module_spec): - raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list") - - cur_list, status = self.get_value(identifier) - if not cur_list: - cur_list = [] + if module_spec is None: + raise isc.cc.data.DataNotFoundError("Unknown item " + str(identifier)) # Hmm. Do we need to check for duplicates? value = None if value_str is not None: value = isc.cc.data.parse_value_str(value_str) - else: - if "item_default" in module_spec["list_item_spec"]: - value = module_spec["list_item_spec"]["item_default"] - if value is None: - raise isc.cc.data.DataNotFoundError("No value given and no default for " + str(identifier)) - - if value not in cur_list: - cur_list.append(value) - self.set_value(identifier, cur_list) + # the specified element must be a list or a named_map + if 'list_item_spec' in module_spec: + self._add_value_to_list(identifier, value) + elif 'named_map_item_spec' in module_spec: + self._add_value_to_named_map(identifier, value) + else: + raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named map") + def remove_value(self, identifier, value_str): """Remove a value from a configuration list. The value string From 4f17845a927e33ad9655c3f711177e376bc10e44 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 11 Jul 2011 10:57:47 +0200 Subject: [PATCH 158/974] [trac926] add/remove for named_map --- src/lib/python/isc/config/ccsession.py | 74 +++++++++++++++++--------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index 37c34edc7e..8c80c9b3ca 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -445,12 +445,14 @@ class UIModuleCCSession(MultiConfigData): def _add_value_to_named_map(self, identifier, value): if value is None: raise isc.cc.data.DataNotFoundError("Need a name to add a new item to named_map " + str(identifier)) - - cur_map, status = self.get_value(identifier) - if not cur_map: - cur_map = {} - cur_map[value] = {} - self.set_value(identifier, cur_map) + elif type(value) != str: + raise isc.cc.data.DataTypeError("Name for named_map " + identifier + " must be a string") + else: + cur_map, status = self.get_value(identifier) + if not cur_map: + cur_map = {} + cur_map[value] = {} + self.set_value(identifier, cur_map) def add_value(self, identifier, value_str = None): """Add a value to a configuration list. Raises a DataTypeError @@ -475,6 +477,35 @@ class UIModuleCCSession(MultiConfigData): else: raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named map") + def _remove_value_from_list(self, identifier, value): + if value is None: + # we are directly removing an list index + id, list_indices = isc.cc.data.split_identifier_list_indices(identifier) + if list_indices is None: + raise DataTypeError("identifier in remove_value() does not contain a list index, and no value to remove") + else: + self.set_value(identifier, None) + else: + cur_list, status = self.get_value(identifier) + if not cur_list: + cur_list = [] + elif value in cur_list: + cur_list.remove(value) + self.set_value(identifier, cur_list) + + def _remove_value_from_named_map(self, identifier, value): + if value is None: + raise isc.cc.data.DataNotFoundError("Need a name to remove an item from named_map " + str(identifier)) + elif type(value) != str: + raise isc.cc.data.DataTypeError("Name for named_map " + identifier + " must be a string") + else: + cur_map, status = self.get_value(identifier) + if not cur_map: + cur_map = {} + if value in cur_map: + del cur_map[value] + else: + raise isc.cc.data.DataNotFoundError(value + " not found in named_map " + str(identifier)) def remove_value(self, identifier, value_str): """Remove a value from a configuration list. The value string @@ -483,27 +514,22 @@ class UIModuleCCSession(MultiConfigData): or if the given value_str does not match the list_item_spec """ module_spec = self.find_spec_part(identifier) - if (type(module_spec) != dict or "list_item_spec" not in module_spec): - raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list") + if module_spec is None: + raise isc.cc.data.DataNotFoundError("Unknown item " + str(identifier)) - if value_str is None: - # we are directly removing an list index - id, list_indices = isc.cc.data.split_identifier_list_indices(identifier) - if list_indices is None: - raise DataTypeError("identifier in remove_value() does not contain a list index, and no value to remove") - else: - self.set_value(identifier, None) - else: + value = None + if value_str is not None: value = isc.cc.data.parse_value_str(value_str) isc.config.config_data.check_type(module_spec, [value]) - cur_list, status = self.get_value(identifier) - #if not cur_list: - # cur_list = isc.cc.data.find_no_exc(self.config.data, identifier) - if not cur_list: - cur_list = [] - if value in cur_list: - cur_list.remove(value) - self.set_value(identifier, cur_list) + + if 'list_item_spec' in module_spec: + self._remove_value_from_list(identifier, value) + elif 'named_map_item_spec' in module_spec: + self._remove_value_from_named_map(identifier, value) + else: + raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named_map") + + def commit(self): """Commit all local changes, send them through b10-cmdctl to From 2cd7eb5d2c64c6a54350e6399f07fd4826933bff Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 11 Jul 2011 11:05:19 +0200 Subject: [PATCH 159/974] [trac926] error on duplicate add --- src/bin/bindctl/bindcmd.py | 2 ++ src/lib/python/isc/cc/data.py | 18 ++++++++++++++++-- src/lib/python/isc/config/ccsession.py | 14 +++++++++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index 6c3e1abf6a..4843d021c1 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -398,6 +398,8 @@ class BindCmdInterpreter(Cmd): print("Error: " + str(dte)) except isc.cc.data.DataNotFoundError as dnfe: print("Error: " + str(dnfe)) + except isc.cc.data.DataAlreadyPresentError as dnfe: + print("Error: " + str(dnfe)) # [XX] TODO: add back #except KeyError as ke: # print("Error: missing " + str(ke)) diff --git a/src/lib/python/isc/cc/data.py b/src/lib/python/isc/cc/data.py index ce1bba0aeb..76ef94226e 100644 --- a/src/lib/python/isc/cc/data.py +++ b/src/lib/python/isc/cc/data.py @@ -22,8 +22,22 @@ import json -class DataNotFoundError(Exception): pass -class DataTypeError(Exception): pass +class DataNotFoundError(Exception): + """Raised if an identifier does not exist according to a spec file, + or if an item is addressed that is not in the current (or default) + config (such as a nonexistent list or map element)""" + pass + +class DataAlreadyPresentError(Exception): + """Raised if there is an attemt to add an element to a list or a + map that is already present in that list or map (i.e. if 'add' + is used when it should be 'set')""" + pass + +class DataTypeError(Exception): + """Raised if there is an attempt to set an element that is of a + different type than the type specified in the specification.""" + pass def remove_identical(a, b): """Removes the values from dict a that are the same as in dict b. diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index 8c80c9b3ca..7105ae440a 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -441,6 +441,8 @@ class UIModuleCCSession(MultiConfigData): if value not in cur_list: cur_list.append(value) self.set_value(identifier, cur_list) + else: + raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier) def _add_value_to_named_map(self, identifier, value): if value is None: @@ -451,15 +453,21 @@ class UIModuleCCSession(MultiConfigData): cur_map, status = self.get_value(identifier) if not cur_map: cur_map = {} - cur_map[value] = {} - self.set_value(identifier, cur_map) + if value not in cur_map: + cur_map[value] = {} + self.set_value(identifier, cur_map) + else: + raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier) def add_value(self, identifier, value_str = None): """Add a value to a configuration list. Raises a DataTypeError if the value does not conform to the list_item_spec field of the module config data specification. If value_str is not given, we add the default as specified by the .spec - file.""" + file. Raises a DataNotFoundError if the given identifier + is not specified in the specification as a map or list. + Raises a DataAlreadyPresentError if the specified element + already exists.""" module_spec = self.find_spec_part(identifier) if module_spec is None: raise isc.cc.data.DataNotFoundError("Unknown item " + str(identifier)) From 65bc0fbf12199bee2d16b914a544a69345c37cae Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 11 Jul 2011 02:17:44 -0700 Subject: [PATCH 160/974] [trac764] editorial suggestions: s/packet/message/ --- src/lib/python/isc/notify/notify_out_messages.mes | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/python/isc/notify/notify_out_messages.mes b/src/lib/python/isc/notify/notify_out_messages.mes index 797643e67d..f77446a98c 100644 --- a/src/lib/python/isc/notify/notify_out_messages.mes +++ b/src/lib/python/isc/notify/notify_out_messages.mes @@ -23,7 +23,7 @@ is not correct. The specific place where this library needs to be depends on your system and your specific installation. % NOTIFY_OUT_INVALID_ADDRESS invalid address %1#%2: %3 -The notify_out library tried to send a notify packet to the given +The notify_out library tried to send a notify message to the given address, but it appears to be an invalid address. The configuration for secondary nameservers might contain a typographic error, or a different BIND 10 module has forgotten to validate its data before @@ -50,7 +50,7 @@ the one we sent. Since there was a response, no more notifies will be sent to this server for this notification event. % NOTIFY_OUT_REPLY_QR_NOT_SET QR flags set to 0 in reply to notify from %1#%2 -The notify_out library sent a notify packet to the namesever at the +The notify_out library sent a notify message to the namesever at the given address, but the reply did not have the QR bit set to one. % NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries (%3) exceeded @@ -63,13 +63,13 @@ A notify message is sent to the secondary nameserver at the given address. % NOTIFY_OUT_SOCKET_ERROR socket error sending notify to %1#%2: %3 -There was a network error while trying to send a notify packet to +There was a network error while trying to send a notify message to the given address. The address might be unreachable. The socket error is printed and should provide more information. % NOTIFY_OUT_SOCKET_RECV_ERROR socket error reading notify reply from %1#%2: %3 There was a network error while trying to read a notify reply -packet from the given address. The socket error is printed and should +message from the given address. The socket error is printed and should provide more information. % NOTIFY_OUT_TIMEOUT retry notify to %1#%2 @@ -79,8 +79,8 @@ is reached. % NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION uncaught exception: %1 There was an uncaught exception in the handling of a notify reply -packet, either in the packet parser, or while trying to extract data -from the parsed packet. The error is printed, and notify_out will -treat the response as a bad packet, but this does point to a +message, either in the message parser, or while trying to extract data +from the parsed message. The error is printed, and notify_out will +treat the response as a bad message, but this does point to a programming error, since all exceptions should have been caught explicitely. Please file a bug report. From b327d9aac9bfd87e175d03421069ae679087dd00 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 11 Jul 2011 12:33:55 +0200 Subject: [PATCH 161/974] [trac983] Adjust exception in test --- src/lib/python/isc/acl/tests/dns_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index c3a68ba151..3924222323 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -93,7 +93,7 @@ class RequestContextTest(unittest.TestCase): class RequestACLTest(unittest.TestCase): def test_direct_construct(self): - self.assertRaises(TypeError, RequestACL) + self.assertRaises(Error, RequestACL) def test_request_loader(self): # these shouldn't raise an exception From 7cc84b1bfe00402ea12749c63c7e4d8cef5b2431 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 11 Jul 2011 16:13:40 +0200 Subject: [PATCH 162/974] [trac926] add named map support for named map also a small fix in lists --- src/bin/bindctl/bindcmd.py | 11 ++-- src/lib/python/isc/config/ccsession.py | 22 ++++--- src/lib/python/isc/config/config_data.py | 63 ++++++++++++++++++- .../python/isc/config/tests/ccsession_test.py | 34 +++++++++- .../isc/config/tests/config_data_test.py | 36 +++++++++++ 5 files changed, 149 insertions(+), 17 deletions(-) diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index 4843d021c1..6e38e6f269 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -400,9 +400,8 @@ class BindCmdInterpreter(Cmd): print("Error: " + str(dnfe)) except isc.cc.data.DataAlreadyPresentError as dnfe: print("Error: " + str(dnfe)) - # [XX] TODO: add back - #except KeyError as ke: - # print("Error: missing " + str(ke)) + except KeyError as ke: + print("Error: missing " + str(ke)) else: self.apply_cmd(cmd) @@ -629,7 +628,7 @@ class BindCmdInterpreter(Cmd): values = self.config_data.get_value_maps(identifier, show_all) for value_map in values: line = value_map['name'] - if value_map['type'] in [ 'module', 'map' ]: + if value_map['type'] in [ 'module', 'map', 'named_map' ]: line += "/" elif value_map['type'] == 'list' \ and value_map['value'] != []: @@ -637,7 +636,9 @@ class BindCmdInterpreter(Cmd): # we have more data to show line += "/" else: - line += "\t" + json.dumps(value_map['value']) + # if type is named_map, don't print value + if value_map['type'] != 'named_map': + line += "\t" + json.dumps(value_map['value']) line += "\t" + value_map['type'] line += "\t" if value_map['default']: diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index 7105ae440a..d05ce40f19 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -444,7 +444,7 @@ class UIModuleCCSession(MultiConfigData): else: raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier) - def _add_value_to_named_map(self, identifier, value): + def _add_value_to_named_map(self, identifier, value, item_value): if value is None: raise isc.cc.data.DataNotFoundError("Need a name to add a new item to named_map " + str(identifier)) elif type(value) != str: @@ -454,7 +454,7 @@ class UIModuleCCSession(MultiConfigData): if not cur_map: cur_map = {} if value not in cur_map: - cur_map[value] = {} + cur_map[value] = item_value self.set_value(identifier, cur_map) else: raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier) @@ -481,7 +481,10 @@ class UIModuleCCSession(MultiConfigData): if 'list_item_spec' in module_spec: self._add_value_to_list(identifier, value) elif 'named_map_item_spec' in module_spec: - self._add_value_to_named_map(identifier, value) + item_value = None + if 'item_default' in module_spec['named_map_item_spec']: + item_value = module_spec['named_map_item_spec']['item_default'] + self._add_value_to_named_map(identifier, value, item_value) else: raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named map") @@ -516,11 +519,11 @@ class UIModuleCCSession(MultiConfigData): raise isc.cc.data.DataNotFoundError(value + " not found in named_map " + str(identifier)) def remove_value(self, identifier, value_str): - """Remove a value from a configuration list. The value string - must be a string representation of the full item. Raises - a DataTypeError if the value at the identifier is not a list, - or if the given value_str does not match the list_item_spec - """ + """Remove a value from a configuration list or named map. + The value string must be a string representation of the full + item. Raises a DataTypeError if the value at the identifier + is not a list, or if the given value_str does not match the + list_item_spec """ module_spec = self.find_spec_part(identifier) if module_spec is None: raise isc.cc.data.DataNotFoundError("Unknown item " + str(identifier)) @@ -528,9 +531,10 @@ class UIModuleCCSession(MultiConfigData): value = None if value_str is not None: value = isc.cc.data.parse_value_str(value_str) - isc.config.config_data.check_type(module_spec, [value]) if 'list_item_spec' in module_spec: + if value is not None: + isc.config.config_data.check_type(module_spec['list_item_spec'], value) self._remove_value_from_list(identifier, value) elif 'named_map_item_spec' in module_spec: self._remove_value_from_named_map(identifier, value) diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py index 1efe4a9849..94bcc12194 100644 --- a/src/lib/python/isc/config/config_data.py +++ b/src/lib/python/isc/config/config_data.py @@ -145,6 +145,8 @@ def _find_spec_part_single(cur_spec, id_part): return cur_spec['list_item_spec'] # not found raise isc.cc.data.DataNotFoundError(id + " not found") + elif type(cur_spec) == dict and 'named_map_item_spec' in cur_spec.keys(): + return cur_spec['named_map_item_spec'] elif type(cur_spec) == list: for cur_spec_item in cur_spec: if cur_spec_item['item_name'] == id: @@ -408,11 +410,38 @@ class MultiConfigData: id_parts = isc.cc.data.split_identifier(id) id_prefix = "" while len(id_parts) > 0: + # [XX] TODO: Refactor id_part = id_parts.pop(0) item_id, list_indices = isc.cc.data.split_identifier_list_indices(id_part) id_list = module + "/" + id_prefix + "/" + item_id id_prefix += "/" + id_part - if list_indices is not None: + part_spec = find_spec_part(self._specifications[module].get_config_spec(), id_prefix) + if part_spec['item_type'] == 'named_map': + if len(id_parts) == 0: + if 'item_default' in part_spec: + return part_spec['item_default'] + else: + return None + id_part = id_parts.pop(0) + + named_map_value, type = self.get_value(id_list) + if id_part in named_map_value: + if len(id_parts) > 0: + # we are looking for the *default* value. + # so if not present in here, we need to + # lookup the one from the spec + rest_of_id = "/".join(id_parts) + result = isc.cc.data.find_no_exc(named_map_value[id_part], rest_of_id) + if result is None: + spec_part = self.find_spec_part(identifier) + if 'item_default' in spec_part: + return spec_part['item_default'] + return result + else: + return named_map_value[id_part] + else: + return None + elif list_indices is not None: # there's actually two kinds of default here for # lists; they can have a default value (like an # empty list), but their elements can also have @@ -449,7 +478,12 @@ class MultiConfigData: spec = find_spec_part(self._specifications[module].get_config_spec(), id) if 'item_default' in spec: - return spec['item_default'] + # one special case, named_map + if spec['item_type'] == 'named_map': + print("is " + id_part + " in named map?") + return spec['item_default'] + else: + return spec['item_default'] else: return None @@ -509,12 +543,37 @@ class MultiConfigData: for i in range(len(list_value)): self._append_value_item(result, spec_part_list, "%s[%d]" % (identifier, i), all) elif item_type == "map": + value, status = self.get_value(identifier) # just show the specific contents of a map, we are # almost never interested in just its name spec_part_map = spec_part['map_item_spec'] self._append_value_item(result, spec_part_map, identifier, all) + elif item_type == "named_map": + value, status = self.get_value(identifier) + if status == self.NONE or (status == self.DEFAULT and value == {}): + raise isc.cc.data.DataNotFoundError(identifier) + # show just the one entry, when either the map is empty, + # or when this is element is not requested specifically + if (len(value.keys()) == 0 and (all or first)): + entry = _create_value_map_entry(identifier, + item_type, + {}, status) + result.append(entry) + elif not first and not all: + entry = _create_value_map_entry(identifier, + item_type, + None, status) + result.append(entry) + else: + spec_part_named_map = spec_part['named_map_item_spec'] + for entry in value: + # xxxxxxxxxxx + self._append_value_item(result, spec_part_named_map, identifier + "/" + entry, all) else: value, status = self.get_value(identifier) + if status == self.NONE and not spec_part['item_optional']: + raise isc.cc.data.DataNotFoundError(identifier) + entry = _create_value_map_entry(identifier, item_type, value, status) diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py index 830cbd762d..9bed216434 100644 --- a/src/lib/python/isc/config/tests/ccsession_test.py +++ b/src/lib/python/isc/config/tests/ccsession_test.py @@ -691,6 +691,12 @@ class TestUIModuleCCSession(unittest.TestCase): fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION }) return UIModuleCCSession(fake_conn) + def create_uccs_named_map(self, fake_conn): + module_spec = isc.config.module_spec_from_file(self.spec_file("spec32.spec")) + fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()}) + fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION }) + return UIModuleCCSession(fake_conn) + def test_init(self): fake_conn = fakeUIConn() fake_conn.set_get_answer('/module_spec', {}) @@ -711,12 +717,14 @@ class TestUIModuleCCSession(unittest.TestCase): def test_add_remove_value(self): fake_conn = fakeUIConn() uccs = self.create_uccs2(fake_conn) + self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, 1, "a") self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "no_such_item", "a") self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "Spec2/item1", "a") self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, 1, "a") self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "no_such_item", "a") self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "Spec2/item1", "a") + self.assertEqual({}, uccs._local_changes) uccs.add_value("Spec2/item5", "foo") self.assertEqual({'Spec2': {'item5': ['a', 'b', 'foo']}}, uccs._local_changes) @@ -726,11 +734,35 @@ class TestUIModuleCCSession(unittest.TestCase): uccs.remove_value("Spec2/item5", "foo") uccs.add_value("Spec2/item5", "foo") self.assertEqual({'Spec2': {'item5': ['foo']}}, uccs._local_changes) - uccs.add_value("Spec2/item5", "foo") + self.assertRaises(isc.cc.data.DataAlreadyPresentError, + uccs.add_value, "Spec2/item5", "foo") self.assertEqual({'Spec2': {'item5': ['foo']}}, uccs._local_changes) + self.assertRaises(isc.cc.data.DataNotFoundError, + uccs.remove_value, "Spec2/item5[123]", None) uccs.remove_value("Spec2/item5[0]", None) self.assertEqual({'Spec2': {'item5': []}}, uccs._local_changes) + def test_add_remove_value_named_map(self): + fake_conn = fakeUIConn() + uccs = self.create_uccs_named_map(fake_conn) + value, status = uccs.get_value("/Spec32/named_map_item") + self.assertEqual({'a': 1, 'b': 2}, value) + uccs.add_value("/Spec32/named_map_item", "foo") + value, status = uccs.get_value("/Spec32/named_map_item") + self.assertEqual({'a': 1, 'b': 2, 'foo': 3}, value) + uccs.set_value("/Spec32/named_map_item/bar", 4) + value, status = uccs.get_value("/Spec32/named_map_item") + self.assertEqual({'a': 1, 'b': 2, 'foo': 3, 'bar': 4}, value) + + uccs.remove_value("/Spec32/named_map_item", "a") + uccs.remove_value("/Spec32/named_map_item", "foo") + value, status = uccs.get_value("/Spec32/named_map_item") + self.assertEqual({'b': 2, 'bar': 4}, value) + + self.assertRaises(isc.cc.data.DataNotFoundError, + uccs.remove_value, "/Spec32/named_map_item", + "no_such_item") + def test_commit(self): fake_conn = fakeUIConn() uccs = self.create_uccs2(fake_conn) diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py index fc1bffaef1..e9136bf008 100644 --- a/src/lib/python/isc/config/tests/config_data_test.py +++ b/src/lib/python/isc/config/tests/config_data_test.py @@ -236,6 +236,7 @@ class TestConfigData(unittest.TestCase): value, default = self.cd.get_value("item6/value2") self.assertEqual(None, value) self.assertEqual(False, default) + self.assertRaises(isc.cc.data.DataNotFoundError, self.cd.get_value, "item6/no_such_item") def test_get_default_value(self): self.assertEqual(1, self.cd.get_default_value("item1")) @@ -410,6 +411,7 @@ class TestMultiConfigData(unittest.TestCase): self.assertEqual('a', value) value = self.mcd.get_default_value("Spec2/item5[1]") self.assertEqual('b', value) + self.assertRaises(self.mcd.get_default_value("Spec2/item5[2]")) value = self.mcd.get_default_value("Spec2/item5[5]") self.assertEqual(None, value) value = self.mcd.get_default_value("Spec2/item5[0][1]") @@ -421,6 +423,17 @@ class TestMultiConfigData(unittest.TestCase): value = self.mcd.get_default_value("Spec2/no_such_item/asdf") self.assertEqual(None, value) + module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec") + self.mcd.set_specification(module_spec) + value = self.mcd.get_default_value("Spec32/named_map_item") + self.assertEqual({ 'a': 1, 'b': 2}, value) + value = self.mcd.get_default_value("Spec32/named_map_item/a") + self.assertEqual(1, value) + value = self.mcd.get_default_value("Spec32/named_map_item/b") + self.assertEqual(2, value) + value = self.mcd.get_default_value("Spec32/named_map_item/no_such_item") + self.assertEqual(None, value) + def test_get_value(self): module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec") self.mcd.set_specification(module_spec) @@ -544,6 +557,29 @@ class TestMultiConfigData(unittest.TestCase): maps = self.mcd.get_value_maps("/Spec22/value9") self.assertEqual(expected, maps) + def test_get_value_maps_named_map(self): + module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec") + self.mcd.set_specification(module_spec) + maps = self.mcd.get_value_maps() + self.assertEqual([{'default': False, 'type': 'module', + 'name': 'Spec32', 'value': None, + 'modified': False}], maps) + maps = self.mcd.get_value_maps("/Spec32/named_map_item") + self.assertEqual([{'default': True, 'type': 'integer', + 'name': 'Spec32/named_map_item/a', + 'value': 1, 'modified': False}, + {'default': True, 'type': 'integer', + 'name': 'Spec32/named_map_item/b', + 'value': 2, 'modified': False}], maps) + maps = self.mcd.get_value_maps("/Spec32/named_map_item/a") + self.assertEqual([{'default': True, 'type': 'integer', + 'name': 'Spec32/named_map_item/a', + 'value': 1, 'modified': False}], maps) + maps = self.mcd.get_value_maps("/Spec32/named_map_item/b") + self.assertEqual([{'default': True, 'type': 'integer', + 'name': 'Spec32/named_map_item/b', + 'value': 2, 'modified': False}], maps) + def test_set_value(self): module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec") self.mcd.set_specification(module_spec) From 7e0ef7c21ad41f0e3047059fef61ddbefe143444 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 11 Jul 2011 17:00:38 +0200 Subject: [PATCH 163/974] [trac926] forgot some EXTRA_DIST lines --- src/lib/config/tests/testdata/Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/config/tests/testdata/Makefile.am b/src/lib/config/tests/testdata/Makefile.am index 57d1ed30ec..91d7f04540 100644 --- a/src/lib/config/tests/testdata/Makefile.am +++ b/src/lib/config/tests/testdata/Makefile.am @@ -22,6 +22,9 @@ EXTRA_DIST += data22_7.data EXTRA_DIST += data22_8.data EXTRA_DIST += data22_9.data EXTRA_DIST += data22_10.data +EXTRA_DIST += data32_1.data +EXTRA_DIST += data32_2.data +EXTRA_DIST += data32_3.data EXTRA_DIST += spec1.spec EXTRA_DIST += spec2.spec EXTRA_DIST += spec3.spec @@ -53,3 +56,4 @@ EXTRA_DIST += spec28.spec EXTRA_DIST += spec29.spec EXTRA_DIST += spec30.spec EXTRA_DIST += spec31.spec +EXTRA_DIST += spec32.spec From 0dd272381befd9b464365cc7df0bb2d761d0d2e0 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 11 Jul 2011 18:47:38 +0200 Subject: [PATCH 164/974] [trac764] second round of review --- src/lib/log/compiler/message.cc | 2 +- src/lib/python/isc/notify/notify_out.py | 13 +++++++------ src/lib/python/isc/notify/notify_out_messages.mes | 13 +++++-------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc index 2383196ebe..f74020a762 100644 --- a/src/lib/log/compiler/message.cc +++ b/src/lib/log/compiler/message.cc @@ -60,7 +60,7 @@ static const char* VERSION = "1.0-0"; /// /// It reads the message file and writes out two files of the same /// name in the current working directory (unless -d is used) but -/// with extensions of .h and .cc. +/// with extensions of .h and .cc, or .py if -p is used. /// /// -v causes it to print the version number and exit. -h prints a help /// message (and exits). -p sets the output to python. -d will make diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py index b495714453..f1e02cab29 100644 --- a/src/lib/python/isc/notify/notify_out.py +++ b/src/lib/python/isc/notify/notify_out.py @@ -27,10 +27,11 @@ from notify_out_messages import * logger = isc.log.Logger("notify_out") -try: - from pydnspp import * -except ImportError as e: - logger.error(NOTIFY_OUT_IMPORT_ERROR, str(e)) +# there used to be a printed message if this import failed, but if +# we can't import we should not start anyway, and logging an error +# is a bad idea since the logging system is most likely not +# initialized yet. see trac ticket #1103 +from pydnspp import * ZONE_NEW_DATA_READY_CMD = 'zone_new_data_ready' _MAX_NOTIFY_NUM = 30 @@ -372,8 +373,8 @@ class NotifyOut: if tgt: zone_notify_info.notify_try_num += 1 if zone_notify_info.notify_try_num > _MAX_NOTIFY_TRY_NUM: - logger.error(NOTIFY_OUT_RETRY_EXCEEDED, tgt[0], tgt[1], - _MAX_NOTIFY_TRY_NUM) + logger.warn(NOTIFY_OUT_RETRY_EXCEEDED, tgt[0], tgt[1], + _MAX_NOTIFY_TRY_NUM) self._notify_next_target(zone_notify_info) else: # set exponential backoff according rfc1996 section 3.6 diff --git a/src/lib/python/isc/notify/notify_out_messages.mes b/src/lib/python/isc/notify/notify_out_messages.mes index f77446a98c..f9de744b00 100644 --- a/src/lib/python/isc/notify/notify_out_messages.mes +++ b/src/lib/python/isc/notify/notify_out_messages.mes @@ -15,13 +15,6 @@ # No namespace declaration - these constants go in the global namespace # of the notify_out_messages python module. -% NOTIFY_OUT_IMPORT_ERROR error importing python module: %1 -There was an error importing a python module. One of the modules -needed by notify_out could not be found. This suggests that either -some libraries are missing on the system, or the PYTHONPATH variable -is not correct. The specific place where this library needs to be -depends on your system and your specific installation. - % NOTIFY_OUT_INVALID_ADDRESS invalid address %1#%2: %3 The notify_out library tried to send a notify message to the given address, but it appears to be an invalid address. The configuration @@ -52,6 +45,8 @@ be sent to this server for this notification event. % NOTIFY_OUT_REPLY_QR_NOT_SET QR flags set to 0 in reply to notify from %1#%2 The notify_out library sent a notify message to the namesever at the given address, but the reply did not have the QR bit set to one. +Since there was a response, no more notifies will be sent to this +server for this notification event. % NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries (%3) exceeded The maximum number of retries for the notify target has been exceeded. @@ -83,4 +78,6 @@ message, either in the message parser, or while trying to extract data from the parsed message. The error is printed, and notify_out will treat the response as a bad message, but this does point to a programming error, since all exceptions should have been caught -explicitely. Please file a bug report. +explicitely. Please file a bug report. Since there was a response, +no more notifies will be sent to this server for this notification +event. From 4d685db094731fccfa684f5c0b26ebfc1c28ca2c Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Mon, 11 Jul 2011 17:51:54 +0100 Subject: [PATCH 165/974] [trac1075] Rewording of some of the data source messages --- src/lib/datasrc/cache.cc | 3 +- src/lib/datasrc/data_source.cc | 4 +- src/lib/datasrc/datasrc_messages.mes | 96 +++++++++++++++------------- src/lib/datasrc/memory_datasrc.cc | 2 +- src/lib/datasrc/static_datasrc.cc | 2 +- 5 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/lib/datasrc/cache.cc b/src/lib/datasrc/cache.cc index 9082a6b4ce..d88e649266 100644 --- a/src/lib/datasrc/cache.cc +++ b/src/lib/datasrc/cache.cc @@ -232,7 +232,8 @@ HotCacheImpl::insert(const CacheNodePtr node) { if (iter != map_.end()) { CacheNodePtr old = iter->second; if (old && old->isValid()) { - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND); + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND) + .arg(node->getNodeName()); remove(old); } } diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc index b57a967b13..94dec89352 100644 --- a/src/lib/datasrc/data_source.cc +++ b/src/lib/datasrc/data_source.cc @@ -903,7 +903,7 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) { result = proveNX(q, task, zoneinfo, true); if (result != DataSrc::SUCCESS) { m.setRcode(Rcode::SERVFAIL()); - logger.error(DATASRC_QUERY_WILDCARD_PROVENX_FAIL). + logger.error(DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL). arg(task->qname).arg(result); return (DataSrc::ERROR); } @@ -1162,7 +1162,7 @@ DataSrc::doQuery(Query& q) { result = proveNX(q, task, zoneinfo, false); if (result != DataSrc::SUCCESS) { m.setRcode(Rcode::SERVFAIL()); - logger.error(DATASRC_QUERY_PROVENX_FAIL).arg(task->qname); + logger.error(DATASRC_QUERY_PROVE_NX_FAIL).arg(task->qname); return; } } diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index c69236452b..62e39f1f78 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -17,26 +17,25 @@ $NAMESPACE isc::datasrc # \brief Messages for the data source library % DATASRC_CACHE_CREATE creating the hotspot cache -Debug information that the hotspot cache was created at startup. +This is a debug message issued during startup when the hostspot cache +is created. % DATASRC_CACHE_DESTROY destroying the hotspot cache Debug information. The hotspot cache is being destroyed. % DATASRC_CACHE_DISABLE disabling the cache -The hotspot cache is disabled from now on. It is not going to store -information or return anything. +A debug message issued when the hotspot cache is disabled. % DATASRC_CACHE_ENABLE enabling the cache -The hotspot cache is enabled from now on. +A debug message issued when the hotspot cache is enabled. % DATASRC_CACHE_EXPIRED the item '%1' is expired -Debug information. There was an attempt to look up an item in the hotspot -cache. And the item was actually there, but it was too old, so it was removed -instead and nothing is reported (the external behaviour is the same as with -CACHE_NOT_FOUND). +A debug message issued when a hotspot cache lookup located the item but it +had expired. The item was removed and the program proceeded as if the item +had not been found. % DATASRC_CACHE_FOUND the item '%1' was found -Debug information. An item was successfully looked up in the hotspot cache. +Debug information. An item was successfully located in the hotspot cache. % DATASRC_CACHE_FULL cache is full, dropping oldest Debug information. After inserting an item into the hotspot cache, the @@ -44,17 +43,17 @@ maximum number of items was exceeded, so the least recently used item will be dropped. This should be directly followed by CACHE_REMOVE. % DATASRC_CACHE_INSERT inserting item '%1' into the cache -Debug information. It means a new item is being inserted into the hotspot +A debug message indicating that a new item is being inserted into the hotspot cache. % DATASRC_CACHE_NOT_FOUND the item '%1' was not found -Debug information. It was attempted to look up an item in the hotspot cache, -but it is not there. +A debug message issued when hotspot cache was searched for the specified +item but it was not found. -% DATASRC_CACHE_OLD_FOUND older instance of cache item found, replacing +% DATASRC_CACHE_OLD_FOUND older instance of cache item '%1' found, replacing Debug information. While inserting an item into the hotspot cache, an older -instance of an item with the same name was found. The old instance will be -removed. This should be directly followed by CACHE_REMOVE. +instance of an item with the same name was found; the old instance will be +removed. This will be directly followed by CACHE_REMOVE. % DATASRC_CACHE_REMOVE removing '%1' from the cache Debug information. An item is being removed from the hotspot cache. @@ -65,15 +64,16 @@ number. If there are too many, some of them will be dropped. The size of 0 means no limit. % DATASRC_DO_QUERY handling query for '%1/%2' -Debug information. We're processing some internal query for given name and -type. +A debug message indicating that a query for the given name and RR type is being +processed. % DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3' Debug information. An RRset is being added to the in-memory data source. % DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1' -Debug information. Some special marks above each * in wildcard name are needed. -They are being added now for this name. +This is a debug message issued during the processing of a wildcard +name. The internal domain name tree is scanned and some nodes are +specially marked to allow the wildcard lookup to succeed. % DATASRC_MEM_ADD_ZONE adding zone '%1/%2' Debug information. A zone is being added into the in-memory data source. @@ -114,9 +114,9 @@ stop the search. Debug information. A DNAME was found instead of the requested information. % DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1' -It was requested for DNAME and NS records to be put into the same domain -which is not the apex (the top of the zone). This is forbidden by RFC -2672, section 3. This indicates a problem with provided data. +A request was made for DNAME and NS records to be put into the same +domain which is not the apex (the top of the zone). This is forbidden +by RFC 2672 (section 3) and indicates a problem with provided data. % DATASRC_MEM_DOMAIN_EMPTY requested domain '%1' is empty Debug information. The requested domain exists in the tree of domains, but @@ -201,8 +201,8 @@ behave and BIND 9 refuses that as well. Please describe your intention using different tools. % DATASRC_META_ADD adding a data source into meta data source -Debug information. Yet another data source is being added into the meta data -source. (probably at startup or reconfiguration) +This is a debug message issued during startup or reconfiguration. A +another data source is being added into the meta data source. % DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2' It was attempted to add a data source into a meta data source. But their @@ -251,10 +251,9 @@ Debug information. The software is trying to identify delegation points on the way down to the given domain. % DATASRC_QUERY_EMPTY_CNAME CNAME at '%1' is empty -There was an CNAME and it was being followed. But it contains no records, -so there's nowhere to go. There will be no answer. This indicates a problem -with supplied data. -We tried to follow +A CNAME chain was being followed and an entry was found that pointed +to a domain name that had no RRsets associated with it. As a result, +the query cannot be answered. This indicates a problem with supplied data. % DATASRC_QUERY_EMPTY_DNAME the DNAME on '%1' is empty During an attempt to synthesize CNAME from this DNAME it was discovered the @@ -262,11 +261,11 @@ DNAME is empty (it has no records). This indicates problem with supplied data. % DATASRC_QUERY_FAIL query failed Some subtask of query processing failed. The reason should have been reported -already. We are returning SERVFAIL. +already and a SERVFAIL will be returned to the querying system. % DATASRC_QUERY_FOLLOW_CNAME following CNAME at '%1' -Debug information. The domain is a CNAME (or a DNAME and we created a CNAME -for it already), so it's being followed. +Debug information. The domain is a CNAME (or a DNAME and a CNAME for it +has already been created) and the search is following this chain. % DATASRC_QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2' Debug information. While processing a query, a MX record was met. It @@ -291,14 +290,14 @@ operation code. Debug information. The last DO_QUERY is an auth query. % DATASRC_QUERY_IS_GLUE glue query (%1/%2) -Debug information. The last DO_QUERY is query for glue addresses. +Debug information. The last DO_QUERY is a query for glue addresses. % DATASRC_QUERY_IS_NOGLUE query for non-glue addresses (%1/%2) -Debug information. The last DO_QUERY is query for addresses that are not +Debug information. The last DO_QUERY is a query for addresses that are not glue. % DATASRC_QUERY_IS_REF query for referral (%1/%2) -Debug information. The last DO_QUERY is query for referral information. +Debug information. The last DO_QUERY is a query for referral information. % DATASRC_QUERY_IS_SIMPLE simple query (%1/%2) Debug information. The last DO_QUERY is a simple query. @@ -345,7 +344,7 @@ domain. Maybe someone sent a query to the wrong server for some reason. % DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class Debug information. A sure query is being processed now. -% DATASRC_QUERY_PROVENX_FAIL unable to prove nonexistence of '%1' +% DATASRC_QUERY_PROVE_NX_FAIL unable to prove nonexistence of '%1' The user wants DNSSEC and we discovered the entity doesn't exist (either domain or the record). But there was an error getting NSEC/NSEC3 record to prove the nonexistence. @@ -365,9 +364,9 @@ error, 2 is not implemented. The data source should have logged the specific error already. % DATASRC_QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1' -Debug information. While answering a query, a DNAME was met. The DNAME itself -will be returned, but along with it a CNAME for clients which don't understand -DNAMEs will be synthesized. +This is a debug message. While answering a query, a DNAME was encountered. The +DNAME itself will be returned, along with a synthesized CNAME for clients that +do not understand the DNAME RR. % DATASRC_QUERY_TASK_FAIL task failed with %1 The query subtask failed. The reason should have been reported by the subtask @@ -391,7 +390,7 @@ domain is being looked for now. During an attempt to cover the domain by a wildcard an error happened. The exact kind was hopefully already reported. -% DATASRC_QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2) +% DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL unable to prove nonexistence of '%1' (%2) While processing a wildcard, it wasn't possible to prove nonexistence of the given domain or record. The code is 1 for error and 2 for not implemented. @@ -464,20 +463,27 @@ Debug information. The SQLite data source is loading an SQLite database in the provided file. % DATASRC_SQLITE_PREVIOUS looking for name previous to '%1' -Debug information. We're trying to look up name preceding the supplied one. +This is a debug message. The name given was not found, so the program +is searching for the next name higher up the hierarchy (e.g. if +www.example.com were queried for and not found, the sofftware searches +for the "previous" name, example.com). % DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1' -The SQLite data source tried to identify name preceding this one. But this -one is not contained in any zone in the data source. +The name given was not found, so the program is searching for the next +name higher up the hierarchy (e.g. if www.example.com were queried +for and not found, the sofftware searches for the "previous" name, +example.com). However, this name is not contained in any zone in the +data source. This is an error since it indicates a problem in the earlier +processing of the query. % DATASRC_SQLITE_SETUP setting up SQLite database The database for SQLite data source was found empty. It is assumed this is the first run and it is being initialized with current schema. It'll still contain no data, but it will be ready for use. -% DATASRC_STATIC_BAD_CLASS static data source can handle CH only -For some reason, someone asked the static data source a query that is not in -the CH class. +% DATASRC_STATIC_CLASS_NOT_CH static data source can handle CH class only +An error message indicating that a query requesting a RR for a class other +that CH was sent to the static data source (which only handles CH queries). % DATASRC_STATIC_CREATE creating the static datasource Debug information. The static data source (the one holding stuff like diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index b8019a24b4..1fd4eafb8b 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -129,7 +129,7 @@ struct MemoryZone::MemoryZoneImpl { // Ensure CNAME and other type of RR don't coexist for the same // owner name. if (rrset->getType() == RRType::CNAME()) { - // XXX: this check will become incorrect when we support DNSSEC + // TODO: this check will become incorrect when we support DNSSEC // (depending on how we support DNSSEC). We should revisit it // at that point. if (!domain->empty()) { diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc index dee14b9d1f..65229a05d0 100644 --- a/src/lib/datasrc/static_datasrc.cc +++ b/src/lib/datasrc/static_datasrc.cc @@ -161,7 +161,7 @@ StaticDataSrc::findRRset(const Name& qname, arg(qtype); flags = 0; if (qclass != getClass() && qclass != RRClass::ANY()) { - LOG_ERROR(logger, DATASRC_STATIC_BAD_CLASS); + LOG_ERROR(logger, DATASRC_STATIC_CLASS_NOT_CH); return (ERROR); } From bf9c46a19ba59fa798236b64521fc6d95f18e076 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 11 Jul 2011 11:10:48 -0700 Subject: [PATCH 166/974] [master] fixed the usual MacOS X regression by setting LIBRARY_PATH_PLACEHOLDER in Makefile.am for python tests. --- src/bin/stats/tests/Makefile.am | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index 8163c7fe8e..dad6c48bbc 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -4,6 +4,13 @@ PYTESTS = b10-stats_test.py b10-stats-httpd_test.py EXTRA_DIST = $(PYTESTS) fake_time.py fake_socket.py fake_select.py CLEANFILES = fake_time.pyc fake_socket.pyc fake_select.pyc +# If necessary (rare cases), explicitly specify paths to dynamic libraries +# required by loadable python modules. +LIBRARY_PATH_PLACEHOLDER = +if SET_ENV_LIBRARY_PATH +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +endif + # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE @@ -13,6 +20,7 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ 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/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \ B10_FROM_SOURCE=$(abs_top_srcdir) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ From 7469b1f920d47306f87aab0e2fa0533903bc61af Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 11 Jul 2011 21:26:56 +0000 Subject: [PATCH 167/974] [master] remove the const qualifier from RequestACL_destroy. sunstudio doesn't seem to like this type of mismatch. Since the function is very short, removing this const wouldn't matter much. --- src/lib/python/isc/acl/dns_requestacl_python.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc index 326c7172ef..739639ef2a 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.cc +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -65,7 +65,7 @@ RequestACL_init(PyObject*, PyObject*, PyObject*) { } void -RequestACL_destroy(PyObject* const po_self) { +RequestACL_destroy(PyObject* po_self) { s_RequestACL* const self = static_cast(po_self); self->cppobj.reset(); Py_TYPE(self)->tp_free(self); From f29890eed7bad4aead5e95cfa6aae147287a0b10 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 11 Jul 2011 18:05:28 -0700 Subject: [PATCH 168/974] [trac983] worked around the symbol sharing issue between multiple .so's. there doesn't seem to be a simple and portable way to allow direct sharing, so I chose to get access to the variables via the python interpretor. --- src/lib/python/isc/acl/Makefile.am | 3 +- src/lib/python/isc/acl/acl.cc | 16 +++--- src/lib/python/isc/acl/acl.h | 35 ------------- src/lib/python/isc/acl/dns.cc | 30 ++++++++++- src/lib/python/isc/acl/dns.h | 52 +++++++++++++++++++ .../python/isc/acl/dns_requestacl_python.cc | 8 +-- .../isc/acl/dns_requestcontext_python.cc | 5 +- 7 files changed, 94 insertions(+), 55 deletions(-) delete mode 100644 src/lib/python/isc/acl/acl.h create mode 100644 src/lib/python/isc/acl/dns.h diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index 4a9c5f0ac4..64e6bf35bd 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -10,7 +10,7 @@ pythondir = $(PYTHON_SITEPKG_DIR)/isc/acl pyexec_LTLIBRARIES = acl.la dns.la pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/acl -acl_la_SOURCES = acl.h acl.cc +acl_la_SOURCES = acl.cc acl_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) acl_la_LDFLAGS = $(PYTHON_LDFLAGS) acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) @@ -31,7 +31,6 @@ acl_la_LIBADD += $(PYTHON_LIB) dns_la_LDFLAGS += -module dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la -dns_la_LIBADD += acl.la dns_la_LIBADD += $(PYTHON_LIB) EXTRA_DIST = acl.py dns.py diff --git a/src/lib/python/isc/acl/acl.cc b/src/lib/python/isc/acl/acl.cc index 64279064a9..2a16e8894b 100644 --- a/src/lib/python/isc/acl/acl.cc +++ b/src/lib/python/isc/acl/acl.cc @@ -18,24 +18,22 @@ #include -#include "acl.h" - using namespace isc::util::python; -using namespace isc::acl::python; -namespace isc { -namespace acl { -namespace python { +namespace { +// Commonly used Python exception objects. Right now the acl module consists +// of only one .cc file, so we hide them in an unnamed namespace. If and when +// we extend this module with multiple .cc files, we should move them to +// a named namespace, say isc::acl::python, and declare them in a separate +// header file. PyObject* po_ACLError; PyObject* po_LoaderError; } -} -} namespace { PyModuleDef acl = { { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, - "isc.acl", + "isc.acl.acl", "This module provides Python bindings for the C++ classes in the " "isc::acl namespace", -1, diff --git a/src/lib/python/isc/acl/acl.h b/src/lib/python/isc/acl/acl.h deleted file mode 100644 index 59e083f991..0000000000 --- a/src/lib/python/isc/acl/acl.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC 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. - -#ifndef __PYTHON_ACL_H -#define __PYTHON_ACL_H 1 - -#include - -namespace isc { -namespace acl { -namespace python { - -extern PyObject* po_ACLError; -extern PyObject* po_LoaderError; - -} // namespace python -} // namespace acl -} // namespace isc - -#endif // __PYTHON_ACL_H - -// Local Variables: -// mode: c++ -// End: diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc index 83d7642d2a..7a5c0e88a0 100644 --- a/src/lib/python/isc/acl/dns.cc +++ b/src/lib/python/isc/acl/dns.cc @@ -24,7 +24,7 @@ #include #include -#include "acl.h" +#include "dns.h" #include "dns_requestcontext_python.h" #include "dns_requestacl_python.h" @@ -53,7 +53,7 @@ loadRequestACL(PyObject*, PyObject* args) { } return (py_acl); } catch (const exception& ex) { - PyErr_SetString(isc::acl::python::po_LoaderError, ex.what()); + PyErr_SetString(getACLException("LoaderError"), ex.what()); return (NULL); } catch (...) { PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception"); @@ -86,6 +86,32 @@ PyModuleDef dnsacl = { }; } // end of unnamed namespace +namespace isc { +namespace acl { +namespace dns { +namespace python { +PyObject* +getACLException(const char* ex_name) { + PyObject* ex_obj = NULL; + + PyObject* acl_module = PyImport_AddModule("isc.acl.acl"); + if (acl_module != NULL) { + PyObject* acl_dict = PyModule_GetDict(acl_module); + if (acl_dict != NULL) { + ex_obj = PyDict_GetItemString(acl_dict, ex_name); + } + } + + if (ex_obj == NULL) { + ex_obj = PyExc_RuntimeError; + } + return (ex_obj); +} +} +} +} +} + PyMODINIT_FUNC PyInit_dns(void) { PyObject* mod = PyModule_Create(&dnsacl); diff --git a/src/lib/python/isc/acl/dns.h b/src/lib/python/isc/acl/dns.h new file mode 100644 index 0000000000..3c57ebadf0 --- /dev/null +++ b/src/lib/python/isc/acl/dns.h @@ -0,0 +1,52 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_ACL_DNS_H +#define __PYTHON_ACL_DNS_H 1 + +#include + +namespace isc { +namespace acl { +namespace dns { +namespace python { + +// Return a Python exception object of the given name (ex_name) defined in +// the isc.acl.acl loadable module. +// +// Since the acl module is a different binary image and is loaded separately +// from the dns module, it would be very tricky to directly access to +// C/C++ symbols defined in that module. So we get access to these object +// using the Python interpretor through this wrapper function. +// +// The __init__.py file should ensure isc.acl.acl has been loaded by the time +// whenever this function is called, and there shouldn't be no operation +// within this function that can fail (such as dynamic memory allocation), +// so this function should always succeed. Yet there may be an overlooked +// failure mode, perhaps due to a bug in the binding implementation, or +// due to invalid usage. As a last resort for such cases, this function +// returns PyExc_RuntimeError (a C binding of Python's RuntimeError) should +// it encounters an unexpected failure. +extern PyObject* getACLException(const char* ex_name); + +} // namespace python +} // namespace dns +} // namespace acl +} // namespace isc + +#endif // __PYTHON_ACL_DNS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/acl/dns_requestacl_python.cc b/src/lib/python/isc/acl/dns_requestacl_python.cc index 326c7172ef..a5c10f11a5 100644 --- a/src/lib/python/isc/acl/dns_requestacl_python.cc +++ b/src/lib/python/isc/acl/dns_requestacl_python.cc @@ -28,14 +28,13 @@ #include #include -#include "acl.h" +#include "dns.h" #include "dns_requestacl_python.h" #include "dns_requestcontext_python.h" using namespace std; using namespace isc::util::python; using namespace isc::acl; -using namespace isc::acl::python; using namespace isc::acl::dns; using namespace isc::acl::dns::python; @@ -60,7 +59,8 @@ s_RequestACL::s_RequestACL() {} namespace { int RequestACL_init(PyObject*, PyObject*, PyObject*) { - PyErr_SetString(po_ACLError, "RequestACL cannot be directly constructed"); + PyErr_SetString(getACLException("Error"), + "RequestACL cannot be directly constructed"); return (-1); } @@ -84,7 +84,7 @@ RequestACL_execute(PyObject* po_self, PyObject* args) { } } catch (const exception& ex) { const string ex_what = "Failed to execute ACL: " + string(ex.what()); - PyErr_SetString(po_ACLError, ex_what.c_str()); + PyErr_SetString(getACLException("Error"), ex_what.c_str()); } catch (...) { PyErr_SetString(PyExc_RuntimeError, "Unexpected exception in executing ACL"); diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.cc b/src/lib/python/isc/acl/dns_requestcontext_python.cc index 2da0c38302..6c63b59cbf 100644 --- a/src/lib/python/isc/acl/dns_requestcontext_python.cc +++ b/src/lib/python/isc/acl/dns_requestcontext_python.cc @@ -42,7 +42,7 @@ #include #include -#include "acl.h" +#include "dns.h" #include "dns_requestcontext_python.h" using namespace std; @@ -51,7 +51,6 @@ using boost::lexical_cast; using namespace isc; using namespace isc::util::python; using namespace isc::acl::dns; -using namespace isc::acl::python; using namespace isc::acl::dns::python; namespace isc { @@ -177,7 +176,7 @@ RequestContext_init(PyObject* po_self, PyObject* args, PyObject*) { } catch (const exception& ex) { const string ex_what = "Failed to construct RequestContext object: " + string(ex.what()); - PyErr_SetString(po_ACLError, ex_what.c_str()); + PyErr_SetString(getACLException("Error"), ex_what.c_str()); return (-1); } catch (...) { PyErr_SetString(PyExc_RuntimeError, From 78942e3fc11f22f1bdbbd8fdd629691d5c510a55 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 12 Jul 2011 01:16:39 +0000 Subject: [PATCH 169/974] [trac983] fixed solaris regression: socktype (and probably protocol) arguments must be specified explicitly. --- src/lib/python/isc/acl/tests/dns_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index 3924222323..a38359eb30 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -20,7 +20,8 @@ from isc.acl.dns import * def get_sockaddr(address, port): '''This is a simple shortcut wrapper for getaddrinfo''' - ai = socket.getaddrinfo(address, port, 0, 0, 0, socket.AI_NUMERICHOST)[0] + ai = socket.getaddrinfo(address, port, 0, socket.SOCK_DGRAM, + socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0] return ai[4] def get_acl(prefix): From fd2daaa2c1a27140568cf5a4f04baf57682214d2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 11 Jul 2011 18:32:15 -0700 Subject: [PATCH 170/974] [trac983] missing SOURCES for distcheck --- src/lib/python/isc/acl/Makefile.am | 2 +- src/lib/util/python/wrapper_template.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index 64e6bf35bd..2d5698d630 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -15,7 +15,7 @@ acl_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) acl_la_LDFLAGS = $(PYTHON_LDFLAGS) acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) -dns_la_SOURCES = dns.cc dns_requestacl_python.h dns_requestacl_python.cc +dns_la_SOURCES = dns.h dns.cc dns_requestacl_python.h dns_requestacl_python.cc dns_la_SOURCES += dns_requestcontext_python.h dns_requestcontext_python.cc dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) dns_la_LDFLAGS = $(PYTHON_LDFLAGS) diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc index 3a9282fbba..a703731261 100644 --- a/src/lib/util/python/wrapper_template.cc +++ b/src/lib/util/python/wrapper_template.cc @@ -210,7 +210,7 @@ namespace python { // Most of the functions are not actually implemented and NULL here. PyTypeObject @cppclass@_type = { PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.@CPPCLASS@", + "@MODULE@.@CPPCLASS@", sizeof(s_@CPPCLASS@), // tp_basicsize 0, // tp_itemsize reinterpret_cast(@CPPCLASS@_destroy), // tp_dealloc From 963e72656e6a5d8303034f9085c87834a75c44ce Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 12 Jul 2011 15:11:15 +0200 Subject: [PATCH 171/974] [trac1075] fix typos --- src/lib/datasrc/datasrc_messages.mes | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 62e39f1f78..59ce02b6d5 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -17,7 +17,7 @@ $NAMESPACE isc::datasrc # \brief Messages for the data source library % DATASRC_CACHE_CREATE creating the hotspot cache -This is a debug message issued during startup when the hostspot cache +This is a debug message issued during startup when the hotspot cache is created. % DATASRC_CACHE_DESTROY destroying the hotspot cache @@ -201,11 +201,11 @@ behave and BIND 9 refuses that as well. Please describe your intention using different tools. % DATASRC_META_ADD adding a data source into meta data source -This is a debug message issued during startup or reconfiguration. A -another data source is being added into the meta data source. +This is a debug message issued during startup or reconfiguration. +Another data source is being added into the meta data source. % DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2' -It was attempted to add a data source into a meta data source. But their +It was attempted to add a data source into a meta data source, but their classes do not match. % DATASRC_META_REMOVE removing data source from meta data source @@ -465,13 +465,13 @@ the provided file. % DATASRC_SQLITE_PREVIOUS looking for name previous to '%1' This is a debug message. The name given was not found, so the program is searching for the next name higher up the hierarchy (e.g. if -www.example.com were queried for and not found, the sofftware searches +www.example.com were queried for and not found, the software searches for the "previous" name, example.com). % DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1' The name given was not found, so the program is searching for the next name higher up the hierarchy (e.g. if www.example.com were queried -for and not found, the sofftware searches for the "previous" name, +for and not found, the software searches for the "previous" name, example.com). However, this name is not contained in any zone in the data source. This is an error since it indicates a problem in the earlier processing of the query. From d23f84732df2786fad5bf31f3446e0e088d941ec Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 12 Jul 2011 15:29:12 -0700 Subject: [PATCH 172/974] [trac983] cleanup for verbatim doxygen doc. mainly for the purpose of generating pydoc in the python API implementation. --- src/lib/acl/loader.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/acl/loader.h b/src/lib/acl/loader.h index c86373e1f9..f60b144d07 100644 --- a/src/lib/acl/loader.h +++ b/src/lib/acl/loader.h @@ -101,21 +101,21 @@ BasicAction defaultActionLoader(data::ConstElementPtr action); * * An ACL definition looks like this: * \verbatim - * [ - * { - * "action": "ACCEPT", - * "match-type": - * }, - * { - * "action": "REJECT", - * "match-type": - * "another-match-type": [, ] -* }, -* { -* "action": "DROP" -* } - * ] - * \endverbatim + [ + { + "action": "ACCEPT", + "match-type": + }, + { + "action": "REJECT", + "match-type": , + "another-match-type": [, ] + }, + { + "action": "DROP" + } + ] + \endverbatim * * This is a list of elements. Each element must have an "action" * entry/keyword. That one specifies which action is returned if this From d0df4daafee6703a7b52609b5681846f83310182 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 12 Jul 2011 15:34:41 -0700 Subject: [PATCH 173/974] [trac983] added bindings for RequestLoader as discussed in review. the only exposed interface is load() right now. also made overall document cleanup/enhancements. --- src/lib/python/isc/acl/Makefile.am | 2 + src/lib/python/isc/acl/acl.cc | 5 +- src/lib/python/isc/acl/acl_inc.cc | 16 ++ src/lib/python/isc/acl/dns.cc | 65 +++--- src/lib/python/isc/acl/dns_requestacl_inc.cc | 4 +- .../python/isc/acl/dns_requestloader_inc.cc | 84 ++++++++ .../isc/acl/dns_requestloader_python.cc | 201 ++++++++++++++++++ .../python/isc/acl/dns_requestloader_python.h | 46 ++++ src/lib/python/isc/acl/dnsacl_inc.cc | 28 ++- src/lib/python/isc/acl/tests/dns_test.py | 86 ++++---- 10 files changed, 447 insertions(+), 90 deletions(-) create mode 100644 src/lib/python/isc/acl/acl_inc.cc create mode 100644 src/lib/python/isc/acl/dns_requestloader_inc.cc create mode 100644 src/lib/python/isc/acl/dns_requestloader_python.cc create mode 100644 src/lib/python/isc/acl/dns_requestloader_python.h diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index 2d5698d630..d45e8240bc 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -17,6 +17,7 @@ acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) dns_la_SOURCES = dns.h dns.cc dns_requestacl_python.h dns_requestacl_python.cc dns_la_SOURCES += dns_requestcontext_python.h dns_requestcontext_python.cc +dns_la_SOURCES += dns_requestloader_python.h dns_requestloader_python.cc dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) dns_la_LDFLAGS = $(PYTHON_LDFLAGS) # Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be @@ -34,6 +35,7 @@ dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la dns_la_LIBADD += $(PYTHON_LIB) EXTRA_DIST = acl.py dns.py +EXTRA_DIST += acl_inc.cc EXTRA_DIST += dnsacl_inc.cc dns_requestacl_inc.cc dns_requestcontext_inc.cc CLEANDIRS = __pycache__ diff --git a/src/lib/python/isc/acl/acl.cc b/src/lib/python/isc/acl/acl.cc index 2a16e8894b..0124b5aa93 100644 --- a/src/lib/python/isc/acl/acl.cc +++ b/src/lib/python/isc/acl/acl.cc @@ -20,6 +20,8 @@ using namespace isc::util::python; +#include "acl_inc.cc" + namespace { // Commonly used Python exception objects. Right now the acl module consists // of only one .cc file, so we hide them in an unnamed namespace. If and when @@ -34,8 +36,7 @@ namespace { PyModuleDef acl = { { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, "isc.acl.acl", - "This module provides Python bindings for the C++ classes in the " - "isc::acl namespace", + acl_doc, -1, NULL, NULL, diff --git a/src/lib/python/isc/acl/acl_inc.cc b/src/lib/python/isc/acl/acl_inc.cc new file mode 100644 index 0000000000..a9f7c9da1b --- /dev/null +++ b/src/lib/python/isc/acl/acl_inc.cc @@ -0,0 +1,16 @@ +namespace { +const char* const acl_doc = "\ +Implementation module for ACL operations\n\n\ +This module provides Python bindings for the C++ classes in the\n\ +isc::acl namespace.\n\ +\n\ +Integer constants:\n\ +\n\ +ACCEPT, REJECT, DROP -- Default actions an ACL could perform.\n\ + These are the commonly used actions in specific ACLs.\n\ + It is possible to specify any other values, as the ACL class does\n\ + nothing about them, but these look reasonable, so they are provided\n\ + for convenience. It is not specified what exactly these mean and it's\n\ + up to whoever uses them.\n\ +"; +} // unnamed namespace diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc index 7a5c0e88a0..351a8b3e2e 100644 --- a/src/lib/python/isc/acl/dns.cc +++ b/src/lib/python/isc/acl/dns.cc @@ -27,6 +27,7 @@ #include "dns.h" #include "dns_requestcontext_python.h" #include "dns_requestacl_python.h" +#include "dns_requestloader_python.h" using namespace std; using boost::shared_ptr; @@ -38,45 +39,21 @@ using namespace isc::acl::dns::python; #include "dnsacl_inc.cc" namespace { -PyObject* -loadRequestACL(PyObject*, PyObject* args) { - const char* acl_config; - - if (PyArg_ParseTuple(args, "s", &acl_config)) { - try { - shared_ptr acl( - getRequestLoader().load(Element::fromJSON(acl_config))); - s_RequestACL* py_acl = static_cast( - requestacl_type.tp_alloc(&requestacl_type, 0)); - if (py_acl != NULL) { - py_acl->cppobj = acl; - } - return (py_acl); - } catch (const exception& ex) { - PyErr_SetString(getACLException("LoaderError"), ex.what()); - return (NULL); - } catch (...) { - PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception"); - return (NULL); - } - } - - return (NULL); -} +// This is a Python binding object corresponding to the singleton loader used +// in the C++ version of the library. +// We can define it as a pure object rather than through an accessor function, +// because in Python we can ensure it has been created and initialized +// in the module initializer by the time it's actually used. +s_RequestLoader* po_REQUEST_LOADER; PyMethodDef methods[] = { - { "load_request_acl", loadRequestACL, METH_VARARGS, load_request_acl_doc }, { NULL, NULL, 0, NULL } }; PyModuleDef dnsacl = { { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, "isc.acl.dns", - "This module provides Python bindings for the C++ classes in the " - "isc::acl::dns namespace. Specifically, it defines Python interfaces of " - "handling access control lists (ACLs) with DNS related contexts.\n\n" - "These bindings are close match to the C++ API, but they are not complete " - "(some parts are not needed) and some are done in more python-like ways.", + dnsacl_doc, -1, methods, NULL, @@ -127,6 +104,32 @@ PyInit_dns(void) { Py_DECREF(mod); return (NULL); } + if (!initModulePart_RequestLoader(mod)) { + Py_DECREF(mod); + return (NULL); + } + + // Module constants + try { + if (po_REQUEST_LOADER == NULL) { + po_REQUEST_LOADER = static_cast( + requestloader_type.tp_alloc(&requestloader_type, 0)); + } + if (po_REQUEST_LOADER != NULL) { + // We gain and keep our own reference to the singleton object + // for the same reason as that for exception objects (see comments + // in pycppwrapper_util for more details). Note also that we don't + // bother to release the reference even if exception is thrown + // below (in fact, we cannot delete the singleton loader). + po_REQUEST_LOADER->cppobj = &getRequestLoader(); + Py_INCREF(po_REQUEST_LOADER); + } + PyObjectContainer(po_REQUEST_LOADER).installToModule(mod, + "REQUEST_LOADER"); + } catch (...) { + Py_DECREF(mod); + return (NULL); + } return (mod); } diff --git a/src/lib/python/isc/acl/dns_requestacl_inc.cc b/src/lib/python/isc/acl/dns_requestacl_inc.cc index 9637170b30..673fa2372a 100644 --- a/src/lib/python/isc/acl/dns_requestacl_inc.cc +++ b/src/lib/python/isc/acl/dns_requestacl_inc.cc @@ -4,7 +4,7 @@ The DNS Request ACL.\n\ \n\ It holds bunch of ordered entries, each one consisting of a check for\n\ a given DNS Request context and an action, which is one of ACCEPT,\n\ -REJECT, or DROP, as defined in the isc.acl module.\n\ +REJECT, or DROP, as defined in the isc.acl.acl module.\n\ The checks are tested in the order and first match counts.\n\ \n\ A RequestACL object cannot be constructed directly; an application\n\ @@ -16,7 +16,7 @@ const char* const RequestACL_execute_doc = "\ execute(context) -> action \n\ \n\ The returned action is one of ACCEPT, REJECT or DROP as defined in\n\ -the isc.acl module.\n\ +the isc.acl.acl module.\n\ \n\ This is the function that takes the ACL entries one by one, checks the\n\ context against conditions and if it matches, returns the action that\n\ diff --git a/src/lib/python/isc/acl/dns_requestloader_inc.cc b/src/lib/python/isc/acl/dns_requestloader_inc.cc new file mode 100644 index 0000000000..904d7501fb --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestloader_inc.cc @@ -0,0 +1,84 @@ +namespace { +const char* const RequestLoader_doc = "\ +Loader of DNS Request ACLs.\n\ +\n\ +The goal of this class is to convert JSON description of an ACL to\n\ +object of the ACL class (including the checks inside it).\n\ +\n\ +The class can be used to load the checks only. This is supposed to be\n\ +used by compound checks to create the subexpressions.\n\ +\n\ +To allow any kind of checks to exist in the application, creators are\n\ +registered for the names of the checks (this feature is not yet\n\ +available for the python API).\n\ +\n\ +An ACL definition looks like this: [\n\ + {\n\ + \"action\": \"ACCEPT\",\n\ + \"match-type\": \n\ + },\n\ + {\n\ + \"action\": \"REJECT\",\n\ + \"match-type\": ,\n\ + \"another-match-type\": [, ]\n\ + },\n\ + {\n\ + \"action\": \"DROP\"\n\ + }\n\ + ]\n\ + \n\ +\n\ +This is a list of elements. Each element must have an \"action\"\n\ +entry/keyword. That one specifies which action is returned if this\n\ +element matches (the value of the key is passed to the action loader\n\ +(see the constructor), which is one of ACCEPT,\n\ +REJECT, or DROP, as defined in the isc.acl.acl module.\n\ +\n\ +The rest of the element are matches. The left side is the name of the\n\ +match type (for example \"from\" to match for source IP address).\n\ +The is whatever is needed to describe the\n\ +match and depends on the match type, the loader passes it verbatim to\n\ +creator of that match type.\n\ +\n\ +There may be multiple match types in single element. In such case, all\n\ +of the matches must match for the element to take action (so, in the\n\ +second element, both \"match-type\" and \"another-match-type\" must be\n\ +satisfied). If there's no match in the element, the action is\n\ +taken/returned without conditions, every time (makes sense as the last\n\ +entry, as the ACL will never get past it).\n\ +\n\ +The second entry shows another thing - if there's a list as the value\n\ +for some match and the match itself is not expecting a list, it is\n\ +taken as an \"or\" - a match for at last one of the choices in the\n\ +list must match. So, for the second entry, both \"match-type\" and\n\ +\"another-match-type\" must be satisfied, but the another one is\n\ +satisfied by either parameter1 or parameter2.\n\ +\n\ +Currently, a RequestLoader object cannot be constructed directly;\n\ +an application must use the singleton loader defined in the\n\ +isc.acl.dns module, i.e., isc.acl.dns.REQUEST_LOADER.\n\ +A future version of this implementation may be extended to give\n\ +applications full flexibility of creating arbitrary loader, when\n\ +this restriction may be removed.\n\ +"; + +const char* const RequestLoader_load_doc = "\ +load(description) -> RequestACL\n\ +\n\ +Load a DNS ACL.\n\ +\n\ +This parses an ACL list, creates internal data for each rule\n\ +and returns a RequestACl object that contains all given rules.\n\ +\n\ +Exceptions:\n\ + LoaderError Load failed. The most likely cause of this is a syntax\n\ + error in the description. Other internal errors such as\n\ + memory allocation failure is also converted to this\n\ + exception.\n\ +\n\ +Parameters:\n\ + description String representation of the JSON list of ACL.\n\ +\n\ +Return Value(s): The newly created RequestACL object\n\ +"; +} // unnamed namespace diff --git a/src/lib/python/isc/acl/dns_requestloader_python.cc b/src/lib/python/isc/acl/dns_requestloader_python.cc new file mode 100644 index 0000000000..a5779967bd --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestloader_python.cc @@ -0,0 +1,201 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// Enable this if you use s# variants with PyArg_ParseTuple(), see +// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers +//#define PY_SSIZE_T_CLEAN + +// Python.h needs to be placed at the head of the program file, see: +// http://docs.python.org/py3k/extending/extending.html#a-simple-example +#include + +#include +#include + +#include + +#include + +#include + +#include + +#include "dns.h" +#include "dns_requestacl_python.h" +#include "dns_requestloader_python.h" + +using namespace std; +using boost::shared_ptr; +using namespace isc::util::python; +using namespace isc::data; +using namespace isc::acl::dns; +using namespace isc::acl::dns::python; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// RequestLoader +// + +// Trivial constructor. +s_RequestLoader::s_RequestLoader() : cppobj(NULL) { +} + +// Import pydoc text +#include "dns_requestloader_inc.cc" + +namespace { +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +int +RequestLoader_init(PyObject*, PyObject*, PyObject*) { + PyErr_SetString(getACLException("Error"), + "RequestLoader cannot be directly constructed"); + return (-1); +} + +void +RequestLoader_destroy(PyObject* po_self) { + s_RequestLoader* const self = static_cast(po_self); + delete self->cppobj; + self->cppobj = NULL; + Py_TYPE(self)->tp_free(self); +} + +PyObject* +RequestLoader_load(PyObject* po_self, PyObject* args) { + s_RequestLoader* const self = static_cast(po_self); + const char* acl_config; + + if (PyArg_ParseTuple(args, "s", &acl_config)) { + try { + shared_ptr acl( + self->cppobj->load(Element::fromJSON(acl_config))); + s_RequestACL* py_acl = static_cast( + requestacl_type.tp_alloc(&requestacl_type, 0)); + if (py_acl != NULL) { + py_acl->cppobj = acl; + } + return (py_acl); + } catch (const exception& ex) { + PyErr_SetString(getACLException("LoaderError"), ex.what()); + return (NULL); + } catch (...) { + PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception"); + return (NULL); + } + } + + return (NULL); +} + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef RequestLoader_methods[] = { + { "load", RequestLoader_load, METH_VARARGS, RequestLoader_load_doc }, + { NULL, NULL, 0, NULL } +}; +} // end of unnamed namespace + +namespace isc { +namespace acl { +namespace dns { +namespace python { +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_RequestLoader +// Most of the functions are not actually implemented and NULL here. +PyTypeObject requestloader_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "isc.acl.dns.RequestLoader", + sizeof(s_RequestLoader), // tp_basicsize + 0, // tp_itemsize + RequestLoader_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + NULL, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + RequestLoader_doc, + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + RequestLoader_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + RequestLoader_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +bool +initModulePart_RequestLoader(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&requestloader_type) < 0) { + return (false); + } + void* p = &requestloader_type; + if (PyModule_AddObject(mod, "RequestLoader", + static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&requestloader_type); + + return (true); +} +} // namespace python +} // namespace dns +} // namespace acl +} // namespace isc diff --git a/src/lib/python/isc/acl/dns_requestloader_python.h b/src/lib/python/isc/acl/dns_requestloader_python.h new file mode 100644 index 0000000000..9d0b63ecee --- /dev/null +++ b/src/lib/python/isc/acl/dns_requestloader_python.h @@ -0,0 +1,46 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_REQUESTLOADER_H +#define __PYTHON_REQUESTLOADER_H 1 + +#include + +#include + +namespace isc { +namespace acl { +namespace dns { +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_RequestLoader : public PyObject { +public: + s_RequestLoader(); + RequestLoader* cppobj; +}; + +extern PyTypeObject requestloader_type; + +bool initModulePart_RequestLoader(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace acl +} // namespace isc +#endif // __PYTHON_REQUESTLOADER_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/acl/dnsacl_inc.cc b/src/lib/python/isc/acl/dnsacl_inc.cc index f68e193c10..b2e733821d 100644 --- a/src/lib/python/isc/acl/dnsacl_inc.cc +++ b/src/lib/python/isc/acl/dnsacl_inc.cc @@ -1,21 +1,17 @@ namespace { -const char* const load_request_acl_doc = "\ -load_request_acl(description) -> RequestACL\n\ +const char* const dnsacl_doc = "\ +Implementation module for DNS ACL operations\n\n\ +This module provides Python bindings for the C++ classes in the\n\ +isc::acl::dns namespace. Specifically, it defines Python interfaces of\n\ +handling access control lists (ACLs) with DNS related contexts.\n\ +These bindings are close match to the C++ API, but they are not complete\n\ +(some parts are not needed) and some are done in more python-like ways.\n\ \n\ -Load a DNS ACL.\n\ +Special objects:\n\ \n\ -This parses an ACL list, creates internal data for each rule\n\ -and returns a RequestACl object that contains all given rules.\n\ -\n\ -Exceptions:\n\ - LoaderError Load failed. The most likely cause of this is a syntax\n\ - error in the description. Other internal errors such as\n\ - memory allocation failure is also converted to this\n\ - exception.\n\ -\n\ -Parameters:\n\ - description String representation of the JSON list of ACL.\n\ -\n\ -Return Value(s): The newly created RequestACL object\n\ +REQUEST_LOADER -- A singleton loader of ACLs. It is expected applications\n\ + will use this function instead of creating their own loaders, because\n\ + one is enough, this one will have registered default checks and it is\n\ + known one, so any plugins can registrer additional checks as well.\n\ "; } // unnamed namespace diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index a38359eb30..fa6e802531 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -29,7 +29,8 @@ def get_acl(prefix): that accepts addresses for the given IP prefix (and reject any others by default) ''' - return load_request_acl('[{"action": "ACCEPT", "from": "' + prefix + '"}]') + return REQUEST_LOADER.load('[{"action": "ACCEPT", "from": "' + \ + prefix + '"}]') def get_context(address): '''This is a simple shortcut wrapper for creating a RequestContext @@ -98,64 +99,64 @@ class RequestACLTest(unittest.TestCase): def test_request_loader(self): # these shouldn't raise an exception - load_request_acl('[{"action": "DROP"}]') - load_request_acl('[{"action": "DROP", "from": "192.0.2.1"}]') + REQUEST_LOADER.load('[{"action": "DROP"}]') + REQUEST_LOADER.load('[{"action": "DROP", "from": "192.0.2.1"}]') # Invalid types - self.assertRaises(TypeError, load_request_acl, 1) - self.assertRaises(TypeError, load_request_acl, []) + self.assertRaises(TypeError, REQUEST_LOADER.load, 1) + self.assertRaises(TypeError, REQUEST_LOADER.load, []) # Incorrect number of arguments - self.assertRaises(TypeError, load_request_acl, + self.assertRaises(TypeError, REQUEST_LOADER.load, '[{"action": "DROP"}]', 0) def test_bad_acl_syntax(self): # the following are derived from loader_test.cc - self.assertRaises(LoaderError, load_request_acl, '{}'); - self.assertRaises(LoaderError, load_request_acl, '42'); - self.assertRaises(LoaderError, load_request_acl, 'true'); - self.assertRaises(LoaderError, load_request_acl, 'null'); - self.assertRaises(LoaderError, load_request_acl, '"hello"'); - self.assertRaises(LoaderError, load_request_acl, '[42]'); - self.assertRaises(LoaderError, load_request_acl, '["hello"]'); - self.assertRaises(LoaderError, load_request_acl, '[[]]'); - self.assertRaises(LoaderError, load_request_acl, '[true]'); - self.assertRaises(LoaderError, load_request_acl, '[null]'); - self.assertRaises(LoaderError, load_request_acl, '[{}]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '{}'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '42'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, 'true'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, 'null'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '"hello"'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[42]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '["hello"]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[[]]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[true]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[null]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{}]'); # the following are derived from dns_test.cc - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "bad": "192.0.2.1"}]') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": 4}]') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": []}]') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": "bad"}]') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": null}]') def test_bad_acl_ipsyntax(self): # this test is derived from ip_check_unittest.cc - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "192.0.2.43/-1"}]') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "192.0.2.43//1"') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "192.0.2.43/1/"') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "/192.0.2.43/1"') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "2001:db8::/xxxx"') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "2001:db8::/32/s"') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "1/"') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "/1"') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "192.0.2.0/33"') - self.assertRaises(LoaderError, load_request_acl, + self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "::1/129"') def test_execute(self): @@ -174,13 +175,13 @@ class RequestACLTest(unittest.TestCase): self.assertEqual(REJECT, get_acl('32.1.13.184').execute(CONTEXT6)) # A bit more complicated example, derived from resolver_config_unittest - acl = load_request_acl('[ {"action": "ACCEPT", ' + - ' "from": "192.0.2.1"},' + - ' {"action": "REJECT",' + - ' "from": "192.0.2.0/24"},' + - ' {"action": "DROP",' + - ' "from": "2001:db8::1"},' + - '] }') + acl = REQUEST_LOADER.load('[ {"action": "ACCEPT", ' + + ' "from": "192.0.2.1"},' + + ' {"action": "REJECT",' + + ' "from": "192.0.2.0/24"},' + + ' {"action": "DROP",' + + ' "from": "2001:db8::1"},' + + '] }') self.assertEqual(ACCEPT, acl.execute(CONTEXT4)) self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2'))) self.assertEqual(DROP, acl.execute(get_context('2001:db8::1'))) @@ -195,5 +196,12 @@ class RequestACLTest(unittest.TestCase): # type mismatch self.assertRaises(TypeError, acl.execute, 'bad parameter') +class RequestLoaderTest(unittest.TestCase): + # Note: loading ACLs is tested in other test cases. + + def test_construct(self): + # at least for now, we don't allow direct construction. + self.assertRaises(Error, RequestLoader) + if __name__ == '__main__': unittest.main() From 7cb53c7b33c41bc8c5d76c6994caae800692108d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 12 Jul 2011 15:42:58 -0700 Subject: [PATCH 174/974] [trac983] missing EXTRA_DIST --- src/lib/python/isc/acl/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index d45e8240bc..cabc0a30cc 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -37,6 +37,7 @@ dns_la_LIBADD += $(PYTHON_LIB) EXTRA_DIST = acl.py dns.py EXTRA_DIST += acl_inc.cc EXTRA_DIST += dnsacl_inc.cc dns_requestacl_inc.cc dns_requestcontext_inc.cc +EXTRA_DIST += dns_requestloader_inc.cc CLEANDIRS = __pycache__ From f8092952b50ef238e2ffc63ccb6d17a469f22966 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 12 Jul 2011 16:05:32 -0700 Subject: [PATCH 175/974] [trac983] fixed typo in comments --- src/lib/python/isc/acl/dns.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/acl/dns.h b/src/lib/python/isc/acl/dns.h index 3c57ebadf0..76849c5a3a 100644 --- a/src/lib/python/isc/acl/dns.h +++ b/src/lib/python/isc/acl/dns.h @@ -31,7 +31,7 @@ namespace python { // using the Python interpretor through this wrapper function. // // The __init__.py file should ensure isc.acl.acl has been loaded by the time -// whenever this function is called, and there shouldn't be no operation +// whenever this function is called, and there shouldn't be any operation // within this function that can fail (such as dynamic memory allocation), // so this function should always succeed. Yet there may be an overlooked // failure mode, perhaps due to a bug in the binding implementation, or From 673a619cd628130b0506a5d3669fd6a4d139c790 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 12 Jul 2011 16:31:14 -0700 Subject: [PATCH 176/974] [trac983] clarified comments on refcount of some python constant objects. --- src/lib/python/isc/acl/acl.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/acl/acl.cc b/src/lib/python/isc/acl/acl.cc index 0124b5aa93..6517a1256a 100644 --- a/src/lib/python/isc/acl/acl.cc +++ b/src/lib/python/isc/acl/acl.cc @@ -60,9 +60,11 @@ PyInit_acl(void) { po_LoaderError = PyErr_NewException("isc.acl.LoaderError", NULL, NULL); PyObjectContainer(po_LoaderError).installToModule(mod, "LoaderError"); - // Install module constants. Note that we can release our own - // references to these objects because we don't have corresponding - // C++ variables. + // Install module constants. Note that we can let Py_BuildValue + // "steal" the references to these object (by specifying false to + // installToModule), because, unlike the exception cases above, + // we don't have corresponding C++ variables (see the note in + // pycppwrapper_util for more details). PyObjectContainer(Py_BuildValue("I", isc::acl::ACCEPT)). installToModule(mod, "ACCEPT", false); PyObjectContainer(Py_BuildValue("I", isc::acl::REJECT)). From dffeeebd09195ad602090501c8c9b05b55885596 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 13 Jul 2011 02:16:17 -0700 Subject: [PATCH 177/974] [trac983] added the default constructor and reset() to PyObjectContainer, which will be used for the remaining work in trac983. --- src/lib/util/python/pycppwrapper_util.h | 29 ++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h index fd55c19039..3f396e2b1d 100644 --- a/src/lib/util/python/pycppwrapper_util.h +++ b/src/lib/util/python/pycppwrapper_util.h @@ -94,6 +94,22 @@ public: /// the reference to be decreased, the original bare pointer should be /// extracted using the \c release() method. /// +/// In some other cases, it would be convenient if it's possible to create +/// an "empty" container and reset it with a Python object later. +/// For example, we may want to create a temporary Python object in the +/// middle of a function and make sure that it's valid within the rest of +/// the function scope, while we want to make sure its reference is released +/// when the function returns (either normally or as a result of exception). +/// To allow this scenario, this class defines the default constructor +/// and the \c reset() method. The default constructor allows the class +/// object with an "empty" (NULL) Python object, while \c reset() allows +/// the stored object to be replaced with a new one. If there's a valid +/// object was already set, \c reset() releases its reference. +/// In general, it's safer to construct the container object with a valid +/// Python object pointer. The use of the default constructor and +/// \c reset() should therefore be restricted to cases where it's +/// absolutely necessary. +/// /// There are two convenience methods for commonly used operations: /// \c installAsClassVariable() to add the PyObject as a class variable /// and \c installToModule to add the PyObject to a specified python module. @@ -166,17 +182,28 @@ public: /// exception in a python biding written in C/C++. See the code comment /// of the method for more details. struct PyObjectContainer { + PyObjectContainer() : obj_(NULL) {} PyObjectContainer(PyObject* obj) : obj_(obj) { if (obj_ == NULL) { isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, " "probably due to short memory"); } } - virtual ~PyObjectContainer() { + ~PyObjectContainer() { if (obj_ != NULL) { Py_DECREF(obj_); } } + void reset(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, " + "probably due to short memory"); + } + if (obj_ != NULL) { + Py_DECREF(obj_); + } + obj_ = obj; + } PyObject* get() { return (obj_); } From cca39b307de50546d7e3c4cd9fe4c2435223bf21 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 13 Jul 2011 02:16:51 -0700 Subject: [PATCH 178/974] [trac983] allowed python representation of JSON (as well as bare string) in load() --- .../python/isc/acl/dns_requestloader_inc.cc | 6 +- .../isc/acl/dns_requestloader_python.cc | 72 ++++++++++++-- src/lib/python/isc/acl/tests/dns_test.py | 97 ++++++++++++++++--- 3 files changed, 152 insertions(+), 23 deletions(-) diff --git a/src/lib/python/isc/acl/dns_requestloader_inc.cc b/src/lib/python/isc/acl/dns_requestloader_inc.cc index 904d7501fb..e05a58012b 100644 --- a/src/lib/python/isc/acl/dns_requestloader_inc.cc +++ b/src/lib/python/isc/acl/dns_requestloader_inc.cc @@ -65,7 +65,7 @@ this restriction may be removed.\n\ const char* const RequestLoader_load_doc = "\ load(description) -> RequestACL\n\ \n\ -Load a DNS ACL.\n\ +Load a DNS (Request) ACL.\n\ \n\ This parses an ACL list, creates internal data for each rule\n\ and returns a RequestACl object that contains all given rules.\n\ @@ -77,7 +77,9 @@ Exceptions:\n\ exception.\n\ \n\ Parameters:\n\ - description String representation of the JSON list of ACL.\n\ + description String or Python representation of the JSON list of\n\ + ACL. The Python representation is ones accepted by the\n\ + standard json module.\n\ \n\ Return Value(s): The newly created RequestACL object\n\ "; diff --git a/src/lib/python/isc/acl/dns_requestloader_python.cc b/src/lib/python/isc/acl/dns_requestloader_python.cc index a5779967bd..aa5df678f5 100644 --- a/src/lib/python/isc/acl/dns_requestloader_python.cc +++ b/src/lib/python/isc/acl/dns_requestloader_python.cc @@ -82,13 +82,58 @@ RequestLoader_destroy(PyObject* po_self) { Py_TYPE(self)->tp_free(self); } +// This helper function essentially does: +// import json +// return json.dumps +// Getting access to the json module this way and call one of its functions +// via PyObject_CallObject() may exceed the reasonably acceptable level for +// straightforward bindings. But the alternative would be to write a Python +// frontend for the entire module only for this conversion, which would also +// be too much. So, right now, we implement everything within the binding +// implementation. If future extensions require more such non trivial +// wrappers, we should consider the frontend approach more seriously. +PyObject* +getJSONDumpsObj() { + PyObject* json_dump_obj = NULL; + PyObject* json_module = PyImport_AddModule("json"); + if (json_module != NULL) { + PyObject* json_dict = PyModule_GetDict(json_module); + if (json_dict != NULL) { + json_dump_obj = PyDict_GetItemString(json_dict, "dumps"); + } + } + return (json_dump_obj); +} + PyObject* RequestLoader_load(PyObject* po_self, PyObject* args) { s_RequestLoader* const self = static_cast(po_self); - const char* acl_config; - if (PyArg_ParseTuple(args, "s", &acl_config)) { - try { + try { + PyObjectContainer c1, c2; // placeholder for temporary py objects + const char* acl_config; + + // First, try string + int py_result = PyArg_ParseTuple(args, "s", &acl_config); + if (!py_result) { + PyErr_Clear(); // need to clear the error from ParseTuple + + // If that fails, confirm the argument is a single Python object, + // and pass the argument to json.dumps() without conversion. + // Note that we should pass 'args', not 'json_obj' to + // PyObject_CallObject(), since this function expects a form of + // tuple as its argument parameter, just like ParseTuple. + PyObject* json_obj; + if (PyArg_ParseTuple(args, "O", &json_obj)) { + PyObject* json_dumps_obj = getJSONDumpsObj(); + if (json_dumps_obj != NULL) { + c1.reset(PyObject_CallObject(json_dumps_obj, args)); + c2.reset(Py_BuildValue("(O)", c1.get())); + py_result = PyArg_ParseTuple(c2.get(), "s", &acl_config); + } + } + } + if (py_result) { shared_ptr acl( self->cppobj->load(Element::fromJSON(acl_config))); s_RequestACL* py_acl = static_cast( @@ -97,15 +142,24 @@ RequestLoader_load(PyObject* po_self, PyObject* args) { py_acl->cppobj = acl; } return (py_acl); - } catch (const exception& ex) { - PyErr_SetString(getACLException("LoaderError"), ex.what()); - return (NULL); - } catch (...) { - PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception"); - return (NULL); } + } catch (const PyCPPWrapperException&) { + // If the wrapper utility throws, it's most likely because an invalid + // type of argument is passed (and the call to json.dumps() failed + // above), rather than a rare case of system errors such as memory + // allocation failure. So we fall through to the end of this function + // and raise a TypeError. + ; + } catch (const exception& ex) { + PyErr_SetString(getACLException("LoaderError"), ex.what()); + return (NULL); + } catch (...) { + PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception"); + return (NULL); } + PyErr_SetString(PyExc_TypeError, "RequestLoader.load() " + "expects str or python representation of JSON"); return (NULL); } diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index fa6e802531..acaf32bbeb 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -32,6 +32,13 @@ def get_acl(prefix): return REQUEST_LOADER.load('[{"action": "ACCEPT", "from": "' + \ prefix + '"}]') +def get_acl_json(prefix): + '''Same as get_acl, but this function passes a Python representation of + JSON to the loader, not a string.''' + json = [{"action": "ACCEPT"}] + json[0]["from"] = prefix + return REQUEST_LOADER.load(json) + def get_context(address): '''This is a simple shortcut wrapper for creating a RequestContext object with a given IP address. Port number doesn't matter in the test @@ -100,11 +107,14 @@ class RequestACLTest(unittest.TestCase): def test_request_loader(self): # these shouldn't raise an exception REQUEST_LOADER.load('[{"action": "DROP"}]') + REQUEST_LOADER.load([{"action": "DROP"}]) REQUEST_LOADER.load('[{"action": "DROP", "from": "192.0.2.1"}]') + REQUEST_LOADER.load([{"action": "DROP", "from": "192.0.2.1"}]) - # Invalid types - self.assertRaises(TypeError, REQUEST_LOADER.load, 1) - self.assertRaises(TypeError, REQUEST_LOADER.load, []) + # Invalid types (note that arguments like '1' or '[]' is of valid + # 'type' (but syntax error at a higher level)). So we need to use + # something that is not really JSON nor string. + self.assertRaises(TypeError, REQUEST_LOADER.load, b'') # Incorrect number of arguments self.assertRaises(TypeError, REQUEST_LOADER.load, @@ -113,66 +123,119 @@ class RequestACLTest(unittest.TestCase): def test_bad_acl_syntax(self): # the following are derived from loader_test.cc self.assertRaises(LoaderError, REQUEST_LOADER.load, '{}'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, {}); self.assertRaises(LoaderError, REQUEST_LOADER.load, '42'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, 42); self.assertRaises(LoaderError, REQUEST_LOADER.load, 'true'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, True); self.assertRaises(LoaderError, REQUEST_LOADER.load, 'null'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, None); self.assertRaises(LoaderError, REQUEST_LOADER.load, '"hello"'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, "hello"); self.assertRaises(LoaderError, REQUEST_LOADER.load, '[42]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, [42]); self.assertRaises(LoaderError, REQUEST_LOADER.load, '["hello"]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, ["hello"]); self.assertRaises(LoaderError, REQUEST_LOADER.load, '[[]]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, [[]]); self.assertRaises(LoaderError, REQUEST_LOADER.load, '[true]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, [True]); self.assertRaises(LoaderError, REQUEST_LOADER.load, '[null]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, [None]); self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{}]'); + self.assertRaises(LoaderError, REQUEST_LOADER.load, [{}]); # the following are derived from dns_test.cc self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "bad": "192.0.2.1"}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "bad": "192.0.2.1"}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": 4}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "from": 4}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": []}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "from": []}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": "bad"}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "from": "bad"}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": null}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "from": None}]) def test_bad_acl_ipsyntax(self): # this test is derived from ip_check_unittest.cc self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "DROP", "from": "192.0.2.43/-1"}]') self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "192.0.2.43//1"') + [{"action": "DROP", "from": "192.0.2.43/-1"}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "192.0.2.43/1/"') + '[{"action": "DROP", "from": "192.0.2.43//1"}]') self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "/192.0.2.43/1"') + [{"action": "DROP", "from": "192.0.2.43//1"}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "2001:db8::/xxxx"') + '[{"action": "DROP", "from": "192.0.2.43/1/"}]') self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "2001:db8::/32/s"') + [{"action": "DROP", "from": "192.0.2.43/1/"}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "1/"') + '[{"action": "DROP", "from": "/192.0.2.43/1"}]') self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "/1"') + [{"action": "DROP", "from": "/192.0.2.43/1"}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "192.0.2.0/33"') + '[{"action": "DROP", "from": "2001:db8::/xxxx"}]') self.assertRaises(LoaderError, REQUEST_LOADER.load, - '[{"action": "DROP", "from": "::1/129"') + [{"action": "DROP", "from": "2001:db8::/xxxx"}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + '[{"action": "DROP", "from": "2001:db8::/32/s"}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "DROP", "from": "2001:db8::/32/s"}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + '[{"action": "DROP", "from": "1/"}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "DROP", "from": "1/"}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + '[{"action": "DROP", "from": "/1"}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "DROP", "from": "/1"}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + '[{"action": "DROP", "from": "192.0.2.0/33"}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "DROP", "from": "192.0.2.0/33"}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + '[{"action": "DROP", "from": "::1/129"}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "DROP", "from": "::1/129"}]) def test_execute(self): # tests derived from dns_test.cc. We don't directly expose checks # in the python wrapper, so we test it via execute(). self.assertEqual(ACCEPT, get_acl('192.0.2.1').execute(CONTEXT4)) + self.assertEqual(ACCEPT, get_acl_json('192.0.2.1').execute(CONTEXT4)) self.assertEqual(REJECT, get_acl('192.0.2.53').execute(CONTEXT4)) + self.assertEqual(REJECT, get_acl_json('192.0.2.53').execute(CONTEXT4)) self.assertEqual(ACCEPT, get_acl('192.0.2.0/24').execute(CONTEXT4)) + self.assertEqual(ACCEPT, get_acl_json('192.0.2.0/24').execute(CONTEXT4)) self.assertEqual(REJECT, get_acl('192.0.1.0/24').execute(CONTEXT4)) + self.assertEqual(REJECT, get_acl_json('192.0.1.0/24').execute(CONTEXT4)) self.assertEqual(REJECT, get_acl('192.0.1.0/24').execute(CONTEXT4)) + self.assertEqual(REJECT, get_acl_json('192.0.1.0/24').execute(CONTEXT4)) self.assertEqual(ACCEPT, get_acl('2001:db8::1').execute(CONTEXT6)) + self.assertEqual(ACCEPT, get_acl_json('2001:db8::1').execute(CONTEXT6)) self.assertEqual(REJECT, get_acl('2001:db8::53').execute(CONTEXT6)) + self.assertEqual(REJECT, get_acl_json('2001:db8::53').execute(CONTEXT6)) self.assertEqual(ACCEPT, get_acl('2001:db8::/64').execute(CONTEXT6)) + self.assertEqual(ACCEPT, + get_acl_json('2001:db8::/64').execute(CONTEXT6)) self.assertEqual(REJECT, get_acl('2001:db8:1::/64').execute(CONTEXT6)) + self.assertEqual(REJECT, + get_acl_json('2001:db8:1::/64').execute(CONTEXT6)) self.assertEqual(REJECT, get_acl('32.1.13.184').execute(CONTEXT6)) + self.assertEqual(REJECT, get_acl_json('32.1.13.184').execute(CONTEXT6)) # A bit more complicated example, derived from resolver_config_unittest acl = REQUEST_LOADER.load('[ {"action": "ACCEPT", ' + @@ -187,6 +250,16 @@ class RequestACLTest(unittest.TestCase): self.assertEqual(DROP, acl.execute(get_context('2001:db8::1'))) self.assertEqual(REJECT, acl.execute(get_context('2001:db8::2'))) + # same test using the JSON representation + acl = REQUEST_LOADER.load([{"action": "ACCEPT", "from": "192.0.2.1"}, + {"action": "REJECT", + "from": "192.0.2.0/24"}, + {"action": "DROP", "from": "2001:db8::1"}]) + self.assertEqual(ACCEPT, acl.execute(CONTEXT4)) + self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2'))) + self.assertEqual(DROP, acl.execute(get_context('2001:db8::1'))) + self.assertEqual(REJECT, acl.execute(get_context('2001:db8::2'))) + def test_bad_execute(self): acl = get_acl('192.0.2.1') # missing parameter From 834d48869745039bbd874d76bcafb4ac6ce7a4e8 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 13 Jul 2011 11:23:02 +0200 Subject: [PATCH 179/974] [trac926] error on set for nonexistent item --- src/bin/bindctl/bindcmd.py | 12 +++++-- src/lib/python/isc/config/config_data.py | 36 +++++++++++++------ .../python/isc/config/tests/ccsession_test.py | 9 ++--- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index 6e38e6f269..85ee4fa84d 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -628,7 +628,7 @@ class BindCmdInterpreter(Cmd): values = self.config_data.get_value_maps(identifier, show_all) for value_map in values: line = value_map['name'] - if value_map['type'] in [ 'module', 'map', 'named_map' ]: + if value_map['type'] in [ 'module', 'map' ]: line += "/" elif value_map['type'] == 'list' \ and value_map['value'] != []: @@ -636,8 +636,14 @@ class BindCmdInterpreter(Cmd): # we have more data to show line += "/" else: - # if type is named_map, don't print value - if value_map['type'] != 'named_map': + # if type is named_map, don't print value if None + # (it is either {} meaning empty, or None, meaning + # there actually is data, but not to be shown with + # the current command + if value_map['type'] == 'named_map' and\ + value_map['value'] is None: + line += "/\t" + else: line += "\t" + json.dumps(value_map['value']) line += "\t" + value_map['type'] line += "\t" diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py index 94bcc12194..3e6f4040fe 100644 --- a/src/lib/python/isc/config/config_data.py +++ b/src/lib/python/isc/config/config_data.py @@ -410,7 +410,6 @@ class MultiConfigData: id_parts = isc.cc.data.split_identifier(id) id_prefix = "" while len(id_parts) > 0: - # [XX] TODO: Refactor id_part = id_parts.pop(0) item_id, list_indices = isc.cc.data.split_identifier_list_indices(id_part) id_list = module + "/" + id_prefix + "/" + item_id @@ -527,7 +526,7 @@ class MultiConfigData: spec_part_list = spec_part['list_item_spec'] list_value, status = self.get_value(identifier) if list_value is None: - raise isc.cc.data.DataNotFoundError(identifier) + raise isc.cc.data.DataNotFoundError(identifier + " not found") if type(list_value) != list: # the identifier specified a single element @@ -550,11 +549,10 @@ class MultiConfigData: self._append_value_item(result, spec_part_map, identifier, all) elif item_type == "named_map": value, status = self.get_value(identifier) - if status == self.NONE or (status == self.DEFAULT and value == {}): - raise isc.cc.data.DataNotFoundError(identifier) + # show just the one entry, when either the map is empty, # or when this is element is not requested specifically - if (len(value.keys()) == 0 and (all or first)): + if len(value.keys()) == 0: entry = _create_value_map_entry(identifier, item_type, {}, status) @@ -567,12 +565,14 @@ class MultiConfigData: else: spec_part_named_map = spec_part['named_map_item_spec'] for entry in value: - # xxxxxxxxxxx - self._append_value_item(result, spec_part_named_map, identifier + "/" + entry, all) + self._append_value_item(result, + spec_part_named_map, + identifier + "/" + entry, + all) else: value, status = self.get_value(identifier) if status == self.NONE and not spec_part['item_optional']: - raise isc.cc.data.DataNotFoundError(identifier) + raise isc.cc.data.DataNotFoundError(identifier + " not found") entry = _create_value_map_entry(identifier, item_type, @@ -628,7 +628,7 @@ class MultiConfigData: spec_part = spec_part['list_item_spec'] check_type(spec_part, value) else: - raise isc.cc.data.DataNotFoundError(identifier) + raise isc.cc.data.DataNotFoundError(identifier + " not found") # Since we do not support list diffs (yet?), we need to # copy the currently set list of items to _local_changes @@ -638,12 +638,26 @@ class MultiConfigData: cur_id_part = '/' for id_part in id_parts: id, list_indices = isc.cc.data.split_identifier_list_indices(id_part) + cur_value, status = self.get_value(cur_id_part + id) + # Check if the value was there in the first place + if status == MultiConfigData.NONE and cur_id_part != "/": + raise isc.cc.data.DataNotFoundError(id_part + + " not found in " + + cur_id_part) if list_indices is not None: - cur_list, status = self.get_value(cur_id_part + id) + # And check if we don't set something outside of any + # list + cur_list = cur_value + for list_index in list_indices: + if list_index >= len(cur_list): + raise isc.cc.data.DataNotFoundError("No item " + + str(list_index) + " in " + id_part) + else: + cur_list = cur_list[list_index] if status != MultiConfigData.LOCAL: isc.cc.data.set(self._local_changes, cur_id_part + id, - cur_list) + cur_value) cur_id_part = cur_id_part + id_part + "/" isc.cc.data.set(self._local_changes, identifier, value) diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py index 9bed216434..62936be786 100644 --- a/src/lib/python/isc/config/tests/ccsession_test.py +++ b/src/lib/python/isc/config/tests/ccsession_test.py @@ -750,15 +750,16 @@ class TestUIModuleCCSession(unittest.TestCase): uccs.add_value("/Spec32/named_map_item", "foo") value, status = uccs.get_value("/Spec32/named_map_item") self.assertEqual({'a': 1, 'b': 2, 'foo': 3}, value) - uccs.set_value("/Spec32/named_map_item/bar", 4) - value, status = uccs.get_value("/Spec32/named_map_item") - self.assertEqual({'a': 1, 'b': 2, 'foo': 3, 'bar': 4}, value) uccs.remove_value("/Spec32/named_map_item", "a") uccs.remove_value("/Spec32/named_map_item", "foo") value, status = uccs.get_value("/Spec32/named_map_item") - self.assertEqual({'b': 2, 'bar': 4}, value) + self.assertEqual({'b': 2}, value) + self.assertRaises(isc.cc.data.DataNotFoundError, + uccs.set_value, + "/Spec32/named_map_item/no_such_item", + 4) self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "/Spec32/named_map_item", "no_such_item") From 1d03e4212cffa7fcf57d0f3a4fcdc1920c959e40 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 11 Jul 2011 13:46:54 +0200 Subject: [PATCH 180/974] [trac772] Comment cleanup --- src/bin/xfrout/xfrout.py.in | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index a75ff22245..d949986ef3 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -93,12 +93,9 @@ def get_rrset_len(rrset): class XfroutSession(): def __init__(self, sock_fd, request_data, server, tsig_key_ring): - # The initializer for the superclass may call functions - # that need _log to be set, so we set it first self._sock_fd = sock_fd self._request_data = request_data self._server = server - #self._log = log self._tsig_key_ring = tsig_key_ring self._tsig_ctx = None self._tsig_len = 0 @@ -141,6 +138,8 @@ class XfroutSession(): # TSIG related checks rcode = self._check_request_tsig(msg, mdata) + # TODO The ACL check comes here + except Exception as err: logger.error(XFROUT_PARSE_QUERY_ERROR, str(err)) return Rcode.FORMERR(), None @@ -563,7 +562,6 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): class XfroutServer: def __init__(self): self._unix_socket_server = None - #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, None, True) @@ -601,9 +599,6 @@ class XfroutServer: continue self._config_data[key] = new_config[key] - #if self._log: - # self._log.update_config(new_config) - if self._unix_socket_server: self._unix_socket_server.update_config_data(self._config_data) From 1c1bd99f0add79535b62f6723d7e942661007653 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 11 Jul 2011 13:57:24 +0200 Subject: [PATCH 181/974] [trac772] Add ACL to spec file --- src/bin/xfrout/xfrout.spec.pre.in | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in index 2efa3d7d29..5fad7fd061 100644 --- a/src/bin/xfrout/xfrout.spec.pre.in +++ b/src/bin/xfrout/xfrout.spec.pre.in @@ -16,27 +16,27 @@ }, { "item_name": "log_file", - "item_type": "string", + "item_type": "string", "item_optional": false, "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/log/Xfrout.log" }, { "item_name": "log_severity", - "item_type": "string", + "item_type": "string", "item_optional": false, - "item_default": "debug" + "item_default": "debug" }, { "item_name": "log_versions", - "item_type": "integer", + "item_type": "integer", "item_optional": false, - "item_default": 5 + "item_default": 5 }, { "item_name": "log_max_bytes", - "item_type": "integer", + "item_type": "integer", "item_optional": false, - "item_default": 1048576 + "item_default": 1048576 }, { "item_name": "tsig_key_ring", @@ -49,6 +49,18 @@ "item_type": "string", "item_optional": true } + }, + { + "item_name": "ACL", + "item_type": "list", + "item_optional": false, + "item_default": [], + "list_item_spec": + { + "item_name": "ACL_element", + "item_type": "any", + "item_optional": true + } } ], "commands": [ From 93145c09728dbfb7fe5bd77b5a3671e911c41deb Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 11 Jul 2011 14:39:50 +0200 Subject: [PATCH 182/974] [trac772] Default ACL --- src/bin/xfrout/tests/xfrout_test.py.in | 15 ++++++++++++--- src/bin/xfrout/xfrout.py.in | 11 +++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index adabf48ebf..d959a96ed0 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -23,6 +23,7 @@ from isc.cc.session import * from pydnspp import * from xfrout import * import xfrout +import isc.acl.dns TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==") @@ -515,11 +516,10 @@ class MyCCSession(): class MyUnixSockServer(UnixSockServer): def __init__(self): - self._lock = threading.Lock() - self._transfers_counter = 0 self._shutdown_event = threading.Event() self._max_transfers_out = 10 self._cc = MyCCSession() + self._common_init() #self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False ) class TestUnixSockServer(unittest.TestCase): @@ -535,15 +535,24 @@ class TestUnixSockServer(unittest.TestCase): recv_msg = self.unix._receive_query_message(self.read_sock) self.assertEqual(recv_msg, send_msg) + def check_default_ACL(self): + context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1", + 1234, 0, 0, 0, + socket.AI_NUMERICHOST)[0][4]) + self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context)) + def test_updata_config_data(self): + self.check_default_ACL() tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g==' tsig_key_list = [tsig_key_str] bad_key_list = ['bad..example.com:SFuWd/q99SzF8Yzd1QbB9g=='] self.unix.update_config_data({'transfers_out':10 }) self.assertEqual(self.unix._max_transfers_out, 10) self.assertTrue(self.unix.tsig_key_ring is not None) + self.check_default_ACL() - self.unix.update_config_data({'transfers_out':9, 'tsig_key_ring':tsig_key_list}) + self.unix.update_config_data({'transfers_out':9, + 'tsig_key_ring':tsig_key_list}) self.assertEqual(self.unix._max_transfers_out, 9) self.assertEqual(self.unix.tsig_key_ring.size(), 1) self.unix.tsig_key_ring.remove(Name("example.com.")) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index d949986ef3..fecaef75dd 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -48,6 +48,9 @@ except ImportError as e: # must keep running, so we warn about it and move forward. log.error(XFROUT_IMPORT, str(e)) +from isc.acl.acl import ACCEPT, REJECT, DROP +from isc.acl.dns import REQUEST_LOADER + isc.util.process.rename() def init_paths(): @@ -374,14 +377,18 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): self._sock_file = sock_file socketserver_mixin.NoPollMixIn.__init__(self) ThreadingUnixStreamServer.__init__(self, sock_file, handle_class) - self._lock = threading.Lock() - self._transfers_counter = 0 self._shutdown_event = shutdown_event self._write_sock, self._read_sock = socket.socketpair() + self._common_init() #self._log = log self.update_config_data(config_data) self._cc = cc + def _common_init(self): + self._lock = threading.Lock() + self._transfers_counter = 0 + self._acl = REQUEST_LOADER.load("[]") + def _receive_query_message(self, sock): ''' receive request message from sock''' # receive data length From b0e38303e79e2a487e37a9dcadd5f1730cdeae9e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 11 Jul 2011 14:55:26 +0200 Subject: [PATCH 183/974] [trac772] Loading of ACL from configuration --- src/bin/xfrout/tests/xfrout_test.py.in | 20 ++++++++++++++++++++ src/bin/xfrout/xfrout.py.in | 7 ++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index d959a96ed0..bb2136cd40 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -541,6 +541,16 @@ class TestUnixSockServer(unittest.TestCase): socket.AI_NUMERICHOST)[0][4]) self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context)) + def check_loaded_ACL(self): + context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1", + 1234, 0, 0, 0, + socket.AI_NUMERICHOST)[0][4]) + self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context)) + context = isc.acl.dns.RequestContext(socket.getaddrinfo("192.0.2.1", + 1234, 0, 0, 0, + socket.AI_NUMERICHOST)[0][4]) + self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context)) + def test_updata_config_data(self): self.check_default_ACL() tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g==' @@ -563,6 +573,16 @@ class TestUnixSockServer(unittest.TestCase): self.assertRaises(None, self.unix.update_config_data(config_data)) self.assertEqual(self.unix.tsig_key_ring.size(), 0) + # Load the ACL + self.unix.update_config_data({'ACL': [{'from': '127.0.0.1', + 'action': 'ACCEPT'}]}) + self.check_loaded_ACL() + # Pass a wrong data there and check it does not replace the old one + self.assertRaises(isc.acl.acl.LoaderError, + self.unix.update_config_data, + {'ACL': ['Something bad']}) + self.check_loaded_ACL() + def test_get_db_file(self): self.assertEqual(self.unix.get_db_file(), "initdb.file") diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index fecaef75dd..541291a264 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -517,6 +517,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): def update_config_data(self, new_config): '''Apply the new config setting of xfrout module. ''' + if 'ACL' in new_config: + self._acl = REQUEST_LOADER.load(new_config['ACL']) logger.info(XFROUT_NEW_CONFIG) self._lock.acquire() self._max_transfers_out = new_config.get('transfers_out') @@ -607,7 +609,10 @@ class XfroutServer: self._config_data[key] = new_config[key] if self._unix_socket_server: - self._unix_socket_server.update_config_data(self._config_data) + try: + self._unix_socket_server.update_config_data(self._config_data) + except Exception as e: + answer = create_answer(1, "Bad configuration: " + str(e)) return answer From ed9c17ed1627872d701c76336aff407d3ad5c44e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 13 Jul 2011 11:55:04 +0200 Subject: [PATCH 184/974] [trac772] Propagate the remote endpoint to XfrOut session --- src/bin/xfrout/tests/xfrout_test.py.in | 19 +++++++++++++++++-- src/bin/xfrout/xfrout.py.in | 23 +++++++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index bb2136cd40..c361777c94 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -117,8 +117,8 @@ class TestXfroutSession(unittest.TestCase): def setUp(self): self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM) - #self.log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False ) - self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), TSIGKeyRing()) + self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), + TSIGKeyRing(), ('127.0.0.1', 12345)) self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01') self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200') @@ -527,6 +527,21 @@ class TestUnixSockServer(unittest.TestCase): self.write_sock, self.read_sock = socket.socketpair() self.unix = MyUnixSockServer() + def test_guess_remote(self): + """Test we can guess the remote endpoint when we have only the + file descriptor. This is needed, because we get only that one + from auth.""" + # We test with UDP, as it can be "connected" without other + # endpoint + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.connect(('127.0.0.1', 12345)) + self.assertEqual(('127.0.0.1', 12345), + self.unix._guess_remote(sock.fileno())) + sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + sock.connect(('::1', 12345)) + self.assertEqual(('::1', 12345, 0, 0), + self.unix._guess_remote(sock.fileno())) + def test_receive_query_message(self): send_msg = b"\xd6=\x00\x00\x00\x01\x00" msg_len = struct.pack('H', socket.htons(len(send_msg))) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 541291a264..6913925f0d 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -95,13 +95,14 @@ def get_rrset_len(rrset): class XfroutSession(): - def __init__(self, sock_fd, request_data, server, tsig_key_ring): + def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote): self._sock_fd = sock_fd self._request_data = request_data self._server = server self._tsig_key_ring = tsig_key_ring self._tsig_ctx = None self._tsig_len = 0 + self._remote = remote self.handle() def create_tsig_ctx(self, tsig_record, tsig_key_ring): @@ -471,10 +472,28 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): t.daemon = True t.start() + def _guess_remote(self, sock_fd): + """ + Guess remote address and port of the socket. The socket must be a + socket + """ + # This uses a trick. If the socket is IPv4 in reality and we pretend + # it to to be IPv6, it returns IPv4 address anyway. This doesn't seem + # to care about the SOCK_STREAM parameter at all (which it really is, + # except for testing) + if socket.has_ipv6: + sock = socket.fromfd(sock_fd, socket.AF_INET6, socket.SOCK_STREAM) + else: + # To make it work even on hosts without IPv6 support + # (Any idea how to simulate this in test?) + sock = socket.fromfd(sock_fd, socket.AF_INET, socket.SOCK_STREAM) + return sock.getpeername() def finish_request(self, sock_fd, request_data): '''Finish one request by instantiating RequestHandlerClass.''' - self.RequestHandlerClass(sock_fd, request_data, self, self.tsig_key_ring) + self.RequestHandlerClass(sock_fd, request_data, self, + self.tsig_key_ring, + self._guess_remote(sock_fd)) def _remove_unused_sock_file(self, sock_file): '''Try to remove the socket file. If the file is being used From 49f1d2d2e7f75432465ddd4acae2579c018aab33 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 13 Jul 2011 12:08:54 +0200 Subject: [PATCH 185/974] [trac772] Propagate the ACL as well --- src/bin/xfrout/tests/xfrout_test.py.in | 5 ++++- src/bin/xfrout/xfrout.py.in | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index c361777c94..df374492d2 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -118,7 +118,10 @@ class TestXfroutSession(unittest.TestCase): def setUp(self): self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM) self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), - TSIGKeyRing(), ('127.0.0.1', 12345)) + TSIGKeyRing(), ('127.0.0.1', 12345), + # When not testing ACLs, simply accept + isc.acl.dns.REQUEST_LOADER.load( + [{"action": "ACCEPT"}])) self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01') self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200') diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 6913925f0d..8461d59eee 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -95,7 +95,8 @@ def get_rrset_len(rrset): class XfroutSession(): - def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote): + def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote, + acl): self._sock_fd = sock_fd self._request_data = request_data self._server = server @@ -103,6 +104,7 @@ class XfroutSession(): self._tsig_ctx = None self._tsig_len = 0 self._remote = remote + self._acl = acl self.handle() def create_tsig_ctx(self, tsig_record, tsig_key_ring): @@ -493,7 +495,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): '''Finish one request by instantiating RequestHandlerClass.''' self.RequestHandlerClass(sock_fd, request_data, self, self.tsig_key_ring, - self._guess_remote(sock_fd)) + self._guess_remote(sock_fd), self._acl) def _remove_unused_sock_file(self, sock_file): '''Try to remove the socket file. If the file is being used From e77575c3c85c7e219137b2c616ad104e5b28eb20 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 13 Jul 2011 12:49:48 +0200 Subject: [PATCH 186/974] [trac772] Perform the ACL check --- src/bin/xfrout/tests/xfrout_test.py.in | 23 +++++++++++++++++++++++ src/bin/xfrout/xfrout.py.in | 8 +++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index df374492d2..b20e090629 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -141,6 +141,29 @@ class TestXfroutSession(unittest.TestCase): self.assertEqual(rcode.to_text(), "NOERROR") self.assertTrue(self.xfrsess._tsig_ctx is not None) + # ACL checks, put some ACL inside + self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + { + "from": "127.0.0.1", + "action": "ACCEPT" + }, + { + "from": "192.0.2.1", + "action": "DROP" + } + ]) + # Localhost (the default in this test) is accepted + rcode, msg = self.xfrsess._parse_query_message(self.mdata) + self.assertEqual(rcode.to_text(), "NOERROR") + # This should be dropped completely, therefore returning None + self.xfrsess._remote = ('192.0.2.1', 12345) + rcode, msg = self.xfrsess._parse_query_message(self.mdata) + self.assertTrue(rcode is None) + # This should be rejected, therefore NOTAUTH + self.xfrsess._remote = ('192.0.2.2', 12345) + rcode, msg = self.xfrsess._parse_query_message(self.mdata) + self.assertEqual(rcode.to_text(), "REFUSED") + def test_get_query_zone_name(self): msg = self.getmsg() self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.") diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 8461d59eee..e39e2568df 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -144,7 +144,13 @@ class XfroutSession(): # TSIG related checks rcode = self._check_request_tsig(msg, mdata) - # TODO The ACL check comes here + # ACL checks + acl_result = self._acl.execute( + isc.acl.dns.RequestContext(self._remote)) + if acl_result == isc.acl.acl.DROP: + return None, None + elif acl_result == isc.acl.acl.REJECT: + return Rcode.REFUSED(), msg except Exception as err: logger.error(XFROUT_PARSE_QUERY_ERROR, str(err)) From 6d784213ea929dfa06099d7d85ed87709a7f408e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 13 Jul 2011 13:06:30 +0200 Subject: [PATCH 187/974] [trac772] Send the response based on ACL --- src/bin/xfrout/xfrout.py.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index e39e2568df..15893a580c 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -255,7 +255,9 @@ class XfroutSession(): def dns_xfrout_start(self, sock_fd, msg_query): rcode_, msg = self._parse_query_message(msg_query) #TODO. create query message and parse header - if rcode_ == Rcode.NOTAUTH(): + if rcode_ is None: # Dropped by ACL + return + elif rcode_ == Rcode.NOTAUTH() or rcode_ == Rcode.REFUSED(): return self._reply_query_with_error_rcode(msg, sock_fd, rcode_) elif rcode_ != Rcode.NOERROR(): return self._reply_query_with_format_error(msg, sock_fd) @@ -268,7 +270,7 @@ class XfroutSession(): if rcode_ != Rcode.NOERROR(): logger.info(XFROUT_AXFR_TRANSFER_FAILED, zone_name, zone_class_str, rcode_.to_text()) - return self. _reply_query_with_error_rcode(msg, sock_fd, rcode_) + return self._reply_query_with_error_rcode(msg, sock_fd, rcode_) try: logger.info(XFROUT_AXFR_TRANSFER_STARTED, zone_name, zone_class_str) From df79b8d3306394ae123fb4c558f7239146e9f0d6 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 13 Jul 2011 13:07:05 +0200 Subject: [PATCH 188/974] [trac772] Logging --- src/bin/xfrout/xfrout.py.in | 12 ++++++++++-- src/bin/xfrout/xfrout_messages.mes | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 15893a580c..1b71d4756a 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -117,7 +117,7 @@ class XfroutSession(): self.dns_xfrout_start(self._sock_fd, self._request_data) #TODO, avoid catching all exceptions except Exception as e: - logger.error(XFROUT_HANDLE_QUERY_ERROR, str(e)) + logger.error(XFROUT_HANDLE_QUERY_ERROR, e) pass os.close(self._sock_fd) @@ -148,12 +148,20 @@ class XfroutSession(): acl_result = self._acl.execute( isc.acl.dns.RequestContext(self._remote)) if acl_result == isc.acl.acl.DROP: + logger.info(XFROUT_QUERY_DROPPED, + self._get_query_zone_name(msg), + self._get_query_zone_class(msg), + self._remote[0], self._remote[1]) return None, None elif acl_result == isc.acl.acl.REJECT: + logger.info(XFROUT_QUERY_REJECTED, + self._get_query_zone_name(msg), + self._get_query_zone_class(msg), + self._remote[0], self._remote[1]) return Rcode.REFUSED(), msg except Exception as err: - logger.error(XFROUT_PARSE_QUERY_ERROR, str(err)) + logger.error(XFROUT_PARSE_QUERY_ERROR, err) return Rcode.FORMERR(), None return rcode, msg diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes index 2dada5404d..7a234d0137 100644 --- a/src/bin/xfrout/xfrout_messages.mes +++ b/src/bin/xfrout/xfrout_messages.mes @@ -95,6 +95,14 @@ in the log message, but at this point no specific information other than that could be given. This points to incomplete exception handling in the code. +% XFROUT_QUERY_DROPPED request to transfer %1/%2 to %3:%4 dropped +The xfrout process silently dropped a request to transfer zone to given host. +This is required by the ACLs. + +% XFROUT_QUERY_REJECTED request to transfer %1/%2 to %3:%4 rejected +The xfrout process rejected (by REFUSED rcode) a request to transfer zone to +given host. This is because of ACLs. + % XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection There was an error receiving the file descriptor for the transfer request. Normally, the request is received by b10-auth, and passed on From eea48a1e96605accf8579ae4b7fb869295c9ff99 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 13 Jul 2011 14:56:24 +0200 Subject: [PATCH 189/974] [trac1096] change default handle_logging_config to true so that *not* using it must be specified explicitely changed most test cases to not set it (as they are testing other things) --- src/bin/bind10/bind10.py.in | 3 +- src/bin/cmdctl/cmdctl.py.in | 3 +- src/bin/resolver/main.cc | 3 +- src/bin/xfrin/xfrin.py.in | 3 +- src/bin/xfrout/xfrout.py.in | 2 +- src/lib/config/ccsession.h | 4 +-- src/lib/config/tests/ccsession_unittests.cc | 32 +++++++++++-------- src/lib/python/isc/config/ccsession.py | 4 +-- .../python/isc/config/tests/ccsession_test.py | 7 ++-- src/lib/server_common/tests/keyring_test.cc | 3 +- 10 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in index 6e4997dc49..a624383da6 100755 --- a/src/bin/bind10/bind10.py.in +++ b/src/bin/bind10/bind10.py.in @@ -462,8 +462,7 @@ class BoB: self.log_starting("ccsession") self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, - self.command_handler, - None, True) + self.command_handler) self.ccs.start() self.log_started() diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in index 778d38f368..2f89894066 100755 --- a/src/bin/cmdctl/cmdctl.py.in +++ b/src/bin/cmdctl/cmdctl.py.in @@ -252,8 +252,7 @@ class CommandControl(): self._cc = isc.cc.Session() self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, - self.command_handler, - None, True) + self.command_handler) self._module_name = self._module_cc.get_module_spec().get_module_name() self._cmdctl_config_data = self._module_cc.get_full_config() self._module_cc.start() diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc index d9c30b9495..79146da928 100644 --- a/src/bin/resolver/main.cc +++ b/src/bin/resolver/main.cc @@ -208,8 +208,7 @@ 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, - true, true); + my_command_handler); LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIG_CHANNEL); // FIXME: This does not belong here, but inside Boss diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in index d1fbbfef0e..07de8f0c9e 100755 --- a/src/bin/xfrin/xfrin.py.in +++ b/src/bin/xfrin/xfrin.py.in @@ -548,8 +548,7 @@ class Xfrin: self._send_cc_session = isc.cc.Session() self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, - self.command_handler, - None, True) + self.command_handler) self._module_cc.start() config_data = self._module_cc.get_full_config() self.config_handler(config_data) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index a75ff22245..b44b099386 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -566,7 +566,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, None, True) + self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler) 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.h b/src/lib/config/ccsession.h index 7dc34ba441..a39d996a3b 100644 --- a/src/lib/config/ccsession.h +++ b/src/lib/config/ccsession.h @@ -179,7 +179,7 @@ public: * 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. + * module. Defaults to true. */ ModuleCCSession(const std::string& spec_file_name, isc::cc::AbstractSession& session, @@ -189,7 +189,7 @@ public: const std::string& command, isc::data::ConstElementPtr args) = NULL, bool start_immediately = true, - bool handle_logging = false + bool handle_logging = true ); /// Start receiving new commands and configuration changes asynchronously. diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc index e1a4f9da92..283fcc48fc 100644 --- a/src/lib/config/tests/ccsession_unittests.cc +++ b/src/lib/config/tests/ccsession_unittests.cc @@ -151,7 +151,8 @@ TEST_F(CCSessionTest, parseCommand) { TEST_F(CCSessionTest, session1) { EXPECT_FALSE(session.haveSubscription("Spec1", "*")); - ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL); + ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, + true, false); EXPECT_TRUE(session.haveSubscription("Spec1", "*")); EXPECT_EQ(1, session.getMsgQueue()->size()); @@ -163,14 +164,15 @@ TEST_F(CCSessionTest, session1) { EXPECT_EQ("*", to); EXPECT_EQ(0, session.getMsgQueue()->size()); - // without explicit argument, the session should not automatically + // with this argument, the session should not automatically // subscribe to logging config EXPECT_FALSE(session.haveSubscription("Logging", "*")); } TEST_F(CCSessionTest, session2) { EXPECT_FALSE(session.haveSubscription("Spec2", "*")); - ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL); + ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL, + true, false); EXPECT_TRUE(session.haveSubscription("Spec2", "*")); EXPECT_EQ(1, session.getMsgQueue()->size()); @@ -217,7 +219,7 @@ TEST_F(CCSessionTest, session3) { EXPECT_FALSE(session.haveSubscription("Spec2", "*")); ModuleCCSession mccs(ccspecfile("spec2.spec"), session, my_config_handler, - my_command_handler); + my_command_handler, true, false); EXPECT_TRUE(session.haveSubscription("Spec2", "*")); EXPECT_EQ(2, session.getMsgQueue()->size()); @@ -241,7 +243,7 @@ TEST_F(CCSessionTest, checkCommand) { EXPECT_FALSE(session.haveSubscription("Spec29", "*")); ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler, - my_command_handler); + my_command_handler, true, false); EXPECT_TRUE(session.haveSubscription("Spec29", "*")); EXPECT_EQ(2, session.getMsgQueue()->size()); @@ -318,7 +320,7 @@ TEST_F(CCSessionTest, checkCommand2) { session.getMessages()->add(createAnswer(0, el("{}"))); EXPECT_FALSE(session.haveSubscription("Spec29", "*")); ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler, - my_command_handler); + my_command_handler, true, false); EXPECT_TRUE(session.haveSubscription("Spec29", "*")); ConstElementPtr msg; std::string group, to; @@ -370,7 +372,8 @@ TEST_F(CCSessionTest, remoteConfig) { std::string module_name; int item1; - ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false); + ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, + false, false); EXPECT_TRUE(session.haveSubscription("Spec1", "*")); // first simply connect, with no config values, and see we get @@ -526,7 +529,7 @@ TEST_F(CCSessionTest, ignoreRemoteConfigCommands) { EXPECT_FALSE(session.haveSubscription("Spec29", "*")); ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler, - my_command_handler, false); + my_command_handler, false, false); EXPECT_TRUE(session.haveSubscription("Spec29", "*")); EXPECT_EQ(2, session.getMsgQueue()->size()); @@ -578,14 +581,15 @@ TEST_F(CCSessionTest, initializationFail) { // Test it throws when we try to start it twice (once from the constructor) TEST_F(CCSessionTest, doubleStartImplicit) { - ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL); + ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL, + true, false); EXPECT_THROW(mccs.start(), CCSessionError); } // The same, but both starts are explicit TEST_F(CCSessionTest, doubleStartExplicit) { ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL, - false); + false, false); mccs.start(); EXPECT_THROW(mccs.start(), CCSessionError); } @@ -593,7 +597,8 @@ TEST_F(CCSessionTest, doubleStartExplicit) { // Test we can request synchronous receive before we start the session, // and check there's the mechanism if we do it after TEST_F(CCSessionTest, delayedStart) { - ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL, false); + ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL, + false, false); session.getMessages()->add(createAnswer()); ConstElementPtr env, answer; EXPECT_NO_THROW(session.group_recvmsg(env, answer, false, 3)); @@ -620,7 +625,7 @@ TEST_F(CCSessionTest, loggingStartBadSpec) { // just give an empty config session.getMessages()->add(createAnswer(0, el("{}"))); EXPECT_THROW(new ModuleCCSession(ccspecfile("spec2.spec"), session, - NULL, NULL, true, true), ModuleSpecError); + NULL, NULL), ModuleSpecError); EXPECT_FALSE(session.haveSubscription("Logging", "*")); } @@ -629,7 +634,8 @@ TEST_F(CCSessionTest, loggingStartBadSpec) { // if we need to call addRemoteConfig(). // The correct cases are covered in remoteConfig test. TEST_F(CCSessionTest, doubleStartWithAddRemoteConfig) { - ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL); + ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL, + true, false); session.getMessages()->add(createAnswer(0, el("{}"))); EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), FakeSession::DoubleRead); diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index 8bf7d33ad0..06a7f0f7c8 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -142,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, handle_logging_config=False): + def __init__(self, spec_file_name, config_handler, command_handler, cc_session=None, handle_logging_config=True): """Initialize a ModuleCCSession. This does *NOT* send the specification and request the configuration yet. Use start() for that once the ModuleCCSession has been initialized. @@ -163,7 +163,7 @@ class ModuleCCSession(ConfigData): the logger manager to apply it. It will also inform the logger manager when the logging configuration gets updated. The module does not need to do anything except intializing - its loggers, and provide log messages + its loggers, and provide log messages. Defaults to true. """ module_spec = isc.config.module_spec_from_file(spec_file_name) ConfigData.__init__(self, module_spec) diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py index 5d09c968c4..ada0c8a7f1 100644 --- a/src/lib/python/isc/config/tests/ccsession_test.py +++ b/src/lib/python/isc/config/tests/ccsession_test.py @@ -108,8 +108,11 @@ class TestModuleCCSession(unittest.TestCase): def spec_file(self, file): return self.data_path + os.sep + file - def create_session(self, spec_file_name, config_handler = None, command_handler = None, cc_session = None): - return ModuleCCSession(self.spec_file(spec_file_name), config_handler, command_handler, cc_session) + def create_session(self, spec_file_name, config_handler = None, + command_handler = None, cc_session = None): + return ModuleCCSession(self.spec_file(spec_file_name), + config_handler, command_handler, + cc_session, False) def test_init(self): fake_session = FakeModuleCCSession() diff --git a/src/lib/server_common/tests/keyring_test.cc b/src/lib/server_common/tests/keyring_test.cc index d79b541f97..dab43df7bc 100644 --- a/src/lib/server_common/tests/keyring_test.cc +++ b/src/lib/server_common/tests/keyring_test.cc @@ -38,7 +38,8 @@ public: specfile(std::string(TEST_DATA_PATH) + "/spec.spec") { session.getMessages()->add(createAnswer()); - mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL, false)); + mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL, + false, false)); } isc::cc::FakeSession session; std::auto_ptr mccs; From e7cf8992bed2ef0be2843da6f0eedf9fa6d5f66b Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 13 Jul 2011 14:33:14 +0100 Subject: [PATCH 190/974] [trac1075] Modifications after review --- src/lib/datasrc/datasrc_messages.mes | 22 +++++++++++----------- src/lib/datasrc/memory_datasrc.cc | 2 +- src/lib/datasrc/sqlite3_datasrc.cc | 3 ++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 59ce02b6d5..366cb1352f 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -23,13 +23,13 @@ is created. % DATASRC_CACHE_DESTROY destroying the hotspot cache Debug information. The hotspot cache is being destroyed. -% DATASRC_CACHE_DISABLE disabling the cache +% DATASRC_CACHE_DISABLE disabling the hotspot cache A debug message issued when the hotspot cache is disabled. -% DATASRC_CACHE_ENABLE enabling the cache +% DATASRC_CACHE_ENABLE enabling the hotspot cache A debug message issued when the hotspot cache is enabled. -% DATASRC_CACHE_EXPIRED the item '%1' is expired +% DATASRC_CACHE_EXPIRED item '%1' in the hotspot cache has expired A debug message issued when a hotspot cache lookup located the item but it had expired. The item was removed and the program proceeded as if the item had not been found. @@ -37,28 +37,28 @@ had not been found. % DATASRC_CACHE_FOUND the item '%1' was found Debug information. An item was successfully located in the hotspot cache. -% DATASRC_CACHE_FULL cache is full, dropping oldest +% DATASRC_CACHE_FULL hotspot cache is full, dropping oldest Debug information. After inserting an item into the hotspot cache, the maximum number of items was exceeded, so the least recently used item will be dropped. This should be directly followed by CACHE_REMOVE. -% DATASRC_CACHE_INSERT inserting item '%1' into the cache +% DATASRC_CACHE_INSERT inserting item '%1' into the hotspot cache A debug message indicating that a new item is being inserted into the hotspot cache. -% DATASRC_CACHE_NOT_FOUND the item '%1' was not found +% DATASRC_CACHE_NOT_FOUND the item '%1' was not found in the hotspot cache A debug message issued when hotspot cache was searched for the specified item but it was not found. -% DATASRC_CACHE_OLD_FOUND older instance of cache item '%1' found, replacing +% DATASRC_CACHE_OLD_FOUND older instance of hotspot cache item '%1' found, replacing Debug information. While inserting an item into the hotspot cache, an older instance of an item with the same name was found; the old instance will be removed. This will be directly followed by CACHE_REMOVE. -% DATASRC_CACHE_REMOVE removing '%1' from the cache +% DATASRC_CACHE_REMOVE removing '%1' from the hotspot cache Debug information. An item is being removed from the hotspot cache. -% DATASRC_CACHE_SLOTS setting the cache size to '%1', dropping '%2' items +% DATASRC_CACHE_SLOTS setting the hotspot cache size to '%1', dropping '%2' items The maximum allowed number of items of the hotspot cache is set to the given number. If there are too many, some of them will be dropped. The size of 0 means no limit. @@ -142,7 +142,7 @@ in-memory data source. % DATASRC_MEM_LOAD loading zone '%1' from file '%2' Debug information. The content of master file is being loaded into the memory. -% DATASRC_MEM_NOTFOUND requested domain '%1' not found +% DATASRC_MEM_NOT_FOUND requested domain '%1' not found Debug information. The requested domain does not exist. % DATASRC_MEM_NS_ENCOUNTERED encountered a NS @@ -410,7 +410,7 @@ Debug information. An instance of SQLite data source is being destroyed. Debug information. The SQLite data source is trying to identify which zone should hold this domain. -% DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it +% DATASRC_SQLITE_ENCLOSURE_NOT_FOUND no zone contains '%1' Debug information. The last SQLITE_ENCLOSURE query was unsuccessful; there's no such zone in our data. diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 1fd4eafb8b..65650004a0 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -523,7 +523,7 @@ struct MemoryZone::MemoryZoneImpl { // fall through case DomainTree::NOTFOUND: - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOTFOUND). + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND). arg(name); return (FindResult(NXDOMAIN, ConstRRsetPtr())); case DomainTree::EXACTMATCH: // This one is OK, handle it diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc index 13d98ed0b3..18ee929593 100644 --- a/src/lib/datasrc/sqlite3_datasrc.cc +++ b/src/lib/datasrc/sqlite3_datasrc.cc @@ -356,7 +356,8 @@ Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const { unsigned int position; if (findClosest(match.getName(), &position) == -1) { - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOTFOUND); + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOT_FOUND) + .arg(match.getName()); return; } From aac974498b0a9513f3caf341e1eecbe4adbcff0a Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 13 Jul 2011 14:36:41 +0100 Subject: [PATCH 191/974] [trac1075] Add missing "hotspot" adjective to some messages --- src/lib/datasrc/datasrc_messages.mes | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 366cb1352f..3dc69e070d 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -234,11 +234,11 @@ specific error already. The domain lives in another zone. But it is not possible to generate referral information for it. -% DATASRC_QUERY_CACHED data for %1/%2 found in cache +% DATASRC_QUERY_CACHED data for %1/%2 found in hotspot cache Debug information. The requested data were found in the hotspot cache, so no query is sent to the real data source. -% DATASRC_QUERY_CHECK_CACHE checking cache for '%1/%2' +% DATASRC_QUERY_CHECK_CACHE checking hotspot cache for '%1/%2' Debug information. While processing a query, lookup to the hotspot cache is being made. @@ -321,11 +321,11 @@ The underlying data source failed to answer the no-glue query. 1 means some error, 2 is not implemented. The data source should have logged the specific error already. -% DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class) +% DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring hotspot cache for ANY query (%1/%2 in %3 class) Debug information. The hotspot cache is ignored for authoritative ANY queries for consistency reasons. -% DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class) +% DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring hotspot cache for ANY query (%1/%2 in %3 class) Debug information. The hotspot cache is ignored for ANY queries for consistency reasons. From 71fb105407d496134f0cfcbea73eaea9991dbcf5 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 13 Jul 2011 16:00:40 +0200 Subject: [PATCH 192/974] [trac926] tab-completion for named_map entries --- src/lib/python/isc/config/config_data.py | 37 +++++++++++++++++-- .../isc/config/tests/config_data_test.py | 18 +++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py index 3e6f4040fe..51aed576d5 100644 --- a/src/lib/python/isc/config/config_data.py +++ b/src/lib/python/isc/config/config_data.py @@ -193,11 +193,15 @@ def spec_name_list(spec, prefix="", recurse=False): result.extend(spec_name_list(map_el['map_item_spec'], prefix + map_el['item_name'], recurse)) else: result.append(prefix + name) + elif 'named_map_item_spec' in spec: + # we added a '/' above, but in this one case we don't want it + result.append(prefix[:-1]) + pass else: for name in spec: result.append(prefix + name + "/") if recurse: - result.extend(spec_name_list(spec[name],name, recurse)) + result.extend(spec_name_list(spec[name], name, recurse)) elif type(spec) == list: for list_el in spec: if 'item_name' in list_el: @@ -209,7 +213,7 @@ def spec_name_list(spec, prefix="", recurse=False): else: raise ConfigDataError("Bad specification") else: - raise ConfigDataError("Bad specication") + raise ConfigDataError("Bad specification") return result class ConfigData: @@ -660,7 +664,28 @@ class MultiConfigData: cur_value) cur_id_part = cur_id_part + id_part + "/" isc.cc.data.set(self._local_changes, identifier, value) - + + def _get_list_items(self, item_name): + """This method is used in get_config_item_list, to add list + indices and named_map names to the completion list. If + the given item_name is for a list or named_map, it'll + return a list of those (appended to item_name), otherwise + the list will only contain the item_name itself.""" + spec_part = self.find_spec_part(item_name) + if 'item_type' in spec_part and \ + spec_part['item_type'] == 'named_map': + subslash = "" + if spec_part['named_map_item_spec']['item_type'] == 'map' or\ + spec_part['named_map_item_spec']['item_type'] == 'named_map': + subslash = "/" + values, status = self.get_value(item_name) + if len(values) > 0: + return [ item_name + "/" + v + subslash for v in values.keys() ] + else: + return [ item_name ] + else: + return [ item_name ] + def get_config_item_list(self, identifier = None, recurse = False): """Returns a list of strings containing the item_names of the child items at the given identifier. If no identifier is @@ -671,7 +696,11 @@ class MultiConfigData: if identifier.startswith("/"): identifier = identifier[1:] spec = self.find_spec_part(identifier) - return spec_name_list(spec, identifier + "/", recurse) + spec_list = spec_name_list(spec, identifier + "/", recurse) + result_list = [] + for spec_name in spec_list: + result_list.extend(self._get_list_items(spec_name)) + return result_list else: if recurse: id_list = [] diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py index e9136bf008..102bc528b8 100644 --- a/src/lib/python/isc/config/tests/config_data_test.py +++ b/src/lib/python/isc/config/tests/config_data_test.py @@ -618,6 +618,24 @@ class TestMultiConfigData(unittest.TestCase): config_items = self.mcd.get_config_item_list("Spec2", True) self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items) + def test_get_config_item_list_named_map(self): + config_items = self.mcd.get_config_item_list() + self.assertEqual([], config_items) + module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec") + self.mcd.set_specification(module_spec) + config_items = self.mcd.get_config_item_list() + self.assertEqual(['Spec32'], config_items) + config_items = self.mcd.get_config_item_list(None, False) + self.assertEqual(['Spec32'], config_items) + config_items = self.mcd.get_config_item_list(None, True) + self.assertEqual(['Spec32/named_map_item'], config_items) + self.mcd.set_value('Spec32/named_map_item', { "aaaa": 4, "aabb": 5, "bbbb": 6}) + config_items = self.mcd.get_config_item_list("/Spec32/named_map_item", True) + self.assertEqual(['Spec32/named_map_item/aaaa', + 'Spec32/named_map_item/aabb', + 'Spec32/named_map_item/bbbb', + ], config_items) + if __name__ == '__main__': unittest.main() From e540aaf2cedae6cfeb4c0ea063f8693cf5999822 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 13 Jul 2011 17:40:00 +0100 Subject: [PATCH 193/974] [trac1024] Correctly initialize logging for asiodns unit tests Removes (by default) one of the remaining logging messages identified in the review. --- src/lib/asiodns/tests/run_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/asiodns/tests/run_unittests.cc b/src/lib/asiodns/tests/run_unittests.cc index df77368f43..0a80331343 100644 --- a/src/lib/asiodns/tests/run_unittests.cc +++ b/src/lib/asiodns/tests/run_unittests.cc @@ -22,7 +22,7 @@ int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); // Initialize Google test - isc::log::LoggerManager::init("unittest"); // Set a root logger name + isc::log::initLogger(); // Initialize logging isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data return (isc::util::unittests::run_all()); From 8e66cc336b29bd5acc1f764f26cb0b116db4dc87 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 13 Jul 2011 18:22:53 +0100 Subject: [PATCH 194/974] [trac1024] Suppress logging messages in logging library unit tests Add a reset logging function to specific tests to ensure that the logging is routed to the stream selected by the B10 environment variables prior to each test that generates messages. --- src/lib/asiodns/tests/run_unittests.cc | 2 +- src/lib/log/Makefile.am | 1 + src/lib/log/logger_support.cc | 153 +-------------- src/lib/log/logger_support.h | 47 +---- src/lib/log/logger_unittest_support.cc | 175 ++++++++++++++++++ src/lib/log/logger_unittest_support.h | 121 ++++++++++++ .../log/tests/logger_level_impl_unittest.cc | 7 +- src/lib/log/tests/logger_level_unittest.cc | 8 +- src/lib/log/tests/logger_support_unittest.cc | 15 +- 9 files changed, 325 insertions(+), 204 deletions(-) create mode 100644 src/lib/log/logger_unittest_support.cc create mode 100644 src/lib/log/logger_unittest_support.h diff --git a/src/lib/asiodns/tests/run_unittests.cc b/src/lib/asiodns/tests/run_unittests.cc index 0a80331343..5cacdaf1c1 100644 --- a/src/lib/asiodns/tests/run_unittests.cc +++ b/src/lib/asiodns/tests/run_unittests.cc @@ -15,7 +15,7 @@ #include #include -#include +#include #include int diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am index 63b1dfbb70..9f5272469c 100644 --- a/src/lib/log/Makefile.am +++ b/src/lib/log/Makefile.am @@ -20,6 +20,7 @@ liblog_la_SOURCES += logger_manager_impl.cc logger_manager_impl.h liblog_la_SOURCES += logger_name.cc logger_name.h liblog_la_SOURCES += logger_specification.h liblog_la_SOURCES += logger_support.cc logger_support.h +liblog_la_SOURCES += logger_unittest_support.cc logger_unittest_support.h liblog_la_SOURCES += macros.h liblog_la_SOURCES += log_messages.cc log_messages.h liblog_la_SOURCES += message_dictionary.cc message_dictionary.h diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc index 36798cc9b6..2097136228 100644 --- a/src/lib/log/logger_support.cc +++ b/src/lib/log/logger_support.cc @@ -12,28 +12,9 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE -/// \brief Temporary Logger Support -/// -/// Performs run-time initialization of the logger system. In particular, it -/// is passed information from the command line and: -/// -/// a) Sets the severity of the messages being logged (and debug level if -/// appropriate). -/// b) Reads in the local message file is one has been supplied. -/// -/// These functions will be replaced once the code has been written to obtain -/// the logging parameters from the configuration database. - -#include -#include -#include #include - -#include -#include -#include #include -#include +#include using namespace std; @@ -42,80 +23,6 @@ namespace { // Flag to hold logging initialization state. bool logging_init_state = false; - -// Set logging destination according to the setting of B10_LOGGER_DESTINATION. -// (See header for initLogger() for more details.) This is a no-op if the -// environment variable is not defined. -// -// \param root Name of the root logger -// \param severity Severity level to be assigned to the root logger -// \param dbglevel Debug level - -void -setDestination(const char* root, const isc::log::Severity severity, - const int dbglevel) { - - using namespace isc::log; - - // Constants: not declared static as this is function is expected to be - // called once only - const string DEVNULL = "/dev/null"; - const string STDOUT = "stdout"; - const string STDERR = "stderr"; - const string SYSLOG = "syslog"; - const string SYSLOG_COLON = "syslog:"; - - // Get the destination. If not specified, assume /dev/null. (The default - // severity for unit tests is DEBUG, which generates a lot of output. - // Routing the logging to /dev/null will suppress that, whilst still - // ensuring that the code paths are tested.) - const char* destination = getenv("B10_LOGGER_DESTINATION"); - const string dest((destination == NULL) ? DEVNULL : destination); - - // Prepare the objects to define the logging specification - LoggerSpecification spec(root, severity, dbglevel); - OutputOption option; - - // Set up output option according to destination specification - if (dest == STDOUT) { - option.destination = OutputOption::DEST_CONSOLE; - option.stream = OutputOption::STR_STDOUT; - - } else if (dest == STDERR) { - option.destination = OutputOption::DEST_CONSOLE; - option.stream = OutputOption::STR_STDERR; - - } else if (dest == SYSLOG) { - option.destination = OutputOption::DEST_SYSLOG; - // Use default specified in OutputOption constructor for the - // syslog destination - - } else if (dest.find(SYSLOG_COLON) == 0) { - option.destination = OutputOption::DEST_SYSLOG; - // Must take account of the string actually being "syslog:" - if (dest == SYSLOG_COLON) { - cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " << - SYSLOG_COLON << " is invalid, " << SYSLOG << - " will be used instead\n"; - // Use default for logging facility - - } else { - // Everything else in the string is the facility name - option.facility = dest.substr(SYSLOG_COLON.size()); - } - - } else { - // Not a recognised destination, assume a file. - option.destination = OutputOption::DEST_FILE; - option.filename = dest; - } - - // ... and set the destination - spec.addOutputOption(option); - LoggerManager manager; - manager.process(spec); -} - } // Anonymous namespace namespace isc { @@ -143,63 +50,5 @@ initLogger(const string& root, isc::log::Severity severity, int dbglevel, LoggerManager::init(root, severity, dbglevel, file); } -// Logger Run-Time Initialization via Environment Variables -void initLogger(isc::log::Severity severity, int dbglevel) { - - // Root logger name is defined by the environment variable B10_LOGGER_ROOT. - // If not present, the name is "bind10". - const char* DEFAULT_ROOT = "bind10"; - const char* root = getenv("B10_LOGGER_ROOT"); - if (! root) { - root = DEFAULT_ROOT; - } - - // Set the logging severity. The environment variable is - // B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR" - // of "FATAL". Note that the string must be in upper case with no leading - // of trailing blanks. - const char* sev_char = getenv("B10_LOGGER_SEVERITY"); - if (sev_char) { - severity = isc::log::getSeverity(sev_char); - } - - // If the severity is debug, get the debug level (environment variable - // B10_LOGGER_DBGLEVEL), which should be in the range 0 to 99. - if (severity == isc::log::DEBUG) { - const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL"); - if (dbg_char) { - int level = 0; - try { - level = boost::lexical_cast(dbg_char); - if (level < MIN_DEBUG_LEVEL) { - cerr << "**ERROR** debug level of " << level - << " is invalid - a value of " << MIN_DEBUG_LEVEL - << " will be used\n"; - level = MIN_DEBUG_LEVEL; - } else if (level > MAX_DEBUG_LEVEL) { - cerr << "**ERROR** debug level of " << level - << " is invalid - a value of " << MAX_DEBUG_LEVEL - << " will be used\n"; - level = MAX_DEBUG_LEVEL; - } - } catch (...) { - // Error, but not fatal to the test - cerr << "**ERROR** Unable to translate " - "B10_LOGGER_DBGLEVEL - a value of 0 will be used\n"; - } - dbglevel = level; - } - } - - // Set the local message file - const char* localfile = getenv("B10_LOGGER_LOCALMSG"); - - // Initialize logging - initLogger(root, severity, dbglevel, localfile); - - // Now set the destination for logging output - setDestination(root, severity, dbglevel); -} - } // namespace log } // namespace isc diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h index 1027e089cc..1c5f8e617f 100644 --- a/src/lib/log/logger_support.h +++ b/src/lib/log/logger_support.h @@ -20,6 +20,9 @@ #include #include +// Include the unit test function declarations here for compatibility +#include + namespace isc { namespace log { @@ -63,50 +66,6 @@ void initLogger(const std::string& root, int dbglevel = 0, const char* file = NULL); -/// \brief Run-Time Initialization from Environment -/// -/// Performs run-time initialization of the logger via the setting of -/// environment variables. These are: -/// -/// - B10_LOGGER_ROOT\n -/// Name of the root logger. If not given, the string "bind10" will be used. -/// -/// - B10_LOGGER_SEVERITY\n -/// Severity of messages that will be logged. This must be one of the strings -/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case -/// and must not contain leading or trailing spaces.) If not specified (or if -/// specified but incorrect), the default passed as argument to this function -/// (currently DEBUG) will be used. -/// -/// - B10_LOGGER_DBGLEVEL\n -/// Ignored if the level is not DEBUG, this should be a number between 0 and -/// 99 indicating the logging severity. The default is 0. If outside these -/// limits or if not a number, The value passed to this function (default -/// of MAX_DEBUG_LEVEL) is used. -/// -/// - B10_LOGGER_LOCALMSG\n -/// If defined, the path specification of a file that contains message -/// definitions replacing ones in the default dictionary. -/// -/// - B10_LOGGER_DESTINATION\n -/// If defined, the destination of the logging output. This can be one of: -/// - \c stdout Send output to stdout. -/// - \c stderr Send output to stderr -/// - \c syslog Send output to syslog using the facility local0. -/// - \c syslog:xxx Send output to syslog, using the facility xxx. ("xxx" -/// should be one of the syslog facilities such as "local0".) There must -/// be a colon between "syslog" and "xxx -/// - \c other Anything else is interpreted as the name of a file to which -/// output is appended. If the file does not exist, it is created. -/// -/// Any errors in the settings cause messages to be output to stderr. -/// -/// This function is aimed at test programs, allowing the default settings to -/// be overridden by the tester. It is not intended for use in production -/// code. - -void initLogger(isc::log::Severity severity = isc::log::DEBUG, - int dbglevel = isc::log::MAX_DEBUG_LEVEL); } // namespace log } // namespace isc diff --git a/src/lib/log/logger_unittest_support.cc b/src/lib/log/logger_unittest_support.cc new file mode 100644 index 0000000000..86d8491c6e --- /dev/null +++ b/src/lib/log/logger_unittest_support.cc @@ -0,0 +1,175 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace isc { +namespace log { + +// Get the logging severity. This is defined by the environment variable +// B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR" +// of "FATAL". (Note that the string must be in upper case with no leading +// of trailing blanks.) If not present, the default severity passed to the +// function is returned. +isc::log::Severity +b10LoggerSeverity(isc::log::Severity defseverity) { + const char* sev_char = getenv("B10_LOGGER_SEVERITY"); + if (sev_char) { + return (isc::log::getSeverity(sev_char)); + } + return (defseverity); +} + +// Get the debug level. This is defined by the envornment variable +// B10_LOGGER_DBGLEVEL. If not defined, a default value passed to the function +// is returned. +int +b10LoggerDbglevel(int defdbglevel) { + const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL"); + if (dbg_char) { + int level = 0; + try { + level = boost::lexical_cast(dbg_char); + if (level < MIN_DEBUG_LEVEL) { + std::cerr << "**ERROR** debug level of " << level + << " is invalid - a value of " << MIN_DEBUG_LEVEL + << " will be used\n"; + level = MIN_DEBUG_LEVEL; + } else if (level > MAX_DEBUG_LEVEL) { + std::cerr << "**ERROR** debug level of " << level + << " is invalid - a value of " << MAX_DEBUG_LEVEL + << " will be used\n"; + level = MAX_DEBUG_LEVEL; + } + } catch (...) { + // Error, but not fatal to the test + std::cerr << "**ERROR** Unable to translate " + "B10_LOGGER_DBGLEVEL - a value of 0 will be used\n"; + } + return (level); + } + + return (defdbglevel); +} + + +// Reset characteristics of the root logger to that set by the environment +// variables B10_LOGGER_SEVERITY, B10_LOGGER_DBGLEVEL and B10_LOGGER_DESTINATION. + +void +setUnitTestRootLoggerCharacteristics() { + + using namespace isc::log; + + // Constants: not declared static as this is function is expected to be + // called once only + const string DEVNULL = "/dev/null"; + const string STDOUT = "stdout"; + const string STDERR = "stderr"; + const string SYSLOG = "syslog"; + const string SYSLOG_COLON = "syslog:"; + + // Get the destination. If not specified, assume /dev/null. (The default + // severity for unit tests is DEBUG, which generates a lot of output. + // Routing the logging to /dev/null will suppress that, whilst still + // ensuring that the code paths are tested.) + const char* destination = getenv("B10_LOGGER_DESTINATION"); + const string dest((destination == NULL) ? DEVNULL : destination); + + // Prepare the objects to define the logging specification + LoggerSpecification spec(getRootLoggerName(), + b10LoggerSeverity(isc::log::DEBUG), + b10LoggerDbglevel(isc::log::MAX_DEBUG_LEVEL)); + OutputOption option; + + // Set up output option according to destination specification + if (dest == STDOUT) { + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDOUT; + + } else if (dest == STDERR) { + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDERR; + + } else if (dest == SYSLOG) { + option.destination = OutputOption::DEST_SYSLOG; + // Use default specified in OutputOption constructor for the + // syslog destination + + } else if (dest.find(SYSLOG_COLON) == 0) { + option.destination = OutputOption::DEST_SYSLOG; + // Must take account of the string actually being "syslog:" + if (dest == SYSLOG_COLON) { + cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " << + SYSLOG_COLON << " is invalid, " << SYSLOG << + " will be used instead\n"; + // Use default for logging facility + + } else { + // Everything else in the string is the facility name + option.facility = dest.substr(SYSLOG_COLON.size()); + } + + } else { + // Not a recognised destination, assume a file. + option.destination = OutputOption::DEST_FILE; + option.filename = dest; + } + + // ... and set the destination + spec.addOutputOption(option); + LoggerManager manager; + manager.process(spec); +} + + +// Logger Run-Time Initialization via Environment Variables +void initLogger(isc::log::Severity severity, int dbglevel) { + + // Root logger name is defined by the environment variable B10_LOGGER_ROOT. + // If not present, the name is "bind10". + const char* DEFAULT_ROOT = "bind10"; + const char* root = getenv("B10_LOGGER_ROOT"); + if (! root) { + root = DEFAULT_ROOT; + } + + // Set the local message file + const char* localfile = getenv("B10_LOGGER_LOCALMSG"); + + // Initialize logging + initLogger(root, isc::log::DEBUG, isc::log::MAX_DEBUG_LEVEL, localfile); + + // Now set reset the output destination of the root logger, overriding + // the default severity, debug level and destination with those specified + // in the environment variables. (The two-step approach is used as the + // setUnitTestRootLoggerCharacteristics() function is used in several + // places in the BIND 10 tests, and it avoid duplicating code.) + setUnitTestRootLoggerCharacteristics(); +} + +} // namespace log +} // namespace isc diff --git a/src/lib/log/logger_unittest_support.h b/src/lib/log/logger_unittest_support.h new file mode 100644 index 0000000000..221566a5ae --- /dev/null +++ b/src/lib/log/logger_unittest_support.h @@ -0,0 +1,121 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __LOGGER_UNITTEST_SUPPORT_H +#define __LOGGER_UNITTEST_SUPPORT_H + +#include +#include + +// Note: this file holds logging functions used by unit tests. + +namespace isc { +namespace log { + +/// \brief Run-Time Initialization for Unit Tests from Environment +/// +/// Performs run-time initialization of the logger via the setting of +/// environment variables. These are: +/// +/// - B10_LOGGER_ROOT\n +/// Name of the root logger. If not given, the string "bind10" will be used. +/// +/// - B10_LOGGER_SEVERITY\n +/// Severity of messages that will be logged. This must be one of the strings +/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case +/// and must not contain leading or trailing spaces.) If not specified (or if +/// specified but incorrect), the default passed as argument to this function +/// (currently DEBUG) will be used. +/// +/// - B10_LOGGER_DBGLEVEL\n +/// Ignored if the level is not DEBUG, this should be a number between 0 and +/// 99 indicating the logging severity. The default is 0. If outside these +/// limits or if not a number, The value passed to this function (default +/// of MAX_DEBUG_LEVEL) is used. +/// +/// - B10_LOGGER_LOCALMSG\n +/// If defined, the path specification of a file that contains message +/// definitions replacing ones in the default dictionary. +/// +/// - B10_LOGGER_DESTINATION\n +/// If defined, the destination of the logging output. This can be one of: +/// - \c stdout Send output to stdout. +/// - \c stderr Send output to stderr +/// - \c syslog Send output to syslog using the facility local0. +/// - \c syslog:xxx Send output to syslog, using the facility xxx. ("xxx" +/// should be one of the syslog facilities such as "local0".) There must +/// be a colon between "syslog" and "xxx +/// - \c other Anything else is interpreted as the name of a file to which +/// output is appended. If the file does not exist, it is created. +/// +/// Any errors in the settings cause messages to be output to stderr. +/// +/// This function is aimed at test programs, allowing the default settings to +/// be overridden by the tester. It is not intended for use in production +/// code. +/// +/// TODO: Rename. This function overloads the initLogger() function that can +/// be used to initialize production programs. This may lead to confusion. +void initLogger(isc::log::Severity severity = isc::log::DEBUG, + int dbglevel = isc::log::MAX_DEBUG_LEVEL); + + +/// \brief Logging Severity from B10_LOGGER_SEVERITY +/// +/// Support function called by the unit test logging initialization code. +/// It returns the logging severity defined by B10_LOGGER_SEVERITY. If +/// not defined it returns the default passed to it. +/// +/// \param defseverity Default severity used if B10_LOGGER_SEVERITY is not +// defined. +/// +/// \return Severity to use for the logging. +isc::log::Severity b10LoggerSeverity(isc::log::Severity defseverity); + + +/// \brief Logging Debug Level from B10_LOGGER_DBGLEVEL +/// +/// Support function called by the unit test logging initialization code. +/// It returns the logging debug level defined by B10_LOGGER_DBGLEVEL. If +/// not defined, it returns the default passed to it. +/// +/// N.B. If there is an error, a message is written to stderr and a value +/// related to the error is used. (This is because (a) logging is not yet +/// initialized, hence only the error stream is known to exist, and (b) this +/// function is only used in unit test logging initialization, so incorrect +/// selection of a level is not really an issue.) +/// +/// \param defdbglevel Default debug level to be used if B10_LOGGER_DBGLEVEL +/// is not defined. +/// +/// \return Debug level to use. +int b10LoggerDbglevel(int defdbglevel); + + +/// \brief Reset Logger Characteristics +/// +/// This is a simplified interface into the setting of the characteristics +/// of the root logger. It is aimed for use in unit tests and resets the +/// characteristics of the root logger to use a severity, debug level and +/// destination set by the environment variables B10_LOGGER_SEVERITY, +/// B10_LOGGER_DBGLEVEL and B10_LOGGER_DESTINATION. +void +setUnitTestRootLoggerCharacteristics(); + +} // namespace log +} // namespace isc + + + +#endif // __LOGGER_UNITTEST_SUPPORT_H diff --git a/src/lib/log/tests/logger_level_impl_unittest.cc b/src/lib/log/tests/logger_level_impl_unittest.cc index 0ded7f9c05..0342328400 100644 --- a/src/lib/log/tests/logger_level_impl_unittest.cc +++ b/src/lib/log/tests/logger_level_impl_unittest.cc @@ -20,6 +20,7 @@ #include #include +#include #include using namespace isc::log; @@ -27,8 +28,10 @@ using namespace std; class LoggerLevelImplTest : public ::testing::Test { protected: - LoggerLevelImplTest() - {} + LoggerLevelImplTest() { + // Ensure logging set to default for unit tests + setUnitTestRootLoggerCharacteristics(); + } ~LoggerLevelImplTest() {} diff --git a/src/lib/log/tests/logger_level_unittest.cc b/src/lib/log/tests/logger_level_unittest.cc index 8c98091d5f..efb5937a13 100644 --- a/src/lib/log/tests/logger_level_unittest.cc +++ b/src/lib/log/tests/logger_level_unittest.cc @@ -20,7 +20,7 @@ #include #include #include -#include +#include using namespace isc; using namespace isc::log; @@ -29,7 +29,9 @@ using namespace std; class LoggerLevelTest : public ::testing::Test { protected: LoggerLevelTest() { - // Logger initialization is done in main() + // Logger initialization is done in main(). As logging tests may + // alter the default logging output, it is reset here. + setUnitTestRootLoggerCharacteristics(); } ~LoggerLevelTest() { LoggerManager::reset(); @@ -57,7 +59,7 @@ TEST_F(LoggerLevelTest, Creation) { EXPECT_EQ(42, level3.dbglevel); } -TEST(LoggerLevel, getSeverity) { +TEST_F(LoggerLevelTest, getSeverity) { EXPECT_EQ(DEBUG, getSeverity("DEBUG")); EXPECT_EQ(DEBUG, getSeverity("debug")); EXPECT_EQ(DEBUG, getSeverity("DeBuG")); diff --git a/src/lib/log/tests/logger_support_unittest.cc b/src/lib/log/tests/logger_support_unittest.cc index 6a93652cfc..1c1bc32eef 100644 --- a/src/lib/log/tests/logger_support_unittest.cc +++ b/src/lib/log/tests/logger_support_unittest.cc @@ -18,12 +18,23 @@ using namespace isc::log; +class LoggerSupportTest : public ::testing::Test { +protected: + LoggerSupportTest() { + // Logger initialization is done in main(). As logging tests may + // alter the default logging output, it is reset here. + setUnitTestRootLoggerCharacteristics(); + } + ~LoggerSupportTest() { + } +}; + // Check that the initialized flag can be manipulated. This is a bit chicken- // -and-egg: we want to reset to the flag to the original value at the end // of the test, so use the functions to do that. But we are trying to check // that these functions in fact work. -TEST(LoggerSupportTest, InitializedFlag) { +TEST_F(LoggerSupportTest, InitializedFlag) { bool current_flag = isLoggingInitialized(); // check we can flip the flag. @@ -51,7 +62,7 @@ TEST(LoggerSupportTest, InitializedFlag) { // Check that a logger will throw an exception if logging has not been // initialized. -TEST(LoggerSupportTest, LoggingInitializationCheck) { +TEST_F(LoggerSupportTest, LoggingInitializationCheck) { // Assert that logging has been initialized (it should be in main()). bool current_flag = isLoggingInitialized(); From 0958095d36903cd821afc57be0c038896dd1acdb Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 13 Jul 2011 18:41:57 +0100 Subject: [PATCH 195/974] [trac1024] Rename function resetting root logger characteristics Shortened the name to resetUnitTestRootLogger() to make it a bit less of a mouthful. --- src/lib/log/logger_unittest_support.cc | 4 ++-- src/lib/log/logger_unittest_support.h | 6 +++--- src/lib/log/tests/logger_level_impl_unittest.cc | 2 +- src/lib/log/tests/logger_level_unittest.cc | 2 +- src/lib/log/tests/logger_support_unittest.cc | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/log/logger_unittest_support.cc b/src/lib/log/logger_unittest_support.cc index 86d8491c6e..a0969be6bc 100644 --- a/src/lib/log/logger_unittest_support.cc +++ b/src/lib/log/logger_unittest_support.cc @@ -80,7 +80,7 @@ b10LoggerDbglevel(int defdbglevel) { // variables B10_LOGGER_SEVERITY, B10_LOGGER_DBGLEVEL and B10_LOGGER_DESTINATION. void -setUnitTestRootLoggerCharacteristics() { +resetUnitTestRootLogger() { using namespace isc::log; @@ -168,7 +168,7 @@ void initLogger(isc::log::Severity severity, int dbglevel) { // in the environment variables. (The two-step approach is used as the // setUnitTestRootLoggerCharacteristics() function is used in several // places in the BIND 10 tests, and it avoid duplicating code.) - setUnitTestRootLoggerCharacteristics(); + resetUnitTestRootLogger(); } } // namespace log diff --git a/src/lib/log/logger_unittest_support.h b/src/lib/log/logger_unittest_support.h index 221566a5ae..e98544e442 100644 --- a/src/lib/log/logger_unittest_support.h +++ b/src/lib/log/logger_unittest_support.h @@ -103,15 +103,15 @@ isc::log::Severity b10LoggerSeverity(isc::log::Severity defseverity); int b10LoggerDbglevel(int defdbglevel); -/// \brief Reset Logger Characteristics +/// \brief Reset Root Logger Characteristics /// -/// This is a simplified interface into the setting of the characteristics +/// This is a simplified interface into the resetting of the characteristics /// of the root logger. It is aimed for use in unit tests and resets the /// characteristics of the root logger to use a severity, debug level and /// destination set by the environment variables B10_LOGGER_SEVERITY, /// B10_LOGGER_DBGLEVEL and B10_LOGGER_DESTINATION. void -setUnitTestRootLoggerCharacteristics(); +resetUnitTestRootLogger(); } // namespace log } // namespace isc diff --git a/src/lib/log/tests/logger_level_impl_unittest.cc b/src/lib/log/tests/logger_level_impl_unittest.cc index 0342328400..dacd2023d5 100644 --- a/src/lib/log/tests/logger_level_impl_unittest.cc +++ b/src/lib/log/tests/logger_level_impl_unittest.cc @@ -30,7 +30,7 @@ class LoggerLevelImplTest : public ::testing::Test { protected: LoggerLevelImplTest() { // Ensure logging set to default for unit tests - setUnitTestRootLoggerCharacteristics(); + resetUnitTestRootLogger(); } ~LoggerLevelImplTest() diff --git a/src/lib/log/tests/logger_level_unittest.cc b/src/lib/log/tests/logger_level_unittest.cc index efb5937a13..641a6cccb7 100644 --- a/src/lib/log/tests/logger_level_unittest.cc +++ b/src/lib/log/tests/logger_level_unittest.cc @@ -31,7 +31,7 @@ protected: LoggerLevelTest() { // Logger initialization is done in main(). As logging tests may // alter the default logging output, it is reset here. - setUnitTestRootLoggerCharacteristics(); + resetUnitTestRootLogger(); } ~LoggerLevelTest() { LoggerManager::reset(); diff --git a/src/lib/log/tests/logger_support_unittest.cc b/src/lib/log/tests/logger_support_unittest.cc index 1c1bc32eef..b4189061f6 100644 --- a/src/lib/log/tests/logger_support_unittest.cc +++ b/src/lib/log/tests/logger_support_unittest.cc @@ -23,7 +23,7 @@ protected: LoggerSupportTest() { // Logger initialization is done in main(). As logging tests may // alter the default logging output, it is reset here. - setUnitTestRootLoggerCharacteristics(); + resetUnitTestRootLogger(); } ~LoggerSupportTest() { } From 71eee6e279d7527adbc1e325b0cca49d824b67ee Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 13 Jul 2011 18:46:10 +0100 Subject: [PATCH 196/974] [trac1024] Make "reset unit test root logger" available to Python ... and use it in the Python unit tests that generate logging output. --- src/bin/bind10/tests/bind10_test.py.in | 2 ++ src/bin/cmdctl/tests/cmdctl_test.py | 2 ++ src/bin/xfrout/tests/xfrout_test.py.in | 2 ++ src/lib/python/isc/log/log.cc | 29 +++++++++++++++++++++++++- 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 91d326c339..9d794a6a08 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -26,6 +26,7 @@ import socket from isc.net.addr import IPAddr import time import isc +import isc.log from isc.testutils.parse_args import TestOptParser, OptsError @@ -764,4 +765,5 @@ class TestBrittle(unittest.TestCase): self.assertFalse(bob.runnable) if __name__ == '__main__': + isc.log.resetUnitTestRootLogger() unittest.main() diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py index e77c529299..3103f479c7 100644 --- a/src/bin/cmdctl/tests/cmdctl_test.py +++ b/src/bin/cmdctl/tests/cmdctl_test.py @@ -19,6 +19,7 @@ import socket import tempfile import sys from cmdctl import * +import isc.log SPEC_FILE_PATH = '..' + os.sep if 'CMDCTL_SPEC_PATH' in os.environ: @@ -447,6 +448,7 @@ class TestFuncNotInClass(unittest.TestCase): if __name__== "__main__": + isc.log.resetUnitTestRootLogger() unittest.main() diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index adabf48ebf..7ab4a5895e 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -23,6 +23,7 @@ from isc.cc.session import * from pydnspp import * from xfrout import * import xfrout +import isc.log TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==") @@ -670,4 +671,5 @@ class TestInitialization(unittest.TestCase): self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File") if __name__== "__main__": + isc.log.resetUnitTestRootLogger() unittest.main() diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc index 484151f424..ace1f711df 100644 --- a/src/lib/python/isc/log/log.cc +++ b/src/lib/python/isc/log/log.cc @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -35,7 +36,7 @@ using boost::bind; // (tags/RELEASE_28 115909)) on OSX, where unwinding the stack // segfaults the moment this exception was thrown and caught. // -// Placing it in a named namespace instead of the original +// Placing it in a named namespace instead of the originalRecommend // unnamed namespace appears to solve this, so as a temporary // workaround, we create a local randomly named namespace here // to solve this issue. @@ -184,6 +185,27 @@ init(PyObject*, PyObject* args) { Py_RETURN_NONE; } +// This initialization is for unit tests. It allows message settings to be +// be determined by a set of B10_xxx environment variables. (See the +// description of initLogger() for more details.) The function has been named +// resetUnitTestRootLogger() here as being more descriptive and +// trying to avoid confusion. +PyObject* +resetUnitTestRootLogger(PyObject*, PyObject*) { + try { + isc::log::resetUnitTestRootLogger(); + } + catch (const std::exception& e) { + PyErr_SetString(PyExc_RuntimeError, e.what()); + return (NULL); + } + catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception"); + return (NULL); + } + Py_RETURN_NONE; +} + PyObject* logConfigUpdate(PyObject*, PyObject* args) { // we have no wrappers for ElementPtr and ConfigData, @@ -246,6 +268,11 @@ 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."}, + {"resetUnitTestRootLogger", resetUnitTestRootLogger, METH_VARARGS, + "Initialization for unit tests. Sets the severity and output stream " + "according to a set of environment variables. This should not be " + "used in production code. The name is slightly confusing, but it " + "mirrors a method of the same name used for the C++ unit tests."}, {"log_config_update", logConfigUpdate, METH_VARARGS, "Update logger settings. This method is automatically used when " "ModuleCCSession is initialized with handle_logging_config set " From a7047de1ec7aece83271cc28605ea1d790afee67 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 13 Jul 2011 19:06:00 +0100 Subject: [PATCH 197/974] [trac1024] Route messages to default destination for xfrin tests --- src/bin/xfrin/tests/xfrin_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py index 2acd9d68a9..92bf1b01bb 100644 --- a/src/bin/xfrin/tests/xfrin_test.py +++ b/src/bin/xfrin/tests/xfrin_test.py @@ -18,6 +18,7 @@ import socket import io from isc.testutils.tsigctx_mock import MockTSIGContext from xfrin import * +import isc.log # # Commonly used (mostly constant) test parameters @@ -1115,6 +1116,7 @@ class TestMain(unittest.TestCase): if __name__== "__main__": try: + isc.log.resetUnitTestRootLogger() unittest.main() except KeyboardInterrupt as e: print(e) From 3b4b066b5d1c3726f51e52fee52c317a3ae3f9e3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 13 Jul 2011 11:55:50 -0700 Subject: [PATCH 198/974] [trac983] made importing json.dumps() more robust: - explicitly imports the json module in __init__.py (this should actually be done at the isc package level, but we now don't rely on the implicit side effect). - get the reference to the dumps() callable at the RequestLoader initialization time and keep it thereafter. This would also slightly improve runtime performance. --- src/lib/python/isc/acl/__init__.py | 4 ++ .../isc/acl/dns_requestloader_python.cc | 57 ++++++++++++------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/lib/python/isc/acl/__init__.py b/src/lib/python/isc/acl/__init__.py index e83a101a20..d9b283892f 100644 --- a/src/lib/python/isc/acl/__init__.py +++ b/src/lib/python/isc/acl/__init__.py @@ -2,6 +2,10 @@ Here are function and classes for manipulating access control lists. """ +# The DNS ACL loader would need the json module. Make sure it's imported +# beforehand. +import json + # Other ACL modules highly depends on the main acl sub module, so it's # explicitly imported here. import isc.acl.acl diff --git a/src/lib/python/isc/acl/dns_requestloader_python.cc b/src/lib/python/isc/acl/dns_requestloader_python.cc index aa5df678f5..1ddff4c0b3 100644 --- a/src/lib/python/isc/acl/dns_requestloader_python.cc +++ b/src/lib/python/isc/acl/dns_requestloader_python.cc @@ -82,9 +82,10 @@ RequestLoader_destroy(PyObject* po_self) { Py_TYPE(self)->tp_free(self); } -// This helper function essentially does: -// import json -// return json.dumps +// This C structure corresponds to a Python callable object for json.dumps(). +// This is initialized at the class initialization time (in +// initModulePart_RequestLoader() below) and it's ensured to be non NULL and +// valid in the rest of the class implementation. // Getting access to the json module this way and call one of its functions // via PyObject_CallObject() may exceed the reasonably acceptable level for // straightforward bindings. But the alternative would be to write a Python @@ -92,18 +93,7 @@ RequestLoader_destroy(PyObject* po_self) { // be too much. So, right now, we implement everything within the binding // implementation. If future extensions require more such non trivial // wrappers, we should consider the frontend approach more seriously. -PyObject* -getJSONDumpsObj() { - PyObject* json_dump_obj = NULL; - PyObject* json_module = PyImport_AddModule("json"); - if (json_module != NULL) { - PyObject* json_dict = PyModule_GetDict(json_module); - if (json_dict != NULL) { - json_dump_obj = PyDict_GetItemString(json_dict, "dumps"); - } - } - return (json_dump_obj); -} +PyObject* json_dumps_obj = NULL; PyObject* RequestLoader_load(PyObject* po_self, PyObject* args) { @@ -125,12 +115,9 @@ RequestLoader_load(PyObject* po_self, PyObject* args) { // tuple as its argument parameter, just like ParseTuple. PyObject* json_obj; if (PyArg_ParseTuple(args, "O", &json_obj)) { - PyObject* json_dumps_obj = getJSONDumpsObj(); - if (json_dumps_obj != NULL) { - c1.reset(PyObject_CallObject(json_dumps_obj, args)); - c2.reset(Py_BuildValue("(O)", c1.get())); - py_result = PyArg_ParseTuple(c2.get(), "s", &acl_config); - } + c1.reset(PyObject_CallObject(json_dumps_obj, args)); + c2.reset(Py_BuildValue("(O)", c1.get())); + py_result = PyArg_ParseTuple(c2.get(), "s", &acl_config); } } if (py_result) { @@ -245,6 +232,34 @@ initModulePart_RequestLoader(PyObject* mod) { static_cast(p)) < 0) { return (false); } + + // Get and hold our own reference to json.dumps() for later use. + // Normally it should succeed as __init__.py of the isc.acl package + // explicitly imports the json module, and the code below should be + // error free (e.g. they don't require memory allocation) under this + // condition. + // This could still fail with deviant or evil Python code such as those + // that first import json and then delete the reference to it from + // sys.modules before it imports the acl.dns module. The RequestLoader + // class could still work as long as it doesn't use the JSON decoder, + // but we'd rather refuse to import the module than allowing the partially + // workable class to keep running. + PyObject* json_module = PyImport_AddModule("json"); + if (json_module != NULL) { + PyObject* json_dict = PyModule_GetDict(json_module); + if (json_dict != NULL) { + json_dumps_obj = PyDict_GetItemString(json_dict, "dumps"); + } + } + if (json_dumps_obj != NULL) { + Py_INCREF(json_dumps_obj); + } else { + PyErr_SetString(PyExc_RuntimeError, + "isc.acl.dns.RequestLoader needs the json module, but " + "it's missing"); + return (false); + } + Py_INCREF(&requestloader_type); return (true); From 62f912bd96a5fefeb0eb8b017ff12335810483b0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 13 Jul 2011 14:12:36 -0700 Subject: [PATCH 199/974] [trac910] updated the python binding part and tests for the TSIG + TC bit cases. --- src/lib/dns/python/message_python.cc | 9 ++ .../dns/python/tests/message_python_test.py | 82 +++++++++++++++++-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 2842588b07..00596f87d0 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -703,6 +703,15 @@ Message_toWire(s_Message* self, PyObject* args) { // python program has a bug. PyErr_SetString(po_TSIGContextError, ex.what()); return (NULL); + } catch (const std::exception& ex) { + // Other exceptions should be rare (most likely an implementation + // bug) + PyErr_SetString(po_TSIGContextError, ex.what()); + return (NULL); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, + "Unexpected C++ exception in Message.to_wire"); + return (NULL); } } PyErr_Clear(); diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index 3727ae7438..ea670947f7 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -32,7 +32,7 @@ else: def factoryFromFile(message, file): data = read_wire_data(file) message.from_wire(data) - pass + return data # we don't have direct comparison for rrsets right now (should we? # should go in the cpp version first then), so also no direct list @@ -45,6 +45,15 @@ def compare_rrset_list(list1, list2): return False return True +# These are used for TSIG + TC tests +LONG_TXT1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde"; + +LONG_TXT2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456"; + +LONG_TXT3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"; + +LONG_TXT4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0"; + # a complete message taken from cpp tests, for testing towire and totext def create_message(): message_render = Message(Message.RENDER) @@ -286,12 +295,18 @@ class MessageTest(unittest.TestCase): self.assertRaises(InvalidMessageOperation, self.r.to_wire, MessageRenderer()) - def __common_tsigquery_setup(self, flags=[Message.HEADERFLAG_RD], - rrtype=RRType("A")): + def __common_tsigmessage_setup(self, flags=[Message.HEADERFLAG_RD], + rrtype=RRType("A"), answer_data=None): self.r.set_opcode(Opcode.QUERY()) self.r.set_rcode(Rcode.NOERROR()) for flag in flags: self.r.set_header_flag(flag) + if answer_data is not None: + rrset = RRset(Name("www.example.com"), RRClass("IN"), + rrtype, RRTTL(86400)) + for rdata in answer_data: + rrset.add_rdata(Rdata(rrtype, RRClass("IN"), rdata)) + self.r.add_rrset(Message.SECTION_ANSWER, rrset) self.r.add_question(Question(Name("www.example.com"), RRClass("IN"), rrtype)) @@ -303,18 +318,75 @@ class MessageTest(unittest.TestCase): def test_to_wire_with_tsig(self): fix_current_time(0x4da8877a) self.r.set_qid(0x2d65) - self.__common_tsigquery_setup() + self.__common_tsigmessage_setup() self.__common_tsig_checks("message_toWire2.wire") def test_to_wire_with_edns_tsig(self): fix_current_time(0x4db60d1f) self.r.set_qid(0x6cd) - self.__common_tsigquery_setup() + self.__common_tsigmessage_setup() edns = EDNS() edns.set_udp_size(4096) self.r.set_edns(edns) self.__common_tsig_checks("message_toWire3.wire") + def test_to_wire_tsig_truncation(self): + fix_current_time(0x4e179212) + data = factoryFromFile(self.p, "message_fromWire17.wire") + self.assertEqual(TSIGError.NOERROR, + self.tsig_ctx.verify(self.p.get_tsig_record(), data)) + self.r.set_qid(0x22c2) + self.__common_tsigmessage_setup([Message.HEADERFLAG_QR, + Message.HEADERFLAG_AA, + Message.HEADERFLAG_RD], + RRType("TXT"), + [LONG_TXT1, LONG_TXT2]) + self.__common_tsig_checks("message_toWire4.wire") + + def test_to_wire_tsig_truncation2(self): + fix_current_time(0x4e179212) + data = factoryFromFile(self.p, "message_fromWire17.wire") + self.assertEqual(TSIGError.NOERROR, + self.tsig_ctx.verify(self.p.get_tsig_record(), data)) + self.r.set_qid(0x22c2) + self.__common_tsigmessage_setup([Message.HEADERFLAG_QR, + Message.HEADERFLAG_AA, + Message.HEADERFLAG_RD], + RRType("TXT"), + [LONG_TXT1, LONG_TXT3]) + self.__common_tsig_checks("message_toWire4.wire") + + def test_to_wire_tsig_no_truncation(self): + fix_current_time(0x4e17b38d) + data = factoryFromFile(self.p, "message_fromWire18.wire") + self.assertEqual(TSIGError.NOERROR, + self.tsig_ctx.verify(self.p.get_tsig_record(), data)) + self.r.set_qid(0xd6e2) + self.__common_tsigmessage_setup([Message.HEADERFLAG_QR, + Message.HEADERFLAG_AA, + Message.HEADERFLAG_RD], + RRType("TXT"), + [LONG_TXT1, LONG_TXT4]) + self.__common_tsig_checks("message_toWire5.wire") + + def test_to_wire_tsig_length_errors(self): + renderer = MessageRenderer() + renderer.set_length_limit(84) # 84 = expected TSIG length - 1 + self.__common_tsigmessage_setup() + self.assertRaises(TSIGContextError, + self.r.to_wire, renderer, self.tsig_ctx) + + renderer.clear() + self.r.clear(Message.RENDER) + renderer.set_length_limit(86) # 86 = expected TSIG length + 1 + self.__common_tsigmessage_setup() + self.assertRaises(TSIGContextError, + self.r.to_wire, renderer, self.tsig_ctx) + + # skip the last test of the corresponding C++ test: it requires + # subclassing MessageRenderer, which is (currently) not possible + # for python. In any case, it's very unlikely to happen in practice. + def test_to_text(self): message_render = create_message() From 8cec4587428e4fba8f5cf8791f19f8373212b250 Mon Sep 17 00:00:00 2001 From: Yoshitaka Aharen Date: Thu, 14 Jul 2011 11:36:42 +0900 Subject: [PATCH 200/974] ChangeLog for trac1016 --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 0aee22a67f..8b597e6d5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +269. [bug] y-aharen + Modified IntervalTimerTest not to rely on the accuracy of the timer. + This fix addresses occasional failure of build tests. + (Trac #1016, git 090c4c5abac33b2b28d7bdcf3039005a014f9c5b) + 268. [func] stephen Add environment variable to allow redirection of logging output during unit tests. From 146c48357b32d26019675834eda1daddde95302c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 13 Jul 2011 23:36:41 -0700 Subject: [PATCH 201/974] [trac910] revised Question::toWire() so that it handles truncation case. This is not directly related to the subject of this ticket, but would be necessary to complete the TSIG + TC bit support. --- src/lib/dns/python/tests/question_python_test.py | 10 ++++++++-- src/lib/dns/question.cc | 9 +++++++++ src/lib/dns/question.h | 16 ++++++++-------- src/lib/dns/tests/question_unittest.cc | 16 ++++++++++++++++ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/lib/dns/python/tests/question_python_test.py b/src/lib/dns/python/tests/question_python_test.py index 69e3051933..8c8c81580e 100644 --- a/src/lib/dns/python/tests/question_python_test.py +++ b/src/lib/dns/python/tests/question_python_test.py @@ -74,7 +74,6 @@ class QuestionTest(unittest.TestCase): self.assertEqual("foo.example.com. IN NS\n", str(self.test_question1)) self.assertEqual("bar.example.com. CH A\n", self.test_question2.to_text()) - def test_to_wire_buffer(self): obuffer = bytes() obuffer = self.test_question1.to_wire(obuffer) @@ -82,7 +81,6 @@ class QuestionTest(unittest.TestCase): wiredata = read_wire_data("question_toWire1") self.assertEqual(obuffer, wiredata) - def test_to_wire_renderer(self): renderer = MessageRenderer() self.test_question1.to_wire(renderer) @@ -91,5 +89,13 @@ class QuestionTest(unittest.TestCase): self.assertEqual(renderer.get_data(), wiredata) self.assertRaises(TypeError, self.test_question1.to_wire, 1) + def test_to_wire_truncated(self): + renderer = MessageRenderer() + renderer.set_length_limit(self.example_name1.get_length()) + self.assertFalse(renderer.is_truncated()) + self.test_question1.to_wire(renderer) + self.assertTrue(renderer.is_truncated()) + self.assertEqual(0, renderer.get_length()) + if __name__ == '__main__': unittest.main() diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc index 96e2a9c895..6ccb164ed1 100644 --- a/src/lib/dns/question.cc +++ b/src/lib/dns/question.cc @@ -57,10 +57,19 @@ Question::toWire(OutputBuffer& buffer) const { unsigned int Question::toWire(AbstractMessageRenderer& renderer) const { + const size_t pos0 = renderer.getLength(); + renderer.writeName(name_); rrtype_.toWire(renderer); rrclass_.toWire(renderer); + // Make sure the renderer has a room for the question + if (renderer.getLength() > renderer.getLengthLimit()) { + renderer.trim(renderer.getLength() - pos0); + renderer.setTruncated(); + return (0); + } + return (1); // number of "entries" } diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h index b3f3d98356..5d2783b6f0 100644 --- a/src/lib/dns/question.h +++ b/src/lib/dns/question.h @@ -201,23 +201,23 @@ public: /// class description). /// /// The owner name will be compressed if possible, although it's an - /// unlikely event in practice because the %Question section a DNS + /// unlikely event in practice because the Question section a DNS /// message normally doesn't contain multiple question entries and /// it's located right after the Header section. /// Nevertheless, \c renderer records the information of the owner name /// so that it can be pointed by other RRs in other sections (which is /// more likely to happen). /// - /// In theory, an attempt to render a Question may cause truncation - /// (when the Question section contains a large number of entries), - /// but this implementation doesn't catch that situation. - /// It would make the code unnecessarily complicated (though perhaps - /// slightly) for almost impossible case in practice. - /// An upper layer will handle the pathological case as a general error. + /// It could be possible, though very rare in practice, that + /// an attempt to render a Question may cause truncation + /// (when the Question section contains a large number of entries). + /// In such a case this method avoid the rendering and indicate the + /// truncation in the \c renderer. This method returns 0 in this case. /// /// \param renderer DNS message rendering context that encapsulates the /// output buffer and name compression information. - /// \return 1 + /// + /// \return 1 on success; 0 if it causes truncation unsigned int toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the Question in the wire format without name compression. diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc index 25fd75b4c6..1d483f2591 100644 --- a/src/lib/dns/tests/question_unittest.cc +++ b/src/lib/dns/tests/question_unittest.cc @@ -106,6 +106,22 @@ TEST_F(QuestionTest, toWireRenderer) { obuffer.getLength(), &wiredata[0], wiredata.size()); } +TEST_F(QuestionTest, toWireTruncated) { + // If the available length in the renderer is too small, it would require + // truncation. This won't happen in normal cases, but protocol wise it + // could still happen if and when we support some (possibly future) opcode + // that allows multiple questions. + + // Set the length limit to the qname length so that the whole question + // would request truncated + renderer.setLengthLimit(example_name1.getLength()); + + EXPECT_FALSE(renderer.isTruncated()); // check pre-render condition + EXPECT_EQ(0, test_question1.toWire(renderer)); + EXPECT_TRUE(renderer.isTruncated()); + EXPECT_EQ(0, renderer.getLength()); // renderer shouldn't have any data +} + // test operator<<. We simply confirm it appends the result of toText(). TEST_F(QuestionTest, LeftShiftOperator) { ostringstream oss; From 54c3708f45c72065cefd4d6013be5467bee65f85 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 00:01:48 -0700 Subject: [PATCH 202/974] [trac910] completed TSIG+TC support: added a minor case of too many questions. --- src/lib/dns/message.cc | 26 ++++++---- .../dns/python/tests/message_python_test.py | 14 ++++++ src/lib/dns/tests/message_unittest.cc | 50 ++++++++++++++++--- 3 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index c6e93d097c..c5ba4e1a07 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -240,15 +240,20 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { } // Reserve the space for TSIG (if needed) so that we can handle truncation - // case correctly later when that happens. + // case correctly later when that happens. orig_xxx variables remember + // some configured parameters of renderer in case they are needed in + // truncation processing below. const size_t tsig_len = (tsig_ctx != NULL) ? tsig_ctx->getTSIGLength() : 0; + const size_t orig_msg_len_limit = renderer.getLengthLimit(); + const AbstractMessageRenderer::CompressMode orig_compress_mode = + renderer.getCompressMode(); if (tsig_len > 0) { - if (tsig_len > renderer.getLengthLimit()) { + if (tsig_len > orig_msg_len_limit) { isc_throw(InvalidParameter, "Failed to render DNS message: " "too small limit for a TSIG (" << - renderer.getLengthLimit() << ")"); + orig_msg_len_limit << ")"); } - renderer.setLengthLimit(renderer.getLengthLimit() - tsig_len); + renderer.setLengthLimit(orig_msg_len_limit - tsig_len); } // reserve room for the header @@ -302,14 +307,15 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { // If we're adding a TSIG to a truncated message, clear all RRsets // from the message except for the question before adding the TSIG. - // TODO: If even the question doesn't fit, don't include any question. + // If even (some of) the question doesn't fit, don't include it. if (tsig_ctx != NULL && renderer.isTruncated()) { renderer.clear(); + renderer.setLengthLimit(orig_msg_len_limit - tsig_len); + renderer.setCompressMode(orig_compress_mode); renderer.skip(HEADERLEN); - qdcount = - for_each(questions_.begin(), questions_.end(), - RenderSection(renderer, - false)).getTotalCount(); + qdcount = for_each(questions_.begin(), questions_.end(), + RenderSection(renderer, + false)).getTotalCount(); ancount = 0; nscount = 0; arcount = 0; @@ -348,7 +354,7 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { // Add TSIG, if necessary, at the end of the message. if (tsig_ctx != NULL) { // Release the reserved space in the renderer. - renderer.setLengthLimit(renderer.getLengthLimit() + tsig_len); + renderer.setLengthLimit(orig_msg_len_limit); const int tsig_count = tsig_ctx->sign(qid_, renderer.getData(), diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index ea670947f7..c7312536ed 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -356,6 +356,20 @@ class MessageTest(unittest.TestCase): [LONG_TXT1, LONG_TXT3]) self.__common_tsig_checks("message_toWire4.wire") + def test_to_wire_tsig_truncation3(self): + self.r.set_opcode(Opcode.QUERY()) + self.r.set_rcode(Rcode.NOERROR()) + for i in range(1, 68): + self.r.add_question(Question(Name("www.example.com"), + RRClass("IN"), RRType(i))) + renderer = MessageRenderer() + self.r.to_wire(renderer, self.tsig_ctx) + + self.p.from_wire(renderer.get_data()) + self.assertTrue(self.p.get_header_flag(Message.HEADERFLAG_TC)) + self.assertEqual(66, self.p.get_rr_count(Message.SECTION_QUESTION)) + self.assertNotEqual(None, self.p.get_tsig_record()) + def test_to_wire_tsig_no_truncation(self): fix_current_time(0x4e17b38d) data = factoryFromFile(self.p, "message_fromWire18.wire") diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 2a3d988840..6430626228 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -772,6 +772,39 @@ TEST_F(MessageTest, toWireTSIGTruncation2) { } } +TEST_F(MessageTest, toWireTSIGTruncation3) { + // Similar to previous ones, but truncation occurs due to too many + // Questions (very unusual, but not necessarily illegal). + + // We are going to create a message starting with a standard + // header (12 bytes) and multiple questions in the Question + // section of the same owner name (changing the RRType, just so + // that it would be the form that would be accepted by the BIND 9 + // parser). The first Question is 21 bytes in length, and the subsequent + // ones are 6 bytes. We'll also use a TSIG whose size is 85 bytes. + // Up to 66 questions can fit in the standard 512-byte buffer + // (12 + 21 + 6 * 65 + 85 = 508). If we try to add one more it would + // result in truncation. + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + for (int i = 1; i <= 67; ++i) { + message_render.addQuestion(Question(Name("www.example.com"), + RRClass::IN(), RRType(i))); + } + message_render.toWire(renderer, tsig_ctx); + + // Check the rendered data by parsing it. We only check it has the + // TC bit on, has the correct number of questions, and has a TSIG RR. + // Checking the signature wouldn't be necessary for this rare case + // scenario. + InputBuffer buffer(renderer.getData(), renderer.getLength()); + message_parse.fromWire(buffer); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC)); + // Note that the number of questions are 66, not 67 as we tried to add. + EXPECT_EQ(66, message_parse.getRRCount(Message::SECTION_QUESTION)); + EXPECT_TRUE(message_parse.getTSIGRecord() != NULL); +} + TEST_F(MessageTest, toWireTSIGNoTruncation) { // A boundary case that shouldn't cause truncation: the resulting // response message with a TSIG will be 512 bytes long. @@ -795,20 +828,21 @@ TEST_F(MessageTest, toWireTSIGNoTruncation) { } // This is a buggy renderer for testing. It behaves like the straightforward -// MessageRenderer, but once its internal buffer reaches the length for -// the header and a question for www.example.com (33 bytes), its -// getLengthLimit() returns a faked value, which would make TSIG RR rendering -// fail unexpectedly in the test that follows. +// MessageRenderer, but once it has some data, its setLengthLimit() ignores +// the given parameter and resets the limit to the current length, making +// subsequent insertion result in truncation, which would make TSIG RR +// rendering fail unexpectedly in the test that follows. class BadRenderer : public MessageRenderer { public: BadRenderer(isc::util::OutputBuffer& buffer) : MessageRenderer(buffer) {} - virtual size_t getLengthLimit() const { - if (MessageRenderer::getLength() >= 33) { - return (0); + virtual void setLengthLimit(size_t len) { + if (getLength() > 0) { + MessageRenderer::setLengthLimit(getLength()); + } else { + MessageRenderer::setLengthLimit(len); } - return (MessageRenderer::getLengthLimit()); } }; From dfd8332b1a958ed9aeb6ae423ea937b5e08024f8 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 00:16:57 -0700 Subject: [PATCH 203/974] [trac910] documentation update --- src/lib/dns/tsig.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h index 80a49de25c..13d599c308 100644 --- a/src/lib/dns/tsig.h +++ b/src/lib/dns/tsig.h @@ -353,8 +353,25 @@ public: TSIGError verify(const TSIGRecord* const record, const void* const data, const size_t data_len); - /// TBD: mostly for internal use. context dependent. - /// won't provide python binding. + /// Return the expected length of TSIG RR after \c sign() + /// + /// This method returns the length of the TSIG RR based that would be + /// produced as a result of \c sign() with the state of the context + /// at the time of the call. The expected length can be decided + /// from the key and the algorithm (which determines the MAC size if + /// included) and the recorded TSIG error. Specifically, if a key + /// related error has been identified, the MAC will be excluded; if + /// a time error has occurred, the TSIG will include "other data". + /// + /// This method is provided mainly for the convenient of the Message class, + /// which needs to know the expected TSIG length in rendering a signed + /// DNS message so that it can handle truncated messages with TSIG + /// correctly. Normal applications wouldn't need this method. The Python + /// binding for this method won't be provided for the same reason. + /// + /// \exception None + /// + /// \return The expected TISG RR length in bytes size_t getTSIGLength() const; /// Return the current state of the context From 151ea34890984f1fb2404df848c1dcbf3e61d765 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 01:24:09 -0700 Subject: [PATCH 204/974] [trac983] documentation cleanup: removed a description of RequestLoader which is not (at least yet) applicable. also added a note about the difference between the C++ doc and the python version derived from it. --- src/lib/python/isc/acl/dns_requestloader_inc.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/acl/dns_requestloader_inc.cc b/src/lib/python/isc/acl/dns_requestloader_inc.cc index e05a58012b..a911275382 100644 --- a/src/lib/python/isc/acl/dns_requestloader_inc.cc +++ b/src/lib/python/isc/acl/dns_requestloader_inc.cc @@ -1,13 +1,14 @@ namespace { + +// Note: this is derived from the generic Loader class of the C++ +// implementation, but is slightly different from the original. +// Be careful when you make further merge from the C++ document. const char* const RequestLoader_doc = "\ Loader of DNS Request ACLs.\n\ \n\ The goal of this class is to convert JSON description of an ACL to\n\ object of the ACL class (including the checks inside it).\n\ \n\ -The class can be used to load the checks only. This is supposed to be\n\ -used by compound checks to create the subexpressions.\n\ -\n\ To allow any kind of checks to exist in the application, creators are\n\ registered for the names of the checks (this feature is not yet\n\ available for the python API).\n\ From 365948a46f61db8726a24bfd0c625d26a014f63a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 01:42:26 -0700 Subject: [PATCH 205/974] [master] changelog entry for #983 --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 8b597e6d5e..5d27f6e354 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +270. [func] jinmei + Added python bindings for ACLs using the DNS request as the + context. They are accessible via the isc.acl.dns module. + (Trac #983, git c24553e21fe01121a42e2136d0a1230d75812b27) + 269. [bug] y-aharen Modified IntervalTimerTest not to rely on the accuracy of the timer. This fix addresses occasional failure of build tests. From 4d17de950b96631d01c7928b9cab24860b2e29e5 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 14 Jul 2011 09:56:33 +0100 Subject: [PATCH 206/974] [trac1024] Update documentation --- src/lib/log/logger_support.h | 19 ++++++++----------- src/lib/log/logger_unittest_support.h | 13 +++++++++---- src/lib/python/isc/log/log.cc | 9 +++++---- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h index 1c5f8e617f..4ce3cedcd0 100644 --- a/src/lib/log/logger_support.h +++ b/src/lib/log/logger_support.h @@ -19,10 +19,14 @@ #include #include - -// Include the unit test function declarations here for compatibility #include +/// \file +/// \brief Logging initialization functions +/// +/// Contains a set of functions relating to logging initialization that are +/// used by the production code. + namespace isc { namespace log { @@ -36,17 +40,13 @@ namespace log { /// \return true if logging has been initialized, false if not bool isLoggingInitialized(); -/// \brief Set "logging initialized" flag -/// -/// Sets the state of the "logging initialized" flag. +/// \brief Set state of "logging initialized" flag /// /// \param state State to set the flag to. (This is expected to be "true" - the /// default - for all code apart from specific unit tests.) void setLoggingInitialized(bool state = true); - - -/// \brief Run-Time Initialization +/// \brief Run-time initialization /// /// Performs run-time initialization of the logger in particular supplying: /// @@ -65,10 +65,7 @@ void initLogger(const std::string& root, isc::log::Severity severity = isc::log::INFO, int dbglevel = 0, const char* file = NULL); - - } // namespace log } // namespace isc - #endif // __LOGGER_SUPPORT_H diff --git a/src/lib/log/logger_unittest_support.h b/src/lib/log/logger_unittest_support.h index e98544e442..ce9121b486 100644 --- a/src/lib/log/logger_unittest_support.h +++ b/src/lib/log/logger_unittest_support.h @@ -18,7 +18,12 @@ #include #include -// Note: this file holds logging functions used by unit tests. +/// \file +/// \brief Miscellaneous logging functions used by the unit tests. +/// +/// As the configuration database is unsually unavailable during unit tests, +/// the functions defined here allow a limited amount of logging configuration +/// through the use of environment variables namespace isc { namespace log { @@ -71,7 +76,7 @@ void initLogger(isc::log::Severity severity = isc::log::DEBUG, int dbglevel = isc::log::MAX_DEBUG_LEVEL); -/// \brief Logging Severity from B10_LOGGER_SEVERITY +/// \brief Obtains logging severity from B10_LOGGER_SEVERITY /// /// Support function called by the unit test logging initialization code. /// It returns the logging severity defined by B10_LOGGER_SEVERITY. If @@ -84,7 +89,7 @@ void initLogger(isc::log::Severity severity = isc::log::DEBUG, isc::log::Severity b10LoggerSeverity(isc::log::Severity defseverity); -/// \brief Logging Debug Level from B10_LOGGER_DBGLEVEL +/// \brief Obtains logging debug level from B10_LOGGER_DBGLEVEL /// /// Support function called by the unit test logging initialization code. /// It returns the logging debug level defined by B10_LOGGER_DBGLEVEL. If @@ -103,7 +108,7 @@ isc::log::Severity b10LoggerSeverity(isc::log::Severity defseverity); int b10LoggerDbglevel(int defdbglevel); -/// \brief Reset Root Logger Characteristics +/// \brief Reset root logger characteristics /// /// This is a simplified interface into the resetting of the characteristics /// of the root logger. It is aimed for use in unit tests and resets the diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc index ace1f711df..aa1266438a 100644 --- a/src/lib/python/isc/log/log.cc +++ b/src/lib/python/isc/log/log.cc @@ -269,10 +269,11 @@ PyMethodDef methods[] = { "'FATAL'), a debug level (integer in the range 0-99) and a file name " "of a dictionary with message text translations."}, {"resetUnitTestRootLogger", resetUnitTestRootLogger, METH_VARARGS, - "Initialization for unit tests. Sets the severity and output stream " - "according to a set of environment variables. This should not be " - "used in production code. The name is slightly confusing, but it " - "mirrors a method of the same name used for the C++ unit tests."}, + "Resets the configuration of the root logger to that set by the " + "B10_XXX environment variables. It is aimed at unit tests, where " + "the logging is initialized by the code under test; called before " + "the unit test starts, this function resets the logging configuration " + "to that in use for the C++ unit tests."}, {"log_config_update", logConfigUpdate, METH_VARARGS, "Update logger settings. This method is automatically used when " "ModuleCCSession is initialized with handle_logging_config set " From f63ff922e713a04b3f4391d509c2206ac32edbb5 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 14 Jul 2011 16:06:40 +0100 Subject: [PATCH 207/974] [master] ChangeLog for trac1024 --- ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5d27f6e354..81eea322a5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +271. [func] stephen + Default logging for unit tests changed to severity DEBUG (level 99) + with the output routed to /dev/null. This can be altered by setting + the B10_LOGGER_XXX environment variables. + (Trac #1024, git 72a0beb8dfe85b303f546d09986461886fe7a3d8) + 270. [func] jinmei Added python bindings for ACLs using the DNS request as the context. They are accessible via the isc.acl.dns module. From ebc15cde7e0fa14a61127be51267a5ad0c430f90 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 12:55:34 -0700 Subject: [PATCH 208/974] [trac772] fix usual MacOS build failure: add lib/acl/.libs to LIBRARY_PATH_PLACEHOLDER --- src/bin/xfrout/tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index 6ca2b420e1..99f4843f88 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS From 92bf1032800f3365a5d8eb5052a2a045495ca646 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 14:32:39 -0700 Subject: [PATCH 209/974] [trac772] trivial editorial cleanup --- src/bin/xfrout/xfrout.py.in | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 1b71d4756a..02c0ef911b 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -399,7 +399,6 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): self._shutdown_event = shutdown_event self._write_sock, self._read_sock = socket.socketpair() self._common_init() - #self._log = log self.update_config_data(config_data) self._cc = cc @@ -496,7 +495,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): socket """ # This uses a trick. If the socket is IPv4 in reality and we pretend - # it to to be IPv6, it returns IPv4 address anyway. This doesn't seem + # it to be IPv6, it returns IPv4 address anyway. This doesn't seem # to care about the SOCK_STREAM parameter at all (which it really is, # except for testing) if socket.has_ipv6: @@ -614,9 +613,6 @@ class XfroutServer: self._config_data = self._cc.get_full_config() self._cc.start() self._cc.add_remote_config(AUTH_SPECFILE_LOCATION); - #self._log = isc.log.NSLogger(self._config_data.get('log_name'), self._config_data.get('log_file'), - # self._config_data.get('log_severity'), self._config_data.get('log_versions'), - # self._config_data.get('log_max_bytes'), True) self._start_xfr_query_listener() self._start_notifier() From 582348ce86ac20e0bb92079e5f15ba9b05f60a66 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 15:08:40 -0700 Subject: [PATCH 210/974] [trac772] some more trivial editorial fixes --- src/bin/xfrout/tests/xfrout_test.py.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index b20e090629..f881a06dde 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -546,7 +546,6 @@ class MyUnixSockServer(UnixSockServer): self._max_transfers_out = 10 self._cc = MyCCSession() self._common_init() - #self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False ) class TestUnixSockServer(unittest.TestCase): def setUp(self): @@ -592,7 +591,7 @@ class TestUnixSockServer(unittest.TestCase): socket.AI_NUMERICHOST)[0][4]) self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context)) - def test_updata_config_data(self): + def test_update_config_data(self): self.check_default_ACL() tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g==' tsig_key_list = [tsig_key_str] From f52ff519388e7f3ab4e903695b731a2a7000fcf5 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 15:13:41 -0700 Subject: [PATCH 211/974] [trac910] clarified an internal method name with some more comments as suggested in review. --- src/lib/dns/tsig.cc | 10 ++++++---- src/lib/dns/tsig.h | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc index 0472e4b8eb..1bda02105a 100644 --- a/src/lib/dns/tsig.cc +++ b/src/lib/dns/tsig.cc @@ -109,8 +109,10 @@ struct TSIGContext::TSIGContextImpl { // A shortcut method to create an HMAC object for sign/verify. If one // has been successfully created in the constructor, return it; otherwise - // create a new one and return it. - HMACPtr getHMAC() { + // create a new one and return it. In the former case, the ownership is + // transferred to the caller; the stored HMAC will be reset after the + // call. + HMACPtr createHMAC() { if (hmac_) { HMACPtr ret = HMACPtr(); ret.swap(hmac_); @@ -353,7 +355,7 @@ TSIGContext::sign(const uint16_t qid, const void* const data, return (tsig); } - HMACPtr hmac(impl_->getHMAC()); + HMACPtr hmac(impl_->createHMAC()); // If the context has previous MAC (either the Request MAC or its own // previous MAC), digest it. @@ -479,7 +481,7 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data, return (impl_->postVerifyUpdate(error, NULL, 0)); } - HMACPtr hmac(impl_->getHMAC()); + HMACPtr hmac(impl_->createHMAC()); // If the context has previous MAC (either the Request MAC or its own // previous MAC), digest it. diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h index 13d599c308..028d29586c 100644 --- a/src/lib/dns/tsig.h +++ b/src/lib/dns/tsig.h @@ -355,7 +355,7 @@ public: /// Return the expected length of TSIG RR after \c sign() /// - /// This method returns the length of the TSIG RR based that would be + /// This method returns the length of the TSIG RR that would be /// produced as a result of \c sign() with the state of the context /// at the time of the call. The expected length can be decided /// from the key and the algorithm (which determines the MAC size if @@ -363,9 +363,9 @@ public: /// related error has been identified, the MAC will be excluded; if /// a time error has occurred, the TSIG will include "other data". /// - /// This method is provided mainly for the convenient of the Message class, - /// which needs to know the expected TSIG length in rendering a signed - /// DNS message so that it can handle truncated messages with TSIG + /// This method is provided mainly for the convenience of the Message + /// class, which needs to know the expected TSIG length in rendering a + /// signed DNS message so that it can handle truncated messages with TSIG /// correctly. Normal applications wouldn't need this method. The Python /// binding for this method won't be provided for the same reason. /// From d13509441ce77077ccf21b9442458b0fb52b1c07 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 15:26:29 -0700 Subject: [PATCH 212/974] [master] added changelog entry for #910 --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 81eea322a5..ee4db0ef5c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +272. [func] jinmei + libdns++/pydnspp: TSIG signing now handles truncated DNS messages + (i.e. with TC bit on) with TSIG correctly. + (Trac #910, 8e00f359e81c3cb03c5075710ead0f87f87e3220) + 271. [func] stephen Default logging for unit tests changed to severity DEBUG (level 99) with the output routed to /dev/null. This can be altered by setting From a365c21da34b70f50459137ae242767cc336f191 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 14 Jul 2011 16:01:04 -0700 Subject: [PATCH 213/974] [master] added a release marker for the July 2011 release. quickly discussed on jabber. --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index ee4db0ef5c..fc9e8b43a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,8 @@ unit tests. (Trac #1071, git 05164f9d61006869233b498d248486b4307ea8b6) +bind10-devel-20110705 released on July 05, 2011 + 267. [func] tomek Added a dummy module for DHCP6. This module does not actually do anything at this point, and BIND 10 has no option for From 7d85a63f7bd3ef5926b92dd8f7d9c1588cf6e286 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 15 Jul 2011 11:12:46 +0200 Subject: [PATCH 214/974] [master] fix debug call for error responses in auth --- src/bin/auth/auth_srv.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index f29fd05e83..f96e642bef 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -290,7 +290,7 @@ makeErrorMessage(MessagePtr message, OutputBufferPtr buffer, message->toWire(renderer); } LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE) - .arg(message->toText()); + .arg(renderer.getLength()).arg(*message); } } From 1f26ac530c0ca072ff0de69093d38c95b9d3c80a Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 15 Jul 2011 11:33:27 +0200 Subject: [PATCH 215/974] [trac926] call it 'named set', not 'named map' (to avoid confusion with 'normal' maps) --- src/bin/bindctl/bindcmd.py | 4 +- src/bin/xfrin/xfrin.spec | 4 +- src/lib/cc/data.cc | 2 +- src/lib/config/module_spec.cc | 12 +++--- src/lib/config/tests/module_spec_unittests.cc | 2 +- src/lib/config/tests/testdata/data32_1.data | 2 +- src/lib/config/tests/testdata/data32_2.data | 2 +- src/lib/config/tests/testdata/data32_3.data | 2 +- src/lib/config/tests/testdata/spec32.spec | 8 ++-- src/lib/python/isc/config/ccsession.py | 34 ++++++++--------- src/lib/python/isc/config/config_data.py | 38 +++++++++---------- src/lib/python/isc/config/module_spec.py | 10 ++--- .../python/isc/config/tests/ccsession_test.py | 22 +++++------ .../isc/config/tests/config_data_test.py | 38 +++++++++---------- 14 files changed, 90 insertions(+), 90 deletions(-) diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index 85ee4fa84d..598b813fde 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -636,11 +636,11 @@ class BindCmdInterpreter(Cmd): # we have more data to show line += "/" else: - # if type is named_map, don't print value if None + # if type is named_set, don't print value if None # (it is either {} meaning empty, or None, meaning # there actually is data, but not to be shown with # the current command - if value_map['type'] == 'named_map' and\ + if value_map['type'] == 'named_set' and\ value_map['value'] is None: line += "/\t" else: diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec index 23fbfd2e0e..cbe15be9a1 100644 --- a/src/bin/xfrin/xfrin.spec +++ b/src/bin/xfrin/xfrin.spec @@ -48,10 +48,10 @@ } }, { "item_name": "new_zones", - "item_type": "named_map", + "item_type": "named_set", "item_optional": false, "item_default": {}, - "named_map_item_spec": { + "named_set_item_spec": { "item_name": "zone", "item_type": "map", "item_default": {}, diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index 5a14de63de..ffa5346a84 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -511,7 +511,7 @@ Element::nameToType(const std::string& type_name) { return (Element::list); } else if (type_name == "map") { return (Element::map); - } else if (type_name == "named_map") { + } else if (type_name == "named_set") { return (Element::map); } else if (type_name == "null") { return (Element::null); diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc index f84cfca67f..7594458b60 100644 --- a/src/lib/config/module_spec.cc +++ b/src/lib/config/module_spec.cc @@ -71,9 +71,9 @@ check_config_item(ConstElementPtr spec) { if (spec->get("item_type")->stringValue() == "map") { check_leaf_item(spec, "map_item_spec", Element::list, true); check_config_item_list(spec->get("map_item_spec")); - } else if (spec->get("item_type")->stringValue() == "named_map") { - check_leaf_item(spec, "named_map_item_spec", Element::map, true); - check_config_item(spec->get("named_map_item_spec")); + } else if (spec->get("item_type")->stringValue() == "named_set") { + check_leaf_item(spec, "named_set_item_spec", Element::map, true); + check_config_item(spec->get("named_set_item_spec")); } } @@ -290,7 +290,7 @@ check_type(ConstElementPtr spec, ConstElementPtr element) { break; case Element::map: return (cur_item_type == "map" || - cur_item_type == "named_map"); + cur_item_type == "named_set"); break; } return (false); @@ -327,7 +327,7 @@ ModuleSpec::validateItem(ConstElementPtr spec, ConstElementPtr data, } } if (data->getType() == Element::map) { - // either a 'normal' map or a 'named' map + // either a 'normal' map or a 'named' set if (spec->contains("map_item_spec")) { if (!validateSpecList(spec->get("map_item_spec"), data, full, errors)) { return (false); @@ -336,7 +336,7 @@ ModuleSpec::validateItem(ConstElementPtr spec, ConstElementPtr data, typedef std::pair maptype; BOOST_FOREACH(maptype m, data->mapValue()) { - if (!validateItem(spec->get("named_map_item_spec"), m.second, full, errors)) { + if (!validateItem(spec->get("named_set_item_spec"), m.second, full, errors)) { return (false); } } diff --git a/src/lib/config/tests/module_spec_unittests.cc b/src/lib/config/tests/module_spec_unittests.cc index 46f66e45aa..80da86f351 100644 --- a/src/lib/config/tests/module_spec_unittests.cc +++ b/src/lib/config/tests/module_spec_unittests.cc @@ -212,7 +212,7 @@ TEST(ModuleSpec, CommandValidation) { } -TEST(ModuleSpec, NamedMapValidation) { +TEST(ModuleSpec, NamedSetValidation) { ModuleSpec dd = moduleSpecFromFile(specfile("spec32.spec")); ElementPtr errors = Element::createList(); diff --git a/src/lib/config/tests/testdata/data32_1.data b/src/lib/config/tests/testdata/data32_1.data index 670fd52d3b..5695b523a9 100644 --- a/src/lib/config/tests/testdata/data32_1.data +++ b/src/lib/config/tests/testdata/data32_1.data @@ -1,3 +1,3 @@ { - "named_map_item": { "foo": 1, "bar": 2 } + "named_set_item": { "foo": 1, "bar": 2 } } diff --git a/src/lib/config/tests/testdata/data32_2.data b/src/lib/config/tests/testdata/data32_2.data index 0af728eab7..d5b9765ffb 100644 --- a/src/lib/config/tests/testdata/data32_2.data +++ b/src/lib/config/tests/testdata/data32_2.data @@ -1,3 +1,3 @@ { - "named_map_item": { "foo": "wrongtype", "bar": 2 } + "named_set_item": { "foo": "wrongtype", "bar": 2 } } diff --git a/src/lib/config/tests/testdata/data32_3.data b/src/lib/config/tests/testdata/data32_3.data index 5246afa783..85f32feed6 100644 --- a/src/lib/config/tests/testdata/data32_3.data +++ b/src/lib/config/tests/testdata/data32_3.data @@ -1,3 +1,3 @@ { - "named_map_item": [] + "named_set_item": [] } diff --git a/src/lib/config/tests/testdata/spec32.spec b/src/lib/config/tests/testdata/spec32.spec index 46c29c053b..68e774e00a 100644 --- a/src/lib/config/tests/testdata/spec32.spec +++ b/src/lib/config/tests/testdata/spec32.spec @@ -2,12 +2,12 @@ "module_spec": { "module_name": "Spec32", "config_data": [ - { "item_name": "named_map_item", - "item_type": "named_map", + { "item_name": "named_set_item", + "item_type": "named_set", "item_optional": false, "item_default": { "a": 1, "b": 2 }, - "named_map_item_spec": { - "item_name": "named_map_element", + "named_set_item_spec": { + "item_name": "named_set_element", "item_type": "integer", "item_optional": false, "item_default": 3 diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index d05ce40f19..5592f03b0a 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -444,11 +444,11 @@ class UIModuleCCSession(MultiConfigData): else: raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier) - def _add_value_to_named_map(self, identifier, value, item_value): + def _add_value_to_named_set(self, identifier, value, item_value): if value is None: - raise isc.cc.data.DataNotFoundError("Need a name to add a new item to named_map " + str(identifier)) + raise isc.cc.data.DataNotFoundError("Need a name to add a new item to named_set " + str(identifier)) elif type(value) != str: - raise isc.cc.data.DataTypeError("Name for named_map " + identifier + " must be a string") + raise isc.cc.data.DataTypeError("Name for named_set " + identifier + " must be a string") else: cur_map, status = self.get_value(identifier) if not cur_map: @@ -477,16 +477,16 @@ class UIModuleCCSession(MultiConfigData): if value_str is not None: value = isc.cc.data.parse_value_str(value_str) - # the specified element must be a list or a named_map + # the specified element must be a list or a named_set if 'list_item_spec' in module_spec: self._add_value_to_list(identifier, value) - elif 'named_map_item_spec' in module_spec: + elif 'named_set_item_spec' in module_spec: item_value = None - if 'item_default' in module_spec['named_map_item_spec']: - item_value = module_spec['named_map_item_spec']['item_default'] - self._add_value_to_named_map(identifier, value, item_value) + if 'item_default' in module_spec['named_set_item_spec']: + item_value = module_spec['named_set_item_spec']['item_default'] + self._add_value_to_named_set(identifier, value, item_value) else: - raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named map") + raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named set") def _remove_value_from_list(self, identifier, value): if value is None: @@ -504,11 +504,11 @@ class UIModuleCCSession(MultiConfigData): cur_list.remove(value) self.set_value(identifier, cur_list) - def _remove_value_from_named_map(self, identifier, value): + def _remove_value_from_named_set(self, identifier, value): if value is None: - raise isc.cc.data.DataNotFoundError("Need a name to remove an item from named_map " + str(identifier)) + raise isc.cc.data.DataNotFoundError("Need a name to remove an item from named_set " + str(identifier)) elif type(value) != str: - raise isc.cc.data.DataTypeError("Name for named_map " + identifier + " must be a string") + raise isc.cc.data.DataTypeError("Name for named_set " + identifier + " must be a string") else: cur_map, status = self.get_value(identifier) if not cur_map: @@ -516,10 +516,10 @@ class UIModuleCCSession(MultiConfigData): if value in cur_map: del cur_map[value] else: - raise isc.cc.data.DataNotFoundError(value + " not found in named_map " + str(identifier)) + raise isc.cc.data.DataNotFoundError(value + " not found in named_set " + str(identifier)) def remove_value(self, identifier, value_str): - """Remove a value from a configuration list or named map. + """Remove a value from a configuration list or named set. The value string must be a string representation of the full item. Raises a DataTypeError if the value at the identifier is not a list, or if the given value_str does not match the @@ -536,10 +536,10 @@ class UIModuleCCSession(MultiConfigData): if value is not None: isc.config.config_data.check_type(module_spec['list_item_spec'], value) self._remove_value_from_list(identifier, value) - elif 'named_map_item_spec' in module_spec: - self._remove_value_from_named_map(identifier, value) + elif 'named_set_item_spec' in module_spec: + self._remove_value_from_named_set(identifier, value) else: - raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named_map") + raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named_set") diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py index 51aed576d5..eaa8a56e82 100644 --- a/src/lib/python/isc/config/config_data.py +++ b/src/lib/python/isc/config/config_data.py @@ -145,8 +145,8 @@ def _find_spec_part_single(cur_spec, id_part): return cur_spec['list_item_spec'] # not found raise isc.cc.data.DataNotFoundError(id + " not found") - elif type(cur_spec) == dict and 'named_map_item_spec' in cur_spec.keys(): - return cur_spec['named_map_item_spec'] + elif type(cur_spec) == dict and 'named_set_item_spec' in cur_spec.keys(): + return cur_spec['named_set_item_spec'] elif type(cur_spec) == list: for cur_spec_item in cur_spec: if cur_spec_item['item_name'] == id: @@ -193,7 +193,7 @@ def spec_name_list(spec, prefix="", recurse=False): result.extend(spec_name_list(map_el['map_item_spec'], prefix + map_el['item_name'], recurse)) else: result.append(prefix + name) - elif 'named_map_item_spec' in spec: + elif 'named_set_item_spec' in spec: # we added a '/' above, but in this one case we don't want it result.append(prefix[:-1]) pass @@ -419,7 +419,7 @@ class MultiConfigData: id_list = module + "/" + id_prefix + "/" + item_id id_prefix += "/" + id_part part_spec = find_spec_part(self._specifications[module].get_config_spec(), id_prefix) - if part_spec['item_type'] == 'named_map': + if part_spec['item_type'] == 'named_set': if len(id_parts) == 0: if 'item_default' in part_spec: return part_spec['item_default'] @@ -427,21 +427,21 @@ class MultiConfigData: return None id_part = id_parts.pop(0) - named_map_value, type = self.get_value(id_list) - if id_part in named_map_value: + named_set_value, type = self.get_value(id_list) + if id_part in named_set_value: if len(id_parts) > 0: # we are looking for the *default* value. # so if not present in here, we need to # lookup the one from the spec rest_of_id = "/".join(id_parts) - result = isc.cc.data.find_no_exc(named_map_value[id_part], rest_of_id) + result = isc.cc.data.find_no_exc(named_set_value[id_part], rest_of_id) if result is None: spec_part = self.find_spec_part(identifier) if 'item_default' in spec_part: return spec_part['item_default'] return result else: - return named_map_value[id_part] + return named_set_value[id_part] else: return None elif list_indices is not None: @@ -481,9 +481,9 @@ class MultiConfigData: spec = find_spec_part(self._specifications[module].get_config_spec(), id) if 'item_default' in spec: - # one special case, named_map - if spec['item_type'] == 'named_map': - print("is " + id_part + " in named map?") + # one special case, named_set + if spec['item_type'] == 'named_set': + print("is " + id_part + " in named set?") return spec['item_default'] else: return spec['item_default'] @@ -551,7 +551,7 @@ class MultiConfigData: # almost never interested in just its name spec_part_map = spec_part['map_item_spec'] self._append_value_item(result, spec_part_map, identifier, all) - elif item_type == "named_map": + elif item_type == "named_set": value, status = self.get_value(identifier) # show just the one entry, when either the map is empty, @@ -567,10 +567,10 @@ class MultiConfigData: None, status) result.append(entry) else: - spec_part_named_map = spec_part['named_map_item_spec'] + spec_part_named_set = spec_part['named_set_item_spec'] for entry in value: self._append_value_item(result, - spec_part_named_map, + spec_part_named_set, identifier + "/" + entry, all) else: @@ -667,16 +667,16 @@ class MultiConfigData: def _get_list_items(self, item_name): """This method is used in get_config_item_list, to add list - indices and named_map names to the completion list. If - the given item_name is for a list or named_map, it'll + indices and named_set names to the completion list. If + the given item_name is for a list or named_set, it'll return a list of those (appended to item_name), otherwise the list will only contain the item_name itself.""" spec_part = self.find_spec_part(item_name) if 'item_type' in spec_part and \ - spec_part['item_type'] == 'named_map': + spec_part['item_type'] == 'named_set': subslash = "" - if spec_part['named_map_item_spec']['item_type'] == 'map' or\ - spec_part['named_map_item_spec']['item_type'] == 'named_map': + if spec_part['named_set_item_spec']['item_type'] == 'map' or\ + spec_part['named_set_item_spec']['item_type'] == 'named_set': subslash = "/" values, status = self.get_value(item_name) if len(values) > 0: diff --git a/src/lib/python/isc/config/module_spec.py b/src/lib/python/isc/config/module_spec.py index 80c44e59bd..9aa49e03e7 100644 --- a/src/lib/python/isc/config/module_spec.py +++ b/src/lib/python/isc/config/module_spec.py @@ -229,7 +229,7 @@ def _check_item_spec(config_item): item_type = config_item["item_type"] if type(item_type) != str: raise ModuleSpecError("item_type in " + item_name + " is not a string: " + str(type(item_type))) - if item_type not in ["integer", "real", "boolean", "string", "list", "map", "named_map", "any"]: + if item_type not in ["integer", "real", "boolean", "string", "list", "map", "named_set", "any"]: raise ModuleSpecError("unknown item_type in " + item_name + ": " + item_type) if "item_optional" in config_item: if type(config_item["item_optional"]) != bool: @@ -293,7 +293,7 @@ def _validate_type(spec, value, errors): if errors != None: errors.append(str(value) + " should be a map") return False - elif data_type == "named_map" and type(value) != dict: + elif data_type == "named_set" and type(value) != dict: if errors != None: errors.append(str(value) + " should be a map") return False @@ -316,11 +316,11 @@ def _validate_item(spec, full, data, errors): if not _validate_spec_list(spec['map_item_spec'], full, data, errors): return False else: - named_map_spec = spec['named_map_item_spec'] + named_set_spec = spec['named_set_item_spec'] for data_el in data.values(): - if not _validate_type(named_map_spec, data_el, errors): + if not _validate_type(named_set_spec, data_el, errors): return False - if not _validate_item(named_map_spec, full, data_el, errors): + if not _validate_item(named_set_spec, full, data_el, errors): return False return True diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py index 62936be786..9cf1497e81 100644 --- a/src/lib/python/isc/config/tests/ccsession_test.py +++ b/src/lib/python/isc/config/tests/ccsession_test.py @@ -691,7 +691,7 @@ class TestUIModuleCCSession(unittest.TestCase): fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION }) return UIModuleCCSession(fake_conn) - def create_uccs_named_map(self, fake_conn): + def create_uccs_named_set(self, fake_conn): module_spec = isc.config.module_spec_from_file(self.spec_file("spec32.spec")) fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()}) fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION }) @@ -742,26 +742,26 @@ class TestUIModuleCCSession(unittest.TestCase): uccs.remove_value("Spec2/item5[0]", None) self.assertEqual({'Spec2': {'item5': []}}, uccs._local_changes) - def test_add_remove_value_named_map(self): + def test_add_remove_value_named_set(self): fake_conn = fakeUIConn() - uccs = self.create_uccs_named_map(fake_conn) - value, status = uccs.get_value("/Spec32/named_map_item") + uccs = self.create_uccs_named_set(fake_conn) + value, status = uccs.get_value("/Spec32/named_set_item") self.assertEqual({'a': 1, 'b': 2}, value) - uccs.add_value("/Spec32/named_map_item", "foo") - value, status = uccs.get_value("/Spec32/named_map_item") + uccs.add_value("/Spec32/named_set_item", "foo") + value, status = uccs.get_value("/Spec32/named_set_item") self.assertEqual({'a': 1, 'b': 2, 'foo': 3}, value) - uccs.remove_value("/Spec32/named_map_item", "a") - uccs.remove_value("/Spec32/named_map_item", "foo") - value, status = uccs.get_value("/Spec32/named_map_item") + uccs.remove_value("/Spec32/named_set_item", "a") + uccs.remove_value("/Spec32/named_set_item", "foo") + value, status = uccs.get_value("/Spec32/named_set_item") self.assertEqual({'b': 2}, value) self.assertRaises(isc.cc.data.DataNotFoundError, uccs.set_value, - "/Spec32/named_map_item/no_such_item", + "/Spec32/named_set_item/no_such_item", 4) self.assertRaises(isc.cc.data.DataNotFoundError, - uccs.remove_value, "/Spec32/named_map_item", + uccs.remove_value, "/Spec32/named_set_item", "no_such_item") def test_commit(self): diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py index 102bc528b8..4d01de62a1 100644 --- a/src/lib/python/isc/config/tests/config_data_test.py +++ b/src/lib/python/isc/config/tests/config_data_test.py @@ -425,13 +425,13 @@ class TestMultiConfigData(unittest.TestCase): module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec") self.mcd.set_specification(module_spec) - value = self.mcd.get_default_value("Spec32/named_map_item") + value = self.mcd.get_default_value("Spec32/named_set_item") self.assertEqual({ 'a': 1, 'b': 2}, value) - value = self.mcd.get_default_value("Spec32/named_map_item/a") + value = self.mcd.get_default_value("Spec32/named_set_item/a") self.assertEqual(1, value) - value = self.mcd.get_default_value("Spec32/named_map_item/b") + value = self.mcd.get_default_value("Spec32/named_set_item/b") self.assertEqual(2, value) - value = self.mcd.get_default_value("Spec32/named_map_item/no_such_item") + value = self.mcd.get_default_value("Spec32/named_set_item/no_such_item") self.assertEqual(None, value) def test_get_value(self): @@ -557,27 +557,27 @@ class TestMultiConfigData(unittest.TestCase): maps = self.mcd.get_value_maps("/Spec22/value9") self.assertEqual(expected, maps) - def test_get_value_maps_named_map(self): + def test_get_value_maps_named_set(self): module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec") self.mcd.set_specification(module_spec) maps = self.mcd.get_value_maps() self.assertEqual([{'default': False, 'type': 'module', 'name': 'Spec32', 'value': None, 'modified': False}], maps) - maps = self.mcd.get_value_maps("/Spec32/named_map_item") + maps = self.mcd.get_value_maps("/Spec32/named_set_item") self.assertEqual([{'default': True, 'type': 'integer', - 'name': 'Spec32/named_map_item/a', + 'name': 'Spec32/named_set_item/a', 'value': 1, 'modified': False}, {'default': True, 'type': 'integer', - 'name': 'Spec32/named_map_item/b', + 'name': 'Spec32/named_set_item/b', 'value': 2, 'modified': False}], maps) - maps = self.mcd.get_value_maps("/Spec32/named_map_item/a") + maps = self.mcd.get_value_maps("/Spec32/named_set_item/a") self.assertEqual([{'default': True, 'type': 'integer', - 'name': 'Spec32/named_map_item/a', + 'name': 'Spec32/named_set_item/a', 'value': 1, 'modified': False}], maps) - maps = self.mcd.get_value_maps("/Spec32/named_map_item/b") + maps = self.mcd.get_value_maps("/Spec32/named_set_item/b") self.assertEqual([{'default': True, 'type': 'integer', - 'name': 'Spec32/named_map_item/b', + 'name': 'Spec32/named_set_item/b', 'value': 2, 'modified': False}], maps) def test_set_value(self): @@ -618,7 +618,7 @@ class TestMultiConfigData(unittest.TestCase): config_items = self.mcd.get_config_item_list("Spec2", True) self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items) - def test_get_config_item_list_named_map(self): + def test_get_config_item_list_named_set(self): config_items = self.mcd.get_config_item_list() self.assertEqual([], config_items) module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec") @@ -628,12 +628,12 @@ class TestMultiConfigData(unittest.TestCase): config_items = self.mcd.get_config_item_list(None, False) self.assertEqual(['Spec32'], config_items) config_items = self.mcd.get_config_item_list(None, True) - self.assertEqual(['Spec32/named_map_item'], config_items) - self.mcd.set_value('Spec32/named_map_item', { "aaaa": 4, "aabb": 5, "bbbb": 6}) - config_items = self.mcd.get_config_item_list("/Spec32/named_map_item", True) - self.assertEqual(['Spec32/named_map_item/aaaa', - 'Spec32/named_map_item/aabb', - 'Spec32/named_map_item/bbbb', + self.assertEqual(['Spec32/named_set_item'], config_items) + self.mcd.set_value('Spec32/named_set_item', { "aaaa": 4, "aabb": 5, "bbbb": 6}) + config_items = self.mcd.get_config_item_list("/Spec32/named_set_item", True) + self.assertEqual(['Spec32/named_set_item/aaaa', + 'Spec32/named_set_item/aabb', + 'Spec32/named_set_item/bbbb', ], config_items) if __name__ == '__main__': From bb1028fd4f52135f4a2c8175d9bf1b90043df1cc Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 15 Jul 2011 11:51:52 +0200 Subject: [PATCH 216/974] [trac772] Address review comments --- src/bin/xfrout/tests/xfrout_test.py.in | 29 ++++++--------- src/bin/xfrout/xfrout.py.in | 51 ++++++++++++-------------- src/bin/xfrout/xfrout.spec.pre.in | 4 +- src/bin/xfrout/xfrout_messages.mes | 11 ++++-- 4 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index f881a06dde..f8ea9e0d11 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -158,11 +158,18 @@ class TestXfroutSession(unittest.TestCase): # This should be dropped completely, therefore returning None self.xfrsess._remote = ('192.0.2.1', 12345) rcode, msg = self.xfrsess._parse_query_message(self.mdata) - self.assertTrue(rcode is None) - # This should be rejected, therefore NOTAUTH + self.assertEqual(None, rcode) + # This should be refused, therefore NOTAUTH self.xfrsess._remote = ('192.0.2.2', 12345) rcode, msg = self.xfrsess._parse_query_message(self.mdata) self.assertEqual(rcode.to_text(), "REFUSED") + # If the TSIG check fails, it should not check ACL + # (If it checked ACL as well, it would just drop the request) + self.xfrsess._remote = ('192.0.2.1', 12345) + self.xfrsess._tsig_key_ring = TSIGKeyRing() + rcode, msg = self.xfrsess._parse_query_message(request_data) + self.assertEqual(rcode.to_text(), "NOTAUTH") + self.assertTrue(self.xfrsess._tsig_ctx is not None) def test_get_query_zone_name(self): msg = self.getmsg() @@ -222,20 +229,6 @@ class TestXfroutSession(unittest.TestCase): self.assertEqual(msg.get_rcode(), rcode) self.assertTrue(msg.get_header_flag(Message.HEADERFLAG_AA)) - def test_reply_query_with_format_error(self): - msg = self.getmsg() - self.xfrsess._reply_query_with_format_error(msg, self.sock) - get_msg = self.sock.read_msg() - self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR") - - # tsig signed message - msg = self.getmsg() - self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR) - self.xfrsess._reply_query_with_format_error(msg, self.sock) - get_msg = self.sock.read_msg() - self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR") - self.assertTrue(self.message_has_tsig(get_msg)) - def test_create_rrset_from_db_record(self): rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record) self.assertEqual(rrset.get_name().to_text(), "example.com.") @@ -614,13 +607,13 @@ class TestUnixSockServer(unittest.TestCase): self.assertEqual(self.unix.tsig_key_ring.size(), 0) # Load the ACL - self.unix.update_config_data({'ACL': [{'from': '127.0.0.1', + self.unix.update_config_data({'query_acl': [{'from': '127.0.0.1', 'action': 'ACCEPT'}]}) self.check_loaded_ACL() # Pass a wrong data there and check it does not replace the old one self.assertRaises(isc.acl.acl.LoaderError, self.unix.update_config_data, - {'ACL': ['Something bad']}) + {'query_acl': ['Something bad']}) self.check_loaded_ACL() def test_get_db_file(self): diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 02c0ef911b..f69ad9f537 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -144,21 +144,22 @@ class XfroutSession(): # TSIG related checks rcode = self._check_request_tsig(msg, mdata) - # ACL checks - acl_result = self._acl.execute( - isc.acl.dns.RequestContext(self._remote)) - if acl_result == isc.acl.acl.DROP: - logger.info(XFROUT_QUERY_DROPPED, - self._get_query_zone_name(msg), - self._get_query_zone_class(msg), - self._remote[0], self._remote[1]) - return None, None - elif acl_result == isc.acl.acl.REJECT: - logger.info(XFROUT_QUERY_REJECTED, - self._get_query_zone_name(msg), - self._get_query_zone_class(msg), - self._remote[0], self._remote[1]) - return Rcode.REFUSED(), msg + if rcode == Rcode.NOERROR(): + # ACL checks + acl_result = self._acl.execute( + isc.acl.dns.RequestContext(self._remote)) + if acl_result == DROP: + logger.info(XFROUT_QUERY_DROPPED, + self._get_query_zone_name(msg), + self._get_query_zone_class(msg), + self._remote[0], self._remote[1]) + return None, None + elif acl_result == REJECT: + logger.info(XFROUT_QUERY_REJECTED, + self._get_query_zone_name(msg), + self._get_query_zone_class(msg), + self._remote[0], self._remote[1]) + return Rcode.REFUSED(), msg except Exception as err: logger.error(XFROUT_PARSE_QUERY_ERROR, err) @@ -202,18 +203,11 @@ class XfroutSession(): def _reply_query_with_error_rcode(self, msg, sock_fd, rcode_): - msg.make_response() - msg.set_rcode(rcode_) - self._send_message(sock_fd, msg, self._tsig_ctx) - - - def _reply_query_with_format_error(self, msg, sock_fd): - '''query message format isn't legal.''' if not msg: return # query message is invalid. send nothing back. msg.make_response() - msg.set_rcode(Rcode.FORMERR()) + msg.set_rcode(rcode_) self._send_message(sock_fd, msg, self._tsig_ctx) def _zone_has_soa(self, zone): @@ -268,7 +262,8 @@ class XfroutSession(): elif rcode_ == Rcode.NOTAUTH() or rcode_ == Rcode.REFUSED(): return self._reply_query_with_error_rcode(msg, sock_fd, rcode_) elif rcode_ != Rcode.NOERROR(): - return self._reply_query_with_format_error(msg, sock_fd) + return self._reply_query_with_error_rcode(msg, sock_fd, + Rcode.FORMERR()) zone_name = self._get_query_zone_name(msg) zone_class_str = self._get_query_zone_class(msg) @@ -553,9 +548,9 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): def update_config_data(self, new_config): '''Apply the new config setting of xfrout module. ''' - if 'ACL' in new_config: - self._acl = REQUEST_LOADER.load(new_config['ACL']) logger.info(XFROUT_NEW_CONFIG) + if 'query_acl' in new_config: + self._acl = REQUEST_LOADER.load(new_config['query_acl']) self._lock.acquire() self._max_transfers_out = new_config.get('transfers_out') self.set_tsig_key_ring(new_config.get('tsig_key_ring')) @@ -645,7 +640,9 @@ class XfroutServer: try: self._unix_socket_server.update_config_data(self._config_data) except Exception as e: - answer = create_answer(1, "Bad configuration: " + str(e)) + answer = create_answer(1, + "Failed to handle new configuration: " + + str(e)) return answer diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in index 5fad7fd061..71168aab58 100644 --- a/src/bin/xfrout/xfrout.spec.pre.in +++ b/src/bin/xfrout/xfrout.spec.pre.in @@ -51,13 +51,13 @@ } }, { - "item_name": "ACL", + "item_name": "query_acl", "item_type": "list", "item_optional": false, "item_default": [], "list_item_spec": { - "item_name": "ACL_element", + "item_name": "acl_element", "item_type": "any", "item_optional": true } diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes index 7a234d0137..19b104ee7c 100644 --- a/src/bin/xfrout/xfrout_messages.mes +++ b/src/bin/xfrout/xfrout_messages.mes @@ -95,13 +95,16 @@ in the log message, but at this point no specific information other than that could be given. This points to incomplete exception handling in the code. -% XFROUT_QUERY_DROPPED request to transfer %1/%2 to %3:%4 dropped +% XFROUT_QUERY_DROPPED request to transfer %1/%2 to [%3]:%4 dropped The xfrout process silently dropped a request to transfer zone to given host. -This is required by the ACLs. +This is required by the ACLs. The %1 and %2 represent the zone name and class, +the %3 and %4 the IP address and port of the peer requesting the transfer. -% XFROUT_QUERY_REJECTED request to transfer %1/%2 to %3:%4 rejected +% XFROUT_QUERY_REJECTED request to transfer %1/%2 to [%3]:%4 rejected The xfrout process rejected (by REFUSED rcode) a request to transfer zone to -given host. This is because of ACLs. +given host. This is because of ACLs. The %1 and %2 represent the zone name and +class, the %3 and %4 the IP address and port of the peer requesting the +transfer. % XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection There was an error receiving the file descriptor for the transfer From f551799e8c3de59be0a6a7c5168194b93987e876 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 15 Jul 2011 12:06:13 +0200 Subject: [PATCH 217/974] [trac772] Comment update --- src/bin/xfrout/xfrout.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index f69ad9f537..f6731e5815 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -486,7 +486,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): def _guess_remote(self, sock_fd): """ - Guess remote address and port of the socket. The socket must be a + Guess remote address and port of the socket. The sock_fd must be a socket """ # This uses a trick. If the socket is IPv4 in reality and we pretend From e108ea6f210bf93250ad4ea23ac3708e1478946e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 15 Jul 2011 15:43:40 +0200 Subject: [PATCH 218/974] [trac926] support direct addition of values to named set and a few minor style fixes --- src/bin/bindctl/bindcmd.py | 9 ++- src/bin/bindctl/bindctl_main.py.in | 19 ++++-- src/lib/config/tests/module_spec_unittests.cc | 1 - src/lib/python/isc/config/ccsession.py | 62 ++++++++++++------- src/lib/python/isc/config/config_data.py | 2 +- .../isc/config/tests/config_data_test.py | 3 +- 6 files changed, 62 insertions(+), 34 deletions(-) diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index 598b813fde..b6b7238476 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -659,10 +659,9 @@ class BindCmdInterpreter(Cmd): data, default = self.config_data.get_value(identifier) print(json.dumps(data)) elif cmd.command == "add": - if 'value' in cmd.params: - self.config_data.add_value(identifier, cmd.params['value']) - else: - self.config_data.add_value(identifier) + self.config_data.add_value(identifier, + cmd.params.get('value_or_name'), + cmd.params.get('value_for_set')) elif cmd.command == "remove": if 'value' in cmd.params: self.config_data.remove_value(identifier, cmd.params['value']) @@ -686,7 +685,7 @@ class BindCmdInterpreter(Cmd): elif cmd.command == "commit": self.config_data.commit() elif cmd.command == "diff": - print(self.config_data.get_local_changes()); + print(self.config_data.get_local_changes()) elif cmd.command == "go": self.go(identifier) diff --git a/src/bin/bindctl/bindctl_main.py.in b/src/bin/bindctl/bindctl_main.py.in index 01307e9798..ee4191db45 100755 --- a/src/bin/bindctl/bindctl_main.py.in +++ b/src/bin/bindctl/bindctl_main.py.in @@ -50,17 +50,28 @@ def prepare_config_commands(tool): cmd.add_param(param) module.add_command(cmd) - cmd = CommandInfo(name = "add", desc = "Add an entry to configuration list. If no value is given, a default value is added.") + cmd = CommandInfo(name = "add", desc = + "Add an entry to configuration list or a named set. " + "When adding to a list, the command has one optional argument, " + "a value to add to the list. The value must be in correct JSON " + "and complete. When adding to a named set, it has one " + "mandatory parameter (the name to add), and an optional " + "parameter value, similar to when adding to a list. " + "In either case, when no value is given, an entry will be " + "constructed with default values.") param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC) cmd.add_param(param) - param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to add to the list. It must be in correct JSON format and complete.") + param = ParamInfo(name = "value_or_name", type = "string", optional=True, desc = "Specifies a value to add to the list, or the name when adding to a named set. It must be in correct JSON format and complete.") + cmd.add_param(param) + module.add_command(cmd) + param = ParamInfo(name = "value_for_set", type = "string", optional=True, desc = "Specifies an optional value to add to the named map. It must be in correct JSON format and complete.") cmd.add_param(param) module.add_command(cmd) - cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list.") + cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list or named set.") param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC) cmd.add_param(param) - param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to remove from the list. It must be in correct JSON format and complete.") + param = ParamInfo(name = "value", type = "string", optional=True, desc = "When identifier is a list, specifies a value to remove from the list. It must be in correct JSON format and complete. When it is a named set, specifies the name to remove.") cmd.add_param(param) module.add_command(cmd) diff --git a/src/lib/config/tests/module_spec_unittests.cc b/src/lib/config/tests/module_spec_unittests.cc index 80da86f351..d642af8286 100644 --- a/src/lib/config/tests/module_spec_unittests.cc +++ b/src/lib/config/tests/module_spec_unittests.cc @@ -217,7 +217,6 @@ TEST(ModuleSpec, NamedSetValidation) { ElementPtr errors = Element::createList(); EXPECT_TRUE(dataTestWithErrors(dd, "data32_1.data", errors)); - std::cout << "[XX] ERRORS: " << *errors << std::endl; EXPECT_FALSE(dataTest(dd, "data32_2.data")); EXPECT_FALSE(dataTest(dd, "data32_3.data")); } diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index 5592f03b0a..ebdbf1bc60 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -312,7 +312,7 @@ class ModuleCCSession(ConfigData): module_spec = isc.config.module_spec_from_file(spec_file_name) module_cfg = ConfigData(module_spec) module_name = module_spec.get_module_name() - self._session.group_subscribe(module_name); + self._session.group_subscribe(module_name) # Get the current config for that module now seq = self._session.group_sendmsg(create_command(COMMAND_GET_CONFIG, { "module_name": module_name }), "ConfigManager") @@ -327,7 +327,7 @@ class ModuleCCSession(ConfigData): rcode, value = parse_answer(answer) if rcode == 0: if value != None and module_spec.validate_config(False, value): - module_cfg.set_local_config(value); + module_cfg.set_local_config(value) if config_update_callback is not None: config_update_callback(value, module_cfg) @@ -377,7 +377,7 @@ class ModuleCCSession(ConfigData): if self.get_module_spec().validate_config(False, value, errors): - self.set_local_config(value); + self.set_local_config(value) if self._config_handler: self._config_handler(value) else: @@ -415,8 +415,8 @@ class UIModuleCCSession(MultiConfigData): self.set_specification(isc.config.ModuleSpec(specs[module])) def update_specs_and_config(self): - self.request_specifications(); - self.request_current_config(); + self.request_specifications() + self.request_current_config() def request_current_config(self): """Requests the current configuration from the configuration @@ -436,19 +436,27 @@ class UIModuleCCSession(MultiConfigData): value = module_spec["list_item_spec"]["item_default"] if value is None: - raise isc.cc.data.DataNotFoundError("No value given and no default for " + str(identifier)) + raise isc.cc.data.DataNotFoundError( + "No value given and no default for " + str(identifier)) if value not in cur_list: cur_list.append(value) self.set_value(identifier, cur_list) else: - raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier) + raise isc.cc.data.DataAlreadyPresentError(value + + " already in " + + identifier) def _add_value_to_named_set(self, identifier, value, item_value): - if value is None: - raise isc.cc.data.DataNotFoundError("Need a name to add a new item to named_set " + str(identifier)) - elif type(value) != str: - raise isc.cc.data.DataTypeError("Name for named_set " + identifier + " must be a string") + if type(value) != str: + raise isc.cc.data.DataTypeError("Name for named_set " + + identifier + + " must be a string") + # fail on both None and empty string + if not value: + raise isc.cc.data.DataNotFoundError( + "Need a name to add a new item to named_set " + + str(identifier)) else: cur_map, status = self.get_value(identifier) if not cur_map: @@ -457,9 +465,11 @@ class UIModuleCCSession(MultiConfigData): cur_map[value] = item_value self.set_value(identifier, cur_map) else: - raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier) + raise isc.cc.data.DataAlreadyPresentError(value + + " already in " + + identifier) - def add_value(self, identifier, value_str = None): + def add_value(self, identifier, value_str = None, set_value_str = None): """Add a value to a configuration list. Raises a DataTypeError if the value does not conform to the list_item_spec field of the module config data specification. If value_str is @@ -472,19 +482,29 @@ class UIModuleCCSession(MultiConfigData): if module_spec is None: raise isc.cc.data.DataNotFoundError("Unknown item " + str(identifier)) - # Hmm. Do we need to check for duplicates? - value = None - if value_str is not None: - value = isc.cc.data.parse_value_str(value_str) - # the specified element must be a list or a named_set if 'list_item_spec' in module_spec: + value = None + # in lists, we might get the value with spaces, making it + # the third argument. In that case we interpret both as + # one big string meant as the value + if value_str is not None: + if set_value_str is not None: + value_str += set_value_str + value = isc.cc.data.parse_value_str(value_str) self._add_value_to_list(identifier, value) elif 'named_set_item_spec' in module_spec: + item_name = None item_value = None - if 'item_default' in module_spec['named_set_item_spec']: - item_value = module_spec['named_set_item_spec']['item_default'] - self._add_value_to_named_set(identifier, value, item_value) + if value_str is not None: + item_name = isc.cc.data.parse_value_str(value_str) + if set_value_str is not None: + item_value = isc.cc.data.parse_value_str(set_value_str) + else: + if 'item_default' in module_spec['named_set_item_spec']: + item_value = module_spec['named_set_item_spec']['item_default'] + self._add_value_to_named_set(identifier, item_name, + item_value) else: raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named set") diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py index eaa8a56e82..f7f338e266 100644 --- a/src/lib/python/isc/config/config_data.py +++ b/src/lib/python/isc/config/config_data.py @@ -261,7 +261,7 @@ class ConfigData: def get_local_config(self): """Returns the non-default config values in a dict""" - return self.data; + return self.data def get_item_list(self, identifier = None, recurse = False): """Returns a list of strings containing the full identifiers of diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py index 4d01de62a1..0dd441ddcb 100644 --- a/src/lib/python/isc/config/tests/config_data_test.py +++ b/src/lib/python/isc/config/tests/config_data_test.py @@ -361,7 +361,7 @@ class TestMultiConfigData(unittest.TestCase): def test_get_current_config(self): cf = { 'module1': { 'item1': 2, 'item2': True } } - self.mcd._set_current_config(cf); + self.mcd._set_current_config(cf) self.assertEqual(cf, self.mcd.get_current_config()) def test_get_local_changes(self): @@ -411,7 +411,6 @@ class TestMultiConfigData(unittest.TestCase): self.assertEqual('a', value) value = self.mcd.get_default_value("Spec2/item5[1]") self.assertEqual('b', value) - self.assertRaises(self.mcd.get_default_value("Spec2/item5[2]")) value = self.mcd.get_default_value("Spec2/item5[5]") self.assertEqual(None, value) value = self.mcd.get_default_value("Spec2/item5[0][1]") From 686ed44b82c009ddb63ed064d46ce44fcade5fbe Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 15 Jul 2011 11:23:13 -0700 Subject: [PATCH 219/974] [trac772] minor editorial fix. --- src/bin/xfrout/tests/xfrout_test.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index f8ea9e0d11..9492e43731 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -159,7 +159,7 @@ class TestXfroutSession(unittest.TestCase): self.xfrsess._remote = ('192.0.2.1', 12345) rcode, msg = self.xfrsess._parse_query_message(self.mdata) self.assertEqual(None, rcode) - # This should be refused, therefore NOTAUTH + # This should be refused, therefore REFUSED self.xfrsess._remote = ('192.0.2.2', 12345) rcode, msg = self.xfrsess._parse_query_message(self.mdata) self.assertEqual(rcode.to_text(), "REFUSED") From 5471e816ab36a6182b2223dea461fc8d086ed9e7 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 15 Jul 2011 21:07:57 +0200 Subject: [PATCH 220/974] [trac772] More review comments --- src/bin/xfrout/tests/xfrout_test.py.in | 8 +++++++- src/bin/xfrout/xfrout.py.in | 2 +- src/bin/xfrout/xfrout.spec.pre.in | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index f8ea9e0d11..30e1a9c939 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -559,6 +559,12 @@ class TestUnixSockServer(unittest.TestCase): sock.connect(('::1', 12345)) self.assertEqual(('::1', 12345, 0, 0), self.unix._guess_remote(sock.fileno())) + # Try when pretending there's no IPv6 support + xfrout.socket.has_ipv6 = False + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.connect(('127.0.0.1', 12345)) + self.assertEqual(('127.0.0.1', 12345), + self.unix._guess_remote(sock.fileno())) def test_receive_query_message(self): send_msg = b"\xd6=\x00\x00\x00\x01\x00" @@ -572,7 +578,7 @@ class TestUnixSockServer(unittest.TestCase): context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1", 1234, 0, 0, 0, socket.AI_NUMERICHOST)[0][4]) - self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context)) + self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context)) def check_loaded_ACL(self): context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1", diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index f6731e5815..b3ee8beb4e 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -400,7 +400,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): def _common_init(self): self._lock = threading.Lock() self._transfers_counter = 0 - self._acl = REQUEST_LOADER.load("[]") + self._acl = REQUEST_LOADER.load('[{"action": "ACCEPT"}]') def _receive_query_message(self, sock): ''' receive request message from sock''' diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in index 71168aab58..8ecbb0b92b 100644 --- a/src/bin/xfrout/xfrout.spec.pre.in +++ b/src/bin/xfrout/xfrout.spec.pre.in @@ -54,7 +54,7 @@ "item_name": "query_acl", "item_type": "list", "item_optional": false, - "item_default": [], + "item_default": [{"action": "ACCEPT"}], "list_item_spec": { "item_name": "acl_element", From 21b4324449c7091d36fc3e153d3e0f4ea3515278 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Fri, 15 Jul 2011 20:10:22 +0100 Subject: [PATCH 221/974] [trac1003] Prefix program names in spec file with b10- for logging Also needed to initialize logging for zonemgr, else the logging calls within ccsession.cc caused it to fall over with a "logging not initialized" exception. --- src/bin/zonemgr/zonemgr.py.in | 4 + src/lib/config/ccsession.cc | 113 ++++++++++++++++---- src/lib/config/ccsession.h | 2 +- src/lib/config/config_log.h | 8 ++ src/lib/config/config_messages.mes | 25 +++++ src/lib/config/tests/ccsession_unittests.cc | 53 +++++---- 6 files changed, 161 insertions(+), 44 deletions(-) diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index c6e316354b..845190b702 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -38,6 +38,10 @@ from optparse import OptionParser, OptionValueError from isc.config.ccsession import * import isc.util.process +# Initialize logging for called modules. +# TODO: Log messages properly +isc.log.init("b10-zonemgr") + isc.util.process.rename() # If B10_FROM_BUILD is set in the environment, we use data files diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc index 6b094ec8c6..9b809307c2 100644 --- a/src/lib/config/ccsession.cc +++ b/src/lib/config/ccsession.cc @@ -18,12 +18,15 @@ #include #include #include +#include -#include -#include -#include +#include #include +#include +#include #include +#include +#include #include #include @@ -175,6 +178,37 @@ ConstElementPtr getValueOrDefault(ConstElementPtr config_part, } } +// Prefix name with "b10-". +// +// Root logger names are based on the name of the binary they're from (e.g. +// b10-resolver). This, however, is not how they appear internally (in for +// instance bindctl, where a module name is based on what is specified in +// the .spec file (e.g. Resolver)). +// +// This function prefixes the name read in the configuration with 'b10-" and +// leaves the module code as it is. (It is now a required convention that the +// name from the specfile and the actual binary name should match). To take +// account of the use of capital letters in module names in bindctl, the first +// letter of the name read in is lower-cased. +// +// In this way, you configure resolver logging with the name "resolver" and in +// the printed output it becomes "b10-resolver". +// +// To allow for (a) people using b10-resolver in the configuration instead of +// "resolver" and (b) that fact that during the resolution of wildcards in + +// +// \param instring String to prefix. Lowercase the first character and apply +// the prefix. If empty, "b10-" is returned. +std::string +b10Prefix(const std::string& instring) { + std::string result = instring; + if (!result.empty()) { + result[0] = static_cast(tolower(result[0])); + } + return (std::string("b10-") + result); +} + // 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). @@ -215,6 +249,7 @@ readLoggersConf(std::vector& specs, ConstElementPtr logger, const ConfigData& config_data) { + // Read name, adding prefix as required. std::string lname = logger->get("name")->stringValue(); ConstElementPtr severity_el = getValueOrDefault(logger, @@ -247,6 +282,25 @@ readLoggersConf(std::vector& specs, specs.push_back(logger_spec); } +// Copies the map for a logger, changing the of the logger. This is +// used because the logger being copied is "const", but we want to +// change a top-level name, so need to create a new one. + +ElementPtr +copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) { + + ElementPtr new_logger(Element::createMap()); + + // since we'll only be updating one first-level element, + // and we return as const again, a shallow map copy is + // enough + new_logger->setValue(cur_logger->mapValue()); + new_logger->set("name", Element::create(new_name)); + + return (new_logger); +} + + } // end anonymous namespace @@ -259,34 +313,53 @@ getRelatedLoggers(ConstElementPtr loggers) { ElementPtr result = isc::data::Element::createList(); BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { + // Need to add the b10- prefix to names ready from the spec file. const std::string cur_name = cur_logger->get("name")->stringValue(); - if (cur_name == root_name || cur_name.find(root_name + ".") == 0) { - our_names.insert(cur_name); - result->add(cur_logger); + const std::string mod_name = b10Prefix(cur_name); + if (mod_name == root_name || mod_name.find(root_name + ".") == 0) { + + // Note this name so that we don't add a wildcard that matches it. + our_names.insert(mod_name); + + // We want to store the logger with the modified name (i.e. with + // the b10- prefix). As we are dealing with const loggers, we + // store a modified copy of the data. + result->add(copyLogger(cur_logger, mod_name)); + LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_EXPLICIT) + .arg(cur_name); + + } else if (!cur_name.empty() && (cur_name[0] != '*')) { + // Not a wildcard logger and we are ignore it, note the fact. + LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, + CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name); } } - // now find the * names + // Mow find the wildcard names (the one that start with "*"). BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { std::string cur_name = cur_logger->get("name")->stringValue(); // if name is '*', or starts with '*.', replace * with root - // logger name + // logger name. if (cur_name == "*" || cur_name.length() > 1 && cur_name[0] == '*' && cur_name[1] == '.') { - cur_name = root_name + cur_name.substr(1); - // now add it to the result list, but only if a logger with - // that name was not configured explicitely - if (our_names.find(cur_name) == our_names.end()) { - // we substitute the name here already, but as + // Substitute the "*" with the root name + std::string mod_name = cur_name; + mod_name.replace(0, 1, root_name); + + // Mow add it to the result list, but only if a logger with + // that name was not configured explicitly + if (our_names.find(mod_name) == our_names.end()) { + // We substitute the name here already, but as // we are dealing with consts, we copy the data - ElementPtr new_logger(Element::createMap()); - // since we'll only be updating one first-level element, - // and we return as const again, a shallow map copy is - // enough - new_logger->setValue(cur_logger->mapValue()); - new_logger->set("name", Element::create(cur_name)); - result->add(new_logger); + result->add(copyLogger(cur_logger, mod_name)); + LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, + CONFIG_LOG_WILD_MATCH).arg(cur_name); + + } else if (!cur_name.empty() && (cur_name[0] == '*')) { + // Is a wildcard and we are ignoring it. + LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, + CONFIG_LOG_IGNORE_WILD).arg(cur_name); } } } diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h index a39d996a3b..3d3cd41017 100644 --- a/src/lib/config/ccsession.h +++ b/src/lib/config/ccsession.h @@ -380,7 +380,7 @@ default_logconfig_handler(const std::string& module_name, /// - it drops the configuration parts for loggers for other modules /// - it replaces the '*' in the name of the loggers by the name of /// this module, but *only* if the expanded name is not configured -/// explicitely +/// explicitly /// /// Examples: if this is the module b10-resolver, /// For the config names ['*', 'b10-auth'] diff --git a/src/lib/config/config_log.h b/src/lib/config/config_log.h index 006385586b..74e6a8463c 100644 --- a/src/lib/config/config_log.h +++ b/src/lib/config/config_log.h @@ -32,6 +32,14 @@ namespace config { /// space. extern isc::log::Logger config_logger; // isc::config::config_logger is the CONFIG logger +/// \brief Debug Levels +/// +/// Debug levels used in the configuration library +enum { + DBG_CONFIG_PROCESS = 40 // Enumerate configuration elements as they + // ... are processed. +}; + } // namespace config } // namespace isc diff --git a/src/lib/config/config_messages.mes b/src/lib/config/config_messages.mes index 660ab9a126..53cb4101ea 100644 --- a/src/lib/config/config_messages.mes +++ b/src/lib/config/config_messages.mes @@ -37,6 +37,31 @@ 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_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 +logger that matches the logger specification for the program. The logging +configuration for the program will updated with the information. + +% CONFIG_LOG_IGNORE_EXPLICIT ignoring 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 logger. As this does not match the logger specification for the +program, it has been ignored. + +% CONFIG_LOG_IGNORE_WILD ignoring logging configuration for wildcard logger %1 +This is a debug message. When processing the "loggers" part of the +configuration file, the configuration library found the named wildcard +entry (one containing the "*" character) that matched a logger already +matched by an explicitly named entry. The configuration is ignored. + +% CONFIG_LOG_WILD_MATCH will use logging configuration for wildcard logger %1 +This is a debug message. When processing the "loggers" part of +the configuration file, the configuration library found the named +wildcard entry (one containing the "*" character) that matches a logger +specification in the program. The logging configuration for the program +will 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 diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc index 283fcc48fc..bf8cc3dbeb 100644 --- a/src/lib/config/tests/ccsession_unittests.cc +++ b/src/lib/config/tests/ccsession_unittests.cc @@ -21,6 +21,7 @@ #include #include +#include #include @@ -51,9 +52,13 @@ protected: // create a ModuleCCSession, we must set an initial // ok answer. session.getMessages()->add(createAnswer()); + root_name = isc::log::getRootLoggerName(); + } + ~CCSessionTest() { + isc::log::setRootLoggerName(root_name); } - ~CCSessionTest() {} FakeSession session; + std::string root_name; }; TEST_F(CCSessionTest, createAnswer) { @@ -652,40 +657,42 @@ void doRelatedLoggersTest(const char* input, const char* expected) { TEST(LogConfigTest, relatedLoggersTest) { // make sure logger configs for 'other' programs are ignored, // and that * is substituted correctly - // The default root logger name is "bind10" + // We'll use a root logger name of "b10-test". + isc::log::setRootLoggerName("b10-test"); + doRelatedLoggersTest("[{ \"name\": \"other_module\" }]", "[]"); doRelatedLoggersTest("[{ \"name\": \"other_module.somelib\" }]", "[]"); - doRelatedLoggersTest("[{ \"name\": \"bind10_other\" }]", + doRelatedLoggersTest("[{ \"name\": \"test_other\" }]", "[]"); - doRelatedLoggersTest("[{ \"name\": \"bind10_other.somelib\" }]", + doRelatedLoggersTest("[{ \"name\": \"test_other.somelib\" }]", "[]"); doRelatedLoggersTest("[ { \"name\": \"other_module\" }," - " { \"name\": \"bind10\" }]", - "[ { \"name\": \"bind10\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"bind10\" }]", - "[ { \"name\": \"bind10\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"bind10.somelib\" }]", - "[ { \"name\": \"bind10.somelib\" } ]"); + " { \"name\": \"test\" }]", + "[ { \"name\": \"b10-test\" } ]"); + doRelatedLoggersTest("[ { \"name\": \"test\" }]", + "[ { \"name\": \"b10-test\" } ]"); + doRelatedLoggersTest("[ { \"name\": \"test.somelib\" }]", + "[ { \"name\": \"b10-test.somelib\" } ]"); doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" }," - " { \"name\": \"bind10.somelib\" }]", - "[ { \"name\": \"bind10.somelib\" } ]"); + " { \"name\": \"test.somelib\" }]", + "[ { \"name\": \"b10-test.somelib\" } ]"); doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" }," - " { \"name\": \"bind10\" }," - " { \"name\": \"bind10.somelib\" }]", - "[ { \"name\": \"bind10\" }," - " { \"name\": \"bind10.somelib\" } ]"); + " { \"name\": \"test\" }," + " { \"name\": \"test.somelib\" }]", + "[ { \"name\": \"b10-test\" }," + " { \"name\": \"b10-test.somelib\" } ]"); doRelatedLoggersTest("[ { \"name\": \"*\" }]", - "[ { \"name\": \"bind10\" } ]"); + "[ { \"name\": \"b10-test\" } ]"); doRelatedLoggersTest("[ { \"name\": \"*.somelib\" }]", - "[ { \"name\": \"bind10.somelib\" } ]"); + "[ { \"name\": \"b10-test.somelib\" } ]"); doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" }," - " { \"name\": \"bind10\", \"severity\": \"WARN\"}]", - "[ { \"name\": \"bind10\", \"severity\": \"WARN\"} ]"); + " { \"name\": \"test\", \"severity\": \"WARN\"}]", + "[ { \"name\": \"b10-test\", \"severity\": \"WARN\"} ]"); doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" }," " { \"name\": \"some_module\", \"severity\": \"WARN\"}]", - "[ { \"name\": \"bind10\", \"severity\": \"DEBUG\"} ]"); + "[ { \"name\": \"b10-test\", \"severity\": \"DEBUG\"} ]"); // make sure 'bad' things like '*foo.x' or '*lib' are ignored // (cfgmgr should have already caught it in the logconfig plugin @@ -696,8 +703,8 @@ TEST(LogConfigTest, relatedLoggersTest) { "[ ]"); doRelatedLoggersTest("[ { \"name\": \"*foo\" }," " { \"name\": \"*foo.lib\" }," - " { \"name\": \"bind10\" } ]", - "[ { \"name\": \"bind10\" } ]"); + " { \"name\": \"test\" } ]", + "[ { \"name\": \"b10-test\" } ]"); } } From 66ebc54e863f58b86c3ae65ca9f4764906c9a348 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 16 Jul 2011 10:12:47 +0200 Subject: [PATCH 222/974] [772] Small cleanups --- src/bin/xfrout/tests/xfrout_test.py.in | 25 +++++++++++++++---------- src/bin/xfrout/xfrout.py.in | 3 +++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index 5e9076ba64..f1d9cbf9ea 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -555,16 +555,21 @@ class TestUnixSockServer(unittest.TestCase): sock.connect(('127.0.0.1', 12345)) self.assertEqual(('127.0.0.1', 12345), self.unix._guess_remote(sock.fileno())) - sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) - sock.connect(('::1', 12345)) - self.assertEqual(('::1', 12345, 0, 0), - self.unix._guess_remote(sock.fileno())) - # Try when pretending there's no IPv6 support - xfrout.socket.has_ipv6 = False - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.connect(('127.0.0.1', 12345)) - self.assertEqual(('127.0.0.1', 12345), - self.unix._guess_remote(sock.fileno())) + if socket.has_ipv6: + # Don't check IPv6 address on hosts not supporting them + sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + sock.connect(('::1', 12345)) + self.assertEqual(('::1', 12345, 0, 0), + self.unix._guess_remote(sock.fileno())) + # Try when pretending there's no IPv6 support + # (No need to pretend when there's really no IPv6) + xfrout.socket.has_ipv6 = False + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.connect(('127.0.0.1', 12345)) + self.assertEqual(('127.0.0.1', 12345), + self.unix._guess_remote(sock.fileno())) + # Return it back + xfrout.socket.has_ipv6 = True def test_receive_query_message(self): send_msg = b"\xd6=\x00\x00\x00\x01\x00" diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index b3ee8beb4e..4b40fe38c5 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -400,6 +400,9 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): def _common_init(self): self._lock = threading.Lock() self._transfers_counter = 0 + # This default value will probably get overwritten by the (same) + # default value from the spec file. This is here just to make + # sure and to make the default value in tests consistent. self._acl = REQUEST_LOADER.load('[{"action": "ACCEPT"}]') def _receive_query_message(self, sock): From d185c72f14dab4b4ca10dd01e6ea9b7aeb42b2df Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 16 Jul 2011 10:23:47 +0200 Subject: [PATCH 223/974] Changelog --- ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index fc9e8b43a3..9c1926a60c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +273. [func] vorner + It is possible to specify ACL for the xfrout module. It is in the ACL + configuration key and has the usual ACL syntax. It currently supports + only the source address currently. Default ACL accepts everything. + (Trac #772, git 50070c824270d5da1db0b716db73b726d458e9f7) + 272. [func] jinmei libdns++/pydnspp: TSIG signing now handles truncated DNS messages (i.e. with TC bit on) with TSIG correctly. From ae79f5fe81a38b64a541adc67194404de5dc8cc5 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 16 Jul 2011 11:59:11 -0700 Subject: [PATCH 224/974] [master] indentation fix --- ChangeLog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9c1926a60c..cba84f3505 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,8 @@ -273. [func] vorner +273. [func] vorner It is possible to specify ACL for the xfrout module. It is in the ACL configuration key and has the usual ACL syntax. It currently supports only the source address currently. Default ACL accepts everything. - (Trac #772, git 50070c824270d5da1db0b716db73b726d458e9f7) + (Trac #772, git 50070c824270d5da1db0b716db73b726d458e9f7) 272. [func] jinmei libdns++/pydnspp: TSIG signing now handles truncated DNS messages From 1c88cc3b00870a93c01688dd5742f5a19e0d0f76 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 16 Jul 2011 19:21:23 +0000 Subject: [PATCH 225/974] [master] fix xfrout test regression on Solaris: it requires more specific values for socket.getaddrinfo(). commiting and pushing it at my discretion. --- src/bin/xfrout/tests/xfrout_test.py.in | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index a41e0ed4f1..e353a60d30 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -582,17 +582,20 @@ class TestUnixSockServer(unittest.TestCase): def check_default_ACL(self): context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1", - 1234, 0, 0, 0, + 1234, 0, socket.SOCK_DGRAM, + socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0][4]) self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context)) def check_loaded_ACL(self): context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1", - 1234, 0, 0, 0, + 1234, 0, socket.SOCK_DGRAM, + socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0][4]) self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context)) context = isc.acl.dns.RequestContext(socket.getaddrinfo("192.0.2.1", - 1234, 0, 0, 0, + 1234, 0, socket.SOCK_DGRAM, + socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0][4]) self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context)) From 10e4a07adce6af4794166cc783eca4fed188cd42 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 18 Jul 2011 15:57:27 -0700 Subject: [PATCH 226/974] [master] minor editorial fix I happened to notice. So minor, so directly pushing. --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index cba84f3505..e46e872ead 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ 273. [func] vorner It is possible to specify ACL for the xfrout module. It is in the ACL configuration key and has the usual ACL syntax. It currently supports - only the source address currently. Default ACL accepts everything. + only the source address. Default ACL accepts everything. (Trac #772, git 50070c824270d5da1db0b716db73b726d458e9f7) 272. [func] jinmei From 136adbdab133d19bf900036b3786d5f709ab2082 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 18 Jul 2011 16:47:38 -0700 Subject: [PATCH 227/974] [trac1104] added a straightforward NameCheck class --- src/lib/acl/dnsname_check.h | 56 +++++++++++++++++++++ src/lib/acl/tests/dnsname_check_unittest.cc | 49 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/lib/acl/dnsname_check.h create mode 100644 src/lib/acl/tests/dnsname_check_unittest.cc diff --git a/src/lib/acl/dnsname_check.h b/src/lib/acl/dnsname_check.h new file mode 100644 index 0000000000..b70fa1bd88 --- /dev/null +++ b/src/lib/acl/dnsname_check.h @@ -0,0 +1,56 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __DNSNAME_CHECK_H +#define __DNSNAME_CHECK_H 1 + +#include + +#include + +namespace isc { +namespace acl { +namespace dns { + +template +class NameCheck : public Check { +public: + NameCheck(const isc::dns::Name& name) : name_(name) {} + + /// \brief Destructor + virtual ~NameCheck() {} + + /// \brief The check itself + /// + /// Matches the passed argument to the condition stored here. Different + /// specialisations must be provided for different argument types, and the + /// program will fail to compile if a required specialisation is not + /// provided. + /// + /// \param context Information to be matched + virtual bool matches(const Context& context) const; + +private: + const isc::dns::Name name_; +}; + +} // namespace dns +} // namespace acl +} // namespace isc + +#endif // __DNSNAME_CHECK_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/acl/tests/dnsname_check_unittest.cc b/src/lib/acl/tests/dnsname_check_unittest.cc new file mode 100644 index 0000000000..c9c043fa95 --- /dev/null +++ b/src/lib/acl/tests/dnsname_check_unittest.cc @@ -0,0 +1,49 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include + +#include + +using namespace isc::dns; +using namespace isc::acl::dns; + +// Provide a specialization of the DNSNameCheck::matches() method. +namespace isc { +namespace acl { +namespace dns { +template <> +bool NameCheck::matches(const Name& name) const { + return (name_ == name); +} +} // namespace dns +} // namespace acl +} // namespace isc + +namespace { +TEST(DNSNameCheck, match) { + NameCheck check(Name("example.com")); + EXPECT_TRUE(check.matches(Name("example.com"))); + EXPECT_FALSE(check.matches(Name("example.org"))); + + // comparison is case insensitive + EXPECT_TRUE(check.matches(Name("EXAMPLE.COM"))); + + // this is exact match. so super/sub domains don't match + EXPECT_FALSE(check.matches(Name("org"))); + EXPECT_FALSE(check.matches(Name("www.example.com"))); +} +} // Unnamed namespace From fafb108c231295b40b7b0d0ea86caff5031a0c30 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 18 Jul 2011 18:15:12 -0700 Subject: [PATCH 228/974] [trac1104] extended RequestContext to support TSIG keys. implemented NameCheck::matches using that support. --- src/lib/acl/dns.cc | 22 +++++- src/lib/acl/dns.h | 14 +++- src/lib/acl/dnsname_check.h | 2 + src/lib/acl/tests/Makefile.am | 1 + src/lib/acl/tests/dns_test.cc | 86 ++++++++++++++++++--- src/lib/acl/tests/dnsname_check_unittest.cc | 10 +++ 6 files changed, 120 insertions(+), 15 deletions(-) diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index cb948ebb4b..679c709304 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -20,6 +20,9 @@ #include +#include +#include + #include #include @@ -29,6 +32,7 @@ using namespace std; using boost::shared_ptr; +using namespace isc::dns; using namespace isc::data; namespace isc { @@ -39,9 +43,6 @@ namespace acl { /// It returns \c true if the remote (source) IP address of the request /// matches the expression encapsulated in the \c IPCheck, and returns /// \c false if not. -/// -/// \note The match logic is expected to be extended as we add -/// more match parameters (at least there's a plan for TSIG key). template <> bool IPCheck::matches( @@ -53,6 +54,16 @@ IPCheck::matches( namespace dns { +/// The specialization of \c NameCheck for access control with +/// \c RequestContext. +/// +/// TBD +template<> +bool +NameCheck::matches(const RequestContext& request) const { + return (request.tsig != NULL && request.tsig->getName() == name_); +} + vector internal::RequestCheckCreator::names() const { // Probably we should eventually build this vector in a more @@ -60,6 +71,7 @@ internal::RequestCheckCreator::names() const { // everything. vector supported_names; supported_names.push_back("from"); + supported_names.push_back("key"); return (supported_names); } @@ -77,6 +89,10 @@ internal::RequestCheckCreator::create(const string& name, if (name == "from") { return (shared_ptr( new internal::RequestIPCheck(definition->stringValue()))); + } else if (name == "key") { + return (shared_ptr( + new internal::RequestKeyCheck( + Name(definition->stringValue())))); } else { // This case shouldn't happen (normally) as it should have been // rejected at the loader level. But we explicitly catch the case diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h index 118e5fda24..d41c2b12b7 100644 --- a/src/lib/acl/dns.h +++ b/src/lib/acl/dns.h @@ -23,9 +23,13 @@ #include #include +#include #include namespace isc { +namespace dns { +class TSIGRecord; +} namespace acl { namespace dns { @@ -68,8 +72,10 @@ struct RequestContext { /// \exception None /// /// \parameter remote_address_param The remote IP address - explicit RequestContext(const IPAddress& remote_address_param) : - remote_address(remote_address_param) + explicit RequestContext(const IPAddress& remote_address_param, + const isc::dns::TSIGRecord* tsig_param) : + remote_address(remote_address_param), + tsig(tsig_param) {} /// @@ -83,6 +89,9 @@ struct RequestContext { //@{ /// \brief The remote IP address (eg. the client's IP address). const IPAddress& remote_address; + + /// TBD + const isc::dns::TSIGRecord* const tsig; //@} }; @@ -114,6 +123,7 @@ namespace internal { // Shortcut typedef typedef isc::acl::IPCheck RequestIPCheck; +typedef isc::acl::dns::NameCheck RequestKeyCheck; class RequestCheckCreator : public acl::Loader::CheckCreator { public: diff --git a/src/lib/acl/dnsname_check.h b/src/lib/acl/dnsname_check.h index b70fa1bd88..30ee6c6f0b 100644 --- a/src/lib/acl/dnsname_check.h +++ b/src/lib/acl/dnsname_check.h @@ -41,6 +41,8 @@ public: /// \param context Information to be matched virtual bool matches(const Context& context) const; + const isc::dns::Name& getName() const { return (name_); } + private: const isc::dns::Name name_; }; diff --git a/src/lib/acl/tests/Makefile.am b/src/lib/acl/tests/Makefile.am index ce1aec5dcc..a08c99cd70 100644 --- a/src/lib/acl/tests/Makefile.am +++ b/src/lib/acl/tests/Makefile.am @@ -16,6 +16,7 @@ run_unittests_SOURCES += acl_test.cc run_unittests_SOURCES += check_test.cc run_unittests_SOURCES += dns_test.cc run_unittests_SOURCES += ip_check_unittest.cc +run_unittests_SOURCES += dnsname_check_unittest.cc run_unittests_SOURCES += loader_test.cc run_unittests_SOURCES += logcheck.h run_unittests_SOURCES += creators.h diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc index 3a42af0355..b3ddbf43c0 100644 --- a/src/lib/acl/tests/dns_test.cc +++ b/src/lib/acl/tests/dns_test.cc @@ -23,6 +23,11 @@ #include +#include +#include +#include +#include + #include #include #include @@ -35,6 +40,8 @@ using namespace std; using boost::scoped_ptr; +using namespace isc::dns; +using namespace isc::dns::rdata; using namespace isc::data; using namespace isc::acl; using namespace isc::acl::dns; @@ -64,8 +71,10 @@ protected: }; TEST_F(RequestCheckCreatorTest, names) { - ASSERT_EQ(1, creator_.names().size()); - EXPECT_EQ("from", creator_.names()[0]); + const vector names = creator_.names(); + EXPECT_EQ(2, names.size()); + EXPECT_TRUE(find(names.begin(), names.end(), "from") != names.end()); + EXPECT_TRUE(find(names.begin(), names.end(), "key") != names.end()); } TEST_F(RequestCheckCreatorTest, allowListAbbreviation) { @@ -93,11 +102,11 @@ TEST_F(RequestCheckCreatorTest, createIPv6Check) { check_ = creator_.create("from", Element::fromJSON("\"2001:db8::5300/120\""), getRequestLoader()); - const dns::internal::RequestIPCheck& ipcheck_ = + const dns::internal::RequestIPCheck& ipcheck = dynamic_cast(*check_); - EXPECT_EQ(AF_INET6, ipcheck_.getFamily()); - EXPECT_EQ(120, ipcheck_.getPrefixlen()); - const vector check_address(ipcheck_.getAddress()); + EXPECT_EQ(AF_INET6, ipcheck.getFamily()); + EXPECT_EQ(120, ipcheck.getPrefixlen()); + const vector check_address(ipcheck.getAddress()); ASSERT_EQ(16, check_address.size()); const uint8_t expected_address[] = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -106,6 +115,14 @@ TEST_F(RequestCheckCreatorTest, createIPv6Check) { expected_address)); } +TEST_F(RequestCheckCreatorTest, createTSIGKeyCheck) { + check_ = creator_.create("key", Element::fromJSON("\"key.example.com\""), + getRequestLoader()); + const dns::internal::RequestKeyCheck& keycheck = + dynamic_cast(*check_); + EXPECT_EQ(Name("key.example.com"), keycheck.getName()); +} + TEST_F(RequestCheckCreatorTest, badCreate) { // Invalid name EXPECT_THROW(creator_.create("bad", Element::fromJSON("\"192.0.2.1\""), @@ -118,12 +135,23 @@ TEST_F(RequestCheckCreatorTest, badCreate) { EXPECT_THROW(creator_.create("from", Element::fromJSON("[]"), getRequestLoader()), isc::data::TypeError); + EXPECT_THROW(creator_.create("key", Element::fromJSON("1"), + getRequestLoader()), + isc::data::TypeError); + EXPECT_THROW(creator_.create("key", Element::fromJSON("{}"), + getRequestLoader()), + isc::data::TypeError); // Syntax error for IPCheck EXPECT_THROW(creator_.create("from", Element::fromJSON("\"bad\""), getRequestLoader()), isc::InvalidParameter); + // Syntax error for Name (key) Check + EXPECT_THROW(creator_.create("key", Element::fromJSON("\"bad..name\""), + getRequestLoader()), + EmptyLabel); + // NULL pointer EXPECT_THROW(creator_.create("from", ConstElementPtr(), getRequestLoader()), LoaderError); @@ -140,23 +168,43 @@ protected: getRequestLoader())); } + // A helper shortcut to create a single Name (key) check for the given + // name. + ConstRequestCheckPtr createKeyCheck(const string& key_name) { + return (creator_.create("key", Element::fromJSON( + string("\"") + key_name + string("\"")), + getRequestLoader())); + } + // create a one time request context for a specific test. Note that // getSockaddr() uses a static storage, so it cannot be called more than // once in a single test. - const dns::RequestContext& getRequest4() { + const dns::RequestContext& getRequest4(const TSIGRecord* tsig = NULL) { ipaddr.reset(new IPAddress(tests::getSockAddr("192.0.2.1"))); - request.reset(new dns::RequestContext(*ipaddr)); + request.reset(new dns::RequestContext(*ipaddr, tsig)); return (*request); } - const dns::RequestContext& getRequest6() { + const dns::RequestContext& getRequest6(const TSIGRecord* tsig = NULL) { ipaddr.reset(new IPAddress(tests::getSockAddr("2001:db8::1"))); - request.reset(new dns::RequestContext(*ipaddr)); + request.reset(new dns::RequestContext(*ipaddr, tsig)); return (*request); } + // create a one time TSIG Record for a specific test. The only parameter + // of the record that matters is the key name; others are hardcoded with + // arbitrarily chosen values. + const TSIGRecord* getTSIGRecord(const string& key_name) { + tsig_rdata.reset(new any::TSIG(TSIGKey::HMACMD5_NAME(), 0, 0, 0, NULL, + 0, 0, 0, NULL)); + tsig.reset(new TSIGRecord(Name(key_name), *tsig_rdata)); + return (tsig.get()); + } + private: scoped_ptr ipaddr; scoped_ptr request; + scoped_ptr tsig_rdata; + scoped_ptr tsig; dns::internal::RequestCheckCreator creator_; }; @@ -184,6 +232,24 @@ TEST_F(RequestCheckTest, checkIPv6) { EXPECT_FALSE(createIPCheck("32.1.13.184")->matches(getRequest6())); } +TEST_F(RequestCheckTest, checkTSIGKey) { + EXPECT_TRUE(createKeyCheck("key.example.com")->matches( + getRequest4(getTSIGRecord("key.example.com")))); + EXPECT_FALSE(createKeyCheck("key.example.com")->matches( + getRequest4(getTSIGRecord("badkey.example.com")))); + + // Same for IPv6 (which shouldn't matter) + EXPECT_TRUE(createKeyCheck("key.example.com")->matches( + getRequest6(getTSIGRecord("key.example.com")))); + EXPECT_FALSE(createKeyCheck("key.example.com")->matches( + getRequest6(getTSIGRecord("badkey.example.com")))); + + // by default the test request doesn't have a TSIG key, which shouldn't + // match any key checks. + EXPECT_FALSE(createKeyCheck("key.example.com")->matches(getRequest4())); + EXPECT_FALSE(createKeyCheck("key.example.com")->matches(getRequest6())); +} + // The following tests test only the creators are registered, they are tested // elsewhere diff --git a/src/lib/acl/tests/dnsname_check_unittest.cc b/src/lib/acl/tests/dnsname_check_unittest.cc index c9c043fa95..0f6ffd088e 100644 --- a/src/lib/acl/tests/dnsname_check_unittest.cc +++ b/src/lib/acl/tests/dnsname_check_unittest.cc @@ -34,6 +34,16 @@ bool NameCheck::matches(const Name& name) const { } // namespace isc namespace { +TEST(DNSNameCheck, construct) { + EXPECT_EQ(Name("example.com"), + NameCheck(Name("example.com")).getName()); + + // Construct the same check with an explicit trailing dot. Should result + // in the same result. + EXPECT_EQ(Name("example.com"), + NameCheck(Name("example.com.")).getName()); +} + TEST(DNSNameCheck, match) { NameCheck check(Name("example.com")); EXPECT_TRUE(check.matches(Name("example.com"))); From 42017c858f5e08f1544620342404904c36d12625 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 18 Jul 2011 18:19:43 -0700 Subject: [PATCH 229/974] [trac1104] adjusted resolver interface to use TSIG for ACL. right now it's always NULL because it cannot be configured with TSIG keys. it should be done in a separate ticket. --- src/bin/resolver/resolver.cc | 3 ++- src/bin/resolver/tests/resolver_config_unittest.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc index fb9621b6dd..6af383ad08 100644 --- a/src/bin/resolver/resolver.cc +++ b/src/bin/resolver/resolver.cc @@ -520,7 +520,8 @@ ResolverImpl::processNormalQuery(const IOMessage& io_message, const Client client(io_message); const BasicAction query_action( getQueryACL().execute(acl::dns::RequestContext( - client.getRequestSourceIPAddress()))); + client.getRequestSourceIPAddress(), + query_message->getTSIGRecord()))); if (query_action == isc::acl::REJECT) { LOG_INFO(resolver_logger, RESOLVER_QUERY_REJECTED) .arg(question->getName()).arg(qtype).arg(qclass).arg(client); diff --git a/src/bin/resolver/tests/resolver_config_unittest.cc b/src/bin/resolver/tests/resolver_config_unittest.cc index 698e535417..c089041d4e 100644 --- a/src/bin/resolver/tests/resolver_config_unittest.cc +++ b/src/bin/resolver/tests/resolver_config_unittest.cc @@ -72,7 +72,8 @@ protected: IOSocket::getDummyUDPSocket(), *endpoint)); client.reset(new Client(*query_message)); - request.reset(new RequestContext(client->getRequestSourceIPAddress())); + request.reset(new RequestContext(client->getRequestSourceIPAddress(), + NULL)); return (*request); } void invalidTest(const string &JSON, const string& name); From eff38a97dea5a54b7a9f3e1213cd5e8b2b15be37 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Tue, 19 Jul 2011 12:07:33 +0100 Subject: [PATCH 230/974] [trac1003] Fix typos and update comments --- src/lib/config/ccsession.cc | 17 ++++++++++------- src/lib/config/ccsession.h | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc index 9b809307c2..9f4a5c63a4 100644 --- a/src/lib/config/ccsession.cc +++ b/src/lib/config/ccsession.cc @@ -329,7 +329,7 @@ getRelatedLoggers(ConstElementPtr loggers) { .arg(cur_name); } else if (!cur_name.empty() && (cur_name[0] != '*')) { - // Not a wildcard logger and we are ignore it, note the fact. + // Not a wildcard logger and we are ignoring it. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name); } @@ -338,7 +338,7 @@ getRelatedLoggers(ConstElementPtr loggers) { // Mow find the wildcard names (the one that start with "*"). BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { std::string cur_name = cur_logger->get("name")->stringValue(); - // if name is '*', or starts with '*.', replace * with root + // If name is '*', or starts with '*.', replace * with root // logger name. if (cur_name == "*" || cur_name.length() > 1 && cur_name[0] == '*' && cur_name[1] == '.') { @@ -347,17 +347,20 @@ getRelatedLoggers(ConstElementPtr loggers) { std::string mod_name = cur_name; mod_name.replace(0, 1, root_name); - // Mow add it to the result list, but only if a logger with - // that name was not configured explicitly + // Now add it to the result list, but only if a logger with + // that name was not configured explicitly. if (our_names.find(mod_name) == our_names.end()) { - // We substitute the name here already, but as - // we are dealing with consts, we copy the data + + // We substitute the name here, but as we are dealing with + // consts, we need to copy the data. result->add(copyLogger(cur_logger, mod_name)); LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_WILD_MATCH).arg(cur_name); } else if (!cur_name.empty() && (cur_name[0] == '*')) { - // Is a wildcard and we are ignoring it. + // Is a wildcard and we are ignoring it (because the wildcard + // expands to a specification that we already encountered when + // processing explicit names). LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_IGNORE_WILD).arg(cur_name); } diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h index 3d3cd41017..50bb65c83c 100644 --- a/src/lib/config/ccsession.h +++ b/src/lib/config/ccsession.h @@ -377,10 +377,10 @@ default_logconfig_handler(const std::string& module_name, /// \brief Returns the loggers related to this module /// /// This function does two things; -/// - it drops the configuration parts for loggers for other modules +/// - it drops the configuration parts for loggers for other modules. /// - it replaces the '*' in the name of the loggers by the name of /// this module, but *only* if the expanded name is not configured -/// explicitly +/// explicitly. /// /// Examples: if this is the module b10-resolver, /// For the config names ['*', 'b10-auth'] From 640a5da59304684d4fe304f2616e8dcf9f234d41 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 14 Jul 2011 14:56:17 +0900 Subject: [PATCH 231/974] [trac1021] apply same patch to other lines which use xml.etree.ElementTree.tostring --- src/bin/stats/stats_httpd.py.in | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index ea68e7d122..74298cf288 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -385,7 +385,14 @@ class StatsHttpd: annotation.append(documentation) element.append(annotation) xsd_root.append(element) - xsd_string = xml.etree.ElementTree.tostring(xsd_root) + # The coding conversion is tricky. xml..tostring() of Python 3.2 + # returns bytes (not string) regardless of the coding, while + # tostring() of Python 3.1 returns a string. To support both + # cases transparently, we first make sure tostring() returns + # bytes by specifying utf-8 and then convert the result to a + # plain string (code below assume it). + xsd_string = str(xml.etree.ElementTree.tostring(xsd_root, encoding='utf-8'), + encoding='us-ascii') self.xsd_body = self.open_template(XSD_TEMPLATE_LOCATION).substitute( xsd_string=xsd_string, xsd_namespace=XSD_NAMESPACE @@ -410,7 +417,14 @@ class StatsHttpd: tr.append(td1) tr.append(td2) xsd_root.append(tr) - xsl_string = xml.etree.ElementTree.tostring(xsd_root) + # The coding conversion is tricky. xml..tostring() of Python 3.2 + # returns bytes (not string) regardless of the coding, while + # tostring() of Python 3.1 returns a string. To support both + # cases transparently, we first make sure tostring() returns + # bytes by specifying utf-8 and then convert the result to a + # plain string (code below assume it). + xsl_string = str(xml.etree.ElementTree.tostring(xsd_root, encoding='utf-8'), + encoding='us-ascii') self.xsl_body = self.open_template(XSL_TEMPLATE_LOCATION).substitute( xsl_string=xsl_string, xsd_namespace=XSD_NAMESPACE) From 687c2d4bdc959da433c141d920820875b701c2be Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 14 Jul 2011 14:59:35 +0900 Subject: [PATCH 232/974] [trac1021] add unittests for xml_handler, xsd_handler and xsl_handler respectively --- src/bin/stats/tests/b10-stats-httpd_test.py | 89 +++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index a7f83a5fa9..6d72dc2f38 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -402,6 +402,95 @@ class TestStatsHttpd(unittest.TestCase): ) self.assertEqual(ret, 1) + def test_xml_handler(self): + orig_get_stats_data = stats_httpd.StatsHttpd.get_stats_data + stats_httpd.StatsHttpd.get_stats_data = lambda x: {'foo':'bar'} + xml_body1 = stats_httpd.StatsHttpd().open_template( + stats_httpd.XML_TEMPLATE_LOCATION).substitute( + xml_string='bar', + xsd_namespace=stats_httpd.XSD_NAMESPACE, + xsd_url_path=stats_httpd.XSD_URL_PATH, + xsl_url_path=stats_httpd.XSL_URL_PATH) + xml_body2 = stats_httpd.StatsHttpd().xml_handler() + self.assertEqual(type(xml_body1), str) + self.assertEqual(type(xml_body2), str) + self.assertEqual(xml_body1, xml_body2) + stats_httpd.StatsHttpd.get_stats_data = lambda x: {'bar':'foo'} + xml_body2 = stats_httpd.StatsHttpd().xml_handler() + self.assertNotEqual(xml_body1, xml_body2) + stats_httpd.StatsHttpd.get_stats_data = orig_get_stats_data + + def test_xsd_handler(self): + orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec + stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ + [{ + "item_name": "foo", + "item_type": "string", + "item_optional": False, + "item_default": "bar", + "item_description": "foo is bar", + "item_title": "Foo" + }] + xsd_body1 = stats_httpd.StatsHttpd().open_template( + stats_httpd.XSD_TEMPLATE_LOCATION).substitute( + xsd_string='' \ + + '' \ + + 'Foo' \ + + 'foo is bar' \ + + '', + xsd_namespace=stats_httpd.XSD_NAMESPACE) + xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() + self.assertEqual(type(xsd_body1), str) + self.assertEqual(type(xsd_body2), str) + self.assertEqual(xsd_body1, xsd_body2) + stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ + [{ + "item_name": "bar", + "item_type": "string", + "item_optional": False, + "item_default": "foo", + "item_description": "bar is foo", + "item_title": "bar" + }] + xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() + self.assertNotEqual(xsd_body1, xsd_body2) + stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec + + def test_xsl_handler(self): + orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec + stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ + [{ + "item_name": "foo", + "item_type": "string", + "item_optional": False, + "item_default": "bar", + "item_description": "foo is bar", + "item_title": "Foo" + }] + xsl_body1 = stats_httpd.StatsHttpd().open_template( + stats_httpd.XSL_TEMPLATE_LOCATION).substitute( + xsl_string='' \ + + 'Foo' \ + + '' \ + + '', + xsd_namespace=stats_httpd.XSD_NAMESPACE) + xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() + self.assertEqual(type(xsl_body1), str) + self.assertEqual(type(xsl_body2), str) + self.assertEqual(xsl_body1, xsl_body2) + stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ + [{ + "item_name": "bar", + "item_type": "string", + "item_optional": False, + "item_default": "foo", + "item_description": "bar is foo", + "item_title": "bar" + }] + xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() + self.assertNotEqual(xsl_body1, xsl_body2) + stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec + def test_for_without_B10_FROM_SOURCE(self): # just lets it go through the code without B10_FROM_SOURCE env # variable From 486bf91e0ecc5fbecfe637e1e75ebe373d42509b Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 14 Jul 2011 16:55:54 +0900 Subject: [PATCH 233/974] [trac1021] add a proposed entry to ChangeLog --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index e46e872ead..1b325021d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +xxx. [bug] naokikambe + add unittests for functions xml_handler, xsd_handler and xsl_handler + respectively to make sure their behaviors are correct, regardless of + whether type which xml.etree.ElementTree.tostring() after Python3.2 + returns is str or byte. + (Trac #1021, git xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) + 273. [func] vorner It is possible to specify ACL for the xfrout module. It is in the ACL configuration key and has the usual ACL syntax. It currently supports From 902ad260c2399b597fe22bba461481a09502b9d5 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 19 Jul 2011 21:18:31 +0900 Subject: [PATCH 234/974] [master] update the latest entry for trac1021 --- ChangeLog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1b325021d1..8f86551d31 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,9 @@ -xxx. [bug] naokikambe +274. [bug] naokikambe add unittests for functions xml_handler, xsd_handler and xsl_handler respectively to make sure their behaviors are correct, regardless of whether type which xml.etree.ElementTree.tostring() after Python3.2 returns is str or byte. - (Trac #1021, git xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) + (Trac #1021, git 486bf91e0ecc5fbecfe637e1e75ebe373d42509b) 273. [func] vorner It is possible to specify ACL for the xfrout module. It is in the ACL From 26e04c45efa440353cd75365c499fc06ba1eb4ea Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 19 Jul 2011 11:13:14 -0700 Subject: [PATCH 235/974] [trac1104] pre-work refactoring: move the actual C++ binding of the DNS ACL to a "hidden" _dns module with a frontend .py module. It will make it easier to handle objects defined in pydnspp. no functional change at this moment. note that we need to allow classes in the module to be subclassed. --- src/lib/python/isc/acl/_dns.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/lib/python/isc/acl/_dns.py diff --git a/src/lib/python/isc/acl/_dns.py b/src/lib/python/isc/acl/_dns.py new file mode 100644 index 0000000000..3bef6e369b --- /dev/null +++ b/src/lib/python/isc/acl/_dns.py @@ -0,0 +1,33 @@ +# 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 file is not installed. The log.so is installed into the right place. +# It is only to find it in the .libs directory when we run as a test or +# from the build directory. +# But as nobody gives us the builddir explicitly (and we can't use generation +# from .in file, as it would put us into the builddir and we wouldn't be found) +# we guess from current directory. Any idea for something better? This should +# be enough for the tests, but would it work for B10_FROM_SOURCE as well? +# Should we look there? Or define something in bind10_config? + +import os +import sys + +for base in sys.path[:]: + bindingdir = os.path.join(base, 'isc/acl/.libs') + if os.path.exists(bindingdir): + sys.path.insert(0, bindingdir) + +from _dns import * From ae1cf18d06bfc92ba1803ad8bb7c90be844f491e Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 19 Jul 2011 15:16:18 -0700 Subject: [PATCH 236/974] [trac1104] allowed python binding to have TSIG in RequestContext. --- src/lib/python/isc/acl/Makefile.am | 22 +-- src/lib/python/isc/acl/dns.cc | 4 +- src/lib/python/isc/acl/dns.py | 70 +++++++--- .../python/isc/acl/dns_requestacl_python.cc | 4 +- .../isc/acl/dns_requestcontext_python.cc | 125 +++++++++++++----- .../isc/acl/dns_requestloader_python.cc | 4 +- src/lib/python/isc/acl/tests/Makefile.am | 2 +- src/lib/python/isc/acl/tests/dns_test.py | 87 +++++++++++- 8 files changed, 249 insertions(+), 69 deletions(-) diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index cabc0a30cc..9e6b40a8da 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -4,10 +4,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CXXFLAGS = $(B10_CXXFLAGS) -python_PYTHON = __init__.py +python_PYTHON = __init__.py dns.py pythondir = $(PYTHON_SITEPKG_DIR)/isc/acl -pyexec_LTLIBRARIES = acl.la dns.la +pyexec_LTLIBRARIES = acl.la _dns.la pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/acl acl_la_SOURCES = acl.cc @@ -15,14 +15,14 @@ acl_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) acl_la_LDFLAGS = $(PYTHON_LDFLAGS) acl_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) -dns_la_SOURCES = dns.h dns.cc dns_requestacl_python.h dns_requestacl_python.cc -dns_la_SOURCES += dns_requestcontext_python.h dns_requestcontext_python.cc -dns_la_SOURCES += dns_requestloader_python.h dns_requestloader_python.cc -dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) -dns_la_LDFLAGS = $(PYTHON_LDFLAGS) +_dns_la_SOURCES = dns.h dns.cc dns_requestacl_python.h dns_requestacl_python.cc +_dns_la_SOURCES += dns_requestcontext_python.h dns_requestcontext_python.cc +_dns_la_SOURCES += dns_requestloader_python.h dns_requestloader_python.cc +_dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) +_dns_la_LDFLAGS = $(PYTHON_LDFLAGS) # Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be # placed after -Wextra defined in AM_CXXFLAGS -dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) +_dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) # Python prefers .so, while some OSes (specifically MacOS) use a different # suffix for dynamic objects. -module is necessary to work this around. @@ -30,9 +30,9 @@ acl_la_LDFLAGS += -module acl_la_LIBADD = $(top_builddir)/src/lib/acl/libacl.la acl_la_LIBADD += $(PYTHON_LIB) -dns_la_LDFLAGS += -module -dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la -dns_la_LIBADD += $(PYTHON_LIB) +_dns_la_LDFLAGS += -module +_dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la +_dns_la_LIBADD += $(PYTHON_LIB) EXTRA_DIST = acl.py dns.py EXTRA_DIST += acl_inc.cc diff --git a/src/lib/python/isc/acl/dns.cc b/src/lib/python/isc/acl/dns.cc index 351a8b3e2e..eb3b57b780 100644 --- a/src/lib/python/isc/acl/dns.cc +++ b/src/lib/python/isc/acl/dns.cc @@ -52,7 +52,7 @@ PyMethodDef methods[] = { PyModuleDef dnsacl = { { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, - "isc.acl.dns", + "isc.acl._dns", dnsacl_doc, -1, methods, @@ -90,7 +90,7 @@ getACLException(const char* ex_name) { } PyMODINIT_FUNC -PyInit_dns(void) { +PyInit__dns(void) { PyObject* mod = PyModule_Create(&dnsacl); if (mod == NULL) { return (NULL); diff --git a/src/lib/python/isc/acl/dns.py b/src/lib/python/isc/acl/dns.py index 8070559b0a..0733bc3ce5 100644 --- a/src/lib/python/isc/acl/dns.py +++ b/src/lib/python/isc/acl/dns.py @@ -13,21 +13,61 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# This file is not installed. The log.so is installed into the right place. -# It is only to find it in the .libs directory when we run as a test or -# from the build directory. -# But as nobody gives us the builddir explicitly (and we can't use generation -# from .in file, as it would put us into the builddir and we wouldn't be found) -# we guess from current directory. Any idea for something better? This should -# be enough for the tests, but would it work for B10_FROM_SOURCE as well? -# Should we look there? Or define something in bind10_config? +"""\ +This module provides Python bindings for the C++ classes in the +isc::acl::dns namespace. Specifically, it defines Python interfaces of +handling access control lists (ACLs) with DNS related contexts. +The actual binding is implemented in an effectively hidden module, +isc.acl._dns; this frontend module is in terms of implementation so that +the C++ binding code doesn't have to deal with complicated operations +that could be done in a more straightforward way in native Python. -import os -import sys +For further details of the actual module, see the documentation of the +_dns module. +""" -for base in sys.path[:]: - bindingdir = os.path.join(base, 'isc/acl/.libs') - if os.path.exists(bindingdir): - sys.path.insert(0, bindingdir) +import pydnspp -from dns import * +import isc.acl._dns +from isc.acl._dns import * + +class RequestACL(isc.acl._dns.RequestACL): + """A straightforward wrapper subclass of isc.acl._dns.RequestACL. + + See the base class documentation for more implementation. + """ + pass + +class RequestLoader(isc.acl._dns.RequestLoader): + """A straightforward wrapper subclass of isc.acl._dns.RequestLoader. + + See the base class documentation for more implementation. + """ + pass + +class RequestContext(isc.acl._dns.RequestContext): + """A straightforward wrapper subclass of isc.acl._dns.RequestContext. + + See the base class documentation for more implementation. + """ + + def __init__(self, remote_address, tsig=None): + """Wrapper for the RequestContext constructor. + + Internal implementation details that the users don't have to + worry about: To avoid dealing with pydnspp bindings in the C++ code, + this wrapper converts the TSIG record in its wire format in the form + of byte data, and has the binding re-construct the record from it. + """ + tsig_wire = b'' + if tsig is not None: + if not isinstance(tsig, pydnspp.TSIGRecord): + raise TypeError("tsig must be a TSIGRecord, not %s" % + tsig.__class__.__name__) + tsig_wire = tsig.to_wire(tsig_wire) + isc.acl._dns.RequestContext.__init__(self, remote_address, tsig_wire) + + def __str__(self): + """Wrap __str__() to convert the module name.""" + s = isc.acl._dns.RequestContext.__str__(self) + return s.replace(' +#include #include +#include +#include +#include +#include +#include +#include + #include #include @@ -49,6 +57,8 @@ using namespace std; using boost::scoped_ptr; using boost::lexical_cast; using namespace isc; +using namespace isc::dns; +using namespace isc::dns::rdata; using namespace isc::util::python; using namespace isc::acl::dns; using namespace isc::acl::dns::python; @@ -59,11 +69,39 @@ namespace dns { namespace python { struct s_RequestContext::Data { - // The constructor. Currently it only accepts the information of the - // request source address, and contains all necessary logic in the body - // of the constructor. As it's extended we may have refactor it by - // introducing helper methods. - Data(const char* const remote_addr, const unsigned short remote_port) { + // The constructor. + Data(const char* const remote_addr, const unsigned short remote_port, + const char* tsig_data, const Py_ssize_t tsig_len) + { + createRemoteAddr(remote_addr, remote_port); + createTSIGRecord(tsig_data, tsig_len); + } + + // A convenient type converter from sockaddr_storage to sockaddr + const struct sockaddr& getRemoteSockaddr() const { + const void* p = &remote_ss; + return (*static_cast(p)); + } + + // The remote (source) IP address of the request. Note that it needs + // a reference to remote_ss. That's why the latter is stored within + // this structure. + scoped_ptr remote_ipaddr; + + // The effective length of remote_ss. It's necessary for getnameinfo() + // called from sockaddrToText (__str__ backend). + socklen_t remote_salen; + + // The TSIG record included in the request, if any. If the request + // doesn't contain a TSIG, this will be NULL. + scoped_ptr tsig_record; + +private: + // A helper method for the constructor that is responsible for constructing + // the remote address. + void createRemoteAddr(const char* const remote_addr, + const unsigned short remote_port) + { struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; @@ -85,21 +123,32 @@ struct s_RequestContext::Data { remote_ipaddr.reset(new IPAddress(getRemoteSockaddr())); } - // A convenient type converter from sockaddr_storage to sockaddr - const struct sockaddr& getRemoteSockaddr() const { - const void* p = &remote_ss; - return (*static_cast(p)); + // A helper method for the constructor that is responsible for constructing + // the request TSIG. + void createTSIGRecord(const char* tsig_data, const Py_ssize_t tsig_len) { + if (tsig_len == 0) { + return; + } + + // Re-construct the TSIG record from the passed binary. This should + // normally succeed because we are generally expected to be called + // from the frontend .py, which converts a valid TSIGRecord in its + // wire format. If some evil or buggy python program directly calls + // us with bogus data, validation in libdns++ will trigger an + // exception, which will be caught and converted to a Python exception + // RequestContext_init(). + isc::util::InputBuffer b(tsig_data, tsig_len); + const Name key_name(b); + const RRType tsig_type(b.readUint16()); + const RRClass tsig_class(b.readUint16()); + const RRTTL ttl(b.readUint32()); + const size_t rdlen(b.readUint16()); + const ConstRdataPtr rdata = createRdata(tsig_type, tsig_class, b, + rdlen); + tsig_record.reset(new TSIGRecord(key_name, tsig_class, ttl, + *rdata, 0)); } - // The remote (source) IP address the request. Note that it needs - // a reference to remote_ss. That's why the latter is stored within - // this structure. - scoped_ptr remote_ipaddr; - - // The effective length of remote_ss. It's necessary for getnameinf() - // called from sockaddrToText (__str__ backend). - socklen_t remote_salen; - private: struct sockaddr_storage remote_ss; }; @@ -145,31 +194,41 @@ RequestContext_init(PyObject* po_self, PyObject* args, PyObject*) { s_RequestContext* const self = static_cast(po_self); try { - // In this initial implementation, the constructor is simply: It - // takes a single parameter, which should be a Python socket address - // object. For IPv4, it's ('address test', numeric_port); for IPv6, + // In this initial implementation, the constructor is simple: It + // takes two parameters. The first parameter should be a Python + // socket address object. + // For IPv4, it's ('address test', numeric_port); for IPv6, // it's ('address text', num_port, num_flowid, num_zoneid). + // The second parameter is wire-format TSIG record in the form of + // Python byte data. If the TSIG isn't included in the request, + // its length will be 0. // Below, we parse the argument in the most straightforward way. // As the constructor becomes more complicated, we should probably // make it more structural (for example, we should first retrieve - // the socket address as a PyObject, and parse it recursively) + // the python objects, and parse them recursively) const char* remote_addr; unsigned short remote_port; unsigned int remote_flowinfo; // IPv6 only, unused here unsigned int remote_zoneid; // IPv6 only, unused here + const char* tsig_data; + Py_ssize_t tsig_len; - if (PyArg_ParseTuple(args, "(sH)", &remote_addr, &remote_port) || - PyArg_ParseTuple(args, "(sHII)", &remote_addr, &remote_port, - &remote_flowinfo, &remote_zoneid)) + if (PyArg_ParseTuple(args, "(sH)y#", &remote_addr, &remote_port, + &tsig_data, &tsig_len) || + PyArg_ParseTuple(args, "(sHII)y#", &remote_addr, &remote_port, + &remote_flowinfo, &remote_zoneid, + &tsig_data, &tsig_len)) { // We need to clear the error in case the first call to PareTuple // fails. PyErr_Clear(); auto_ptr dataptr( - new s_RequestContext::Data(remote_addr, remote_port)); - self->cppobj = new RequestContext(*dataptr->remote_ipaddr); + new s_RequestContext::Data(remote_addr, remote_port, + tsig_data, tsig_len)); + self->cppobj = new RequestContext(*dataptr->remote_ipaddr, + dataptr->tsig_record.get()); self->data_ = dataptr.release(); return (0); } @@ -224,7 +283,11 @@ RequestContext_str(PyObject* po_self) { objss << "<" << requestcontext_type.tp_name << " object, " << "remote_addr=" << sockaddrToText(self->data_->getRemoteSockaddr(), - self->data_->remote_salen) << ">"; + self->data_->remote_salen); + if (self->data_->tsig_record) { + objss << ", key=" << self->data_->tsig_record->getName(); + } + objss << ">"; return (Py_BuildValue("s", objss.str().c_str())); } catch (const exception& ex) { const string ex_what = @@ -248,7 +311,7 @@ namespace python { // Most of the functions are not actually implemented and NULL here. PyTypeObject requestcontext_type = { PyVarObject_HEAD_INIT(NULL, 0) - "isc.acl.dns.RequestContext", + "isc.acl._dns.RequestContext", sizeof(s_RequestContext), // tp_basicsize 0, // tp_itemsize RequestContext_destroy, // tp_dealloc @@ -266,7 +329,7 @@ PyTypeObject requestcontext_type = { NULL, // tp_getattro NULL, // tp_setattro NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, // tp_flags RequestContext_doc, NULL, // tp_traverse NULL, // tp_clear diff --git a/src/lib/python/isc/acl/dns_requestloader_python.cc b/src/lib/python/isc/acl/dns_requestloader_python.cc index 1ddff4c0b3..ab421c5839 100644 --- a/src/lib/python/isc/acl/dns_requestloader_python.cc +++ b/src/lib/python/isc/acl/dns_requestloader_python.cc @@ -171,7 +171,7 @@ namespace python { // Most of the functions are not actually implemented and NULL here. PyTypeObject requestloader_type = { PyVarObject_HEAD_INIT(NULL, 0) - "isc.acl.dns.RequestLoader", + "isc.acl._dns.RequestLoader", sizeof(s_RequestLoader), // tp_basicsize 0, // tp_itemsize RequestLoader_destroy, // tp_dealloc @@ -189,7 +189,7 @@ PyTypeObject requestloader_type = { NULL, // tp_getattro NULL, // tp_setattro NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, // tp_flags RequestLoader_doc, NULL, // tp_traverse NULL, // tp_clear diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am index 64737d2c33..87781d72bc 100644 --- a/src/lib/python/isc/acl/tests/Makefile.am +++ b/src/lib/python/isc/acl/tests/Makefile.am @@ -19,7 +19,7 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ - env PYTHONPATH=$(abs_top_builddir)/src/lib/isc/python/acl/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \ + env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/isc/python/acl/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \ $(LIBRARY_PATH_PLACEHOLDER) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py index acaf32bbeb..7ee3023454 100644 --- a/src/lib/python/isc/acl/tests/dns_test.py +++ b/src/lib/python/isc/acl/tests/dns_test.py @@ -15,6 +15,7 @@ import unittest import socket +from pydnspp import * from isc.acl.acl import LoaderError, Error, ACCEPT, REJECT, DROP from isc.acl.dns import * @@ -39,12 +40,37 @@ def get_acl_json(prefix): json[0]["from"] = prefix return REQUEST_LOADER.load(json) -def get_context(address): +# The following two are similar to the previous two, but use a TSIG key name +# instead of IP prefix. +def get_tsig_acl(key): + return REQUEST_LOADER.load('[{"action": "ACCEPT", "key": "' + \ + key + '"}]') + +def get_tsig_acl_json(key): + json = [{"action": "ACCEPT"}] + json[0]["key"] = key + return REQUEST_LOADER.load(json) + +# commonly used TSIG RDATA. For the purpose of ACL checks only the key name +# matters; other parrameters are simply borrowed from some other tests, which +# can be anything for the purpose of the tests here. +TSIG_RDATA = TSIG("hmac-md5.sig-alg.reg.int. 1302890362 " + \ + "300 16 2tra2tra2tra2tra2tra2g== " + \ + "11621 0 0") + +def get_context(address, key_name=None): '''This is a simple shortcut wrapper for creating a RequestContext - object with a given IP address. Port number doesn't matter in the test - (as of the initial implementation), so it's fixed for simplicity. + object with a given IP address and optionally TSIG key name. + Port number doesn't matter in the test (as of the initial implementation), + so it's fixed for simplicity. + If key_name is not None, it internally creates a (faked) TSIG record + and constructs a context with that key. Note that only the key name + matters for the purpose of ACL checks. ''' - return RequestContext(get_sockaddr(address, 53000)) + tsig_record = None + if key_name is not None: + tsig_record = TSIGRecord(Name(key_name), TSIG_RDATA) + return RequestContext(get_sockaddr(address, 53000), tsig_record) # These are commonly used RequestContext object CONTEXT4 = get_context('192.0.2.1') @@ -63,6 +89,21 @@ class RequestContextTest(unittest.TestCase): RequestContext(('2001:db8::1234', 53006, 0, 0)).__str__()) + # Construct the context from IP address and a TSIG record. + tsig_record = TSIGRecord(Name("key.example.com"), TSIG_RDATA) + self.assertEqual('', + RequestContext(('192.0.2.1', 53001), + tsig_record).__str__()) + + # same with IPv6 address, just in case. + self.assertEqual('', + RequestContext(('2001:db8::1234', 53006, + 0, 0), tsig_record).__str__()) + # Unusual case: port number overflows (this constructor allows that, # although it should be rare anyway; the socket address should # normally come from the Python socket module. @@ -89,7 +130,9 @@ class RequestContextTest(unittest.TestCase): # not a tuple self.assertRaises(TypeError, RequestContext, 1) # invalid number of parameters - self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 53), 0) + self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 53), 0, 1) + # type error for TSIG + self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 53), tsig=1) # tuple is not in the form of sockaddr self.assertRaises(TypeError, RequestContext, (0, 53)) self.assertRaises(TypeError, RequestContext, ('192.0.2.1', 'http')) @@ -158,10 +201,22 @@ class RequestACLTest(unittest.TestCase): '[{"action": "ACCEPT", "from": []}]') self.assertRaises(LoaderError, REQUEST_LOADER.load, [{"action": "ACCEPT", "from": []}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + '[{"action": "ACCEPT", "key": 1}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "key": 1}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + '[{"action": "ACCEPT", "key": {}}]') + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "key": {}}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": "bad"}]') self.assertRaises(LoaderError, REQUEST_LOADER.load, [{"action": "ACCEPT", "from": "bad"}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "key": "bad..name"}]) + self.assertRaises(LoaderError, REQUEST_LOADER.load, + [{"action": "ACCEPT", "key": "bad..name"}]) self.assertRaises(LoaderError, REQUEST_LOADER.load, '[{"action": "ACCEPT", "from": null}]') self.assertRaises(LoaderError, REQUEST_LOADER.load, @@ -237,6 +292,28 @@ class RequestACLTest(unittest.TestCase): self.assertEqual(REJECT, get_acl('32.1.13.184').execute(CONTEXT6)) self.assertEqual(REJECT, get_acl_json('32.1.13.184').execute(CONTEXT6)) + # TSIG checks, derived from dns_test.cc + self.assertEqual(ACCEPT, get_tsig_acl('key.example.com').\ + execute(get_context('192.0.2.1', + 'key.example.com'))) + self.assertEqual(REJECT, get_tsig_acl_json('key.example.com').\ + execute(get_context('192.0.2.1', + 'badkey.example.com'))) + self.assertEqual(ACCEPT, get_tsig_acl('key.example.com').\ + execute(get_context('2001:db8::1', + 'key.example.com'))) + self.assertEqual(REJECT, get_tsig_acl_json('key.example.com').\ + execute(get_context('2001:db8::1', + 'badkey.example.com'))) + self.assertEqual(REJECT, get_tsig_acl('key.example.com').\ + execute(CONTEXT4)) + self.assertEqual(REJECT, get_tsig_acl_json('key.example.com').\ + execute(CONTEXT4)) + self.assertEqual(REJECT, get_tsig_acl('key.example.com').\ + execute(CONTEXT6)) + self.assertEqual(REJECT, get_tsig_acl_json('key.example.com').\ + execute(CONTEXT6)) + # A bit more complicated example, derived from resolver_config_unittest acl = REQUEST_LOADER.load('[ {"action": "ACCEPT", ' + ' "from": "192.0.2.1"},' + From 18d0a74b6464ffbe036c41e706d3130a69a38313 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 19 Jul 2011 15:44:10 -0700 Subject: [PATCH 237/974] [trac1104] supported TSIG ACL for xfrout. --- src/bin/xfrout/tests/xfrout_test.py.in | 49 ++++++++++++++++++++++++++ src/bin/xfrout/xfrout.py.in | 3 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index e353a60d30..44ae1d3a9d 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -172,6 +172,55 @@ class TestXfroutSession(unittest.TestCase): self.assertEqual(rcode.to_text(), "NOTAUTH") self.assertTrue(self.xfrsess._tsig_ctx is not None) + # ACL using TSIG: successful case + self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + {"key": "example.com", "action": "ACCEPT"}, {"action": "REJECT"} + ]) + self.xfrsess._tsig_key_ring.add(TSIG_KEY) + [rcode, msg] = self.xfrsess._parse_query_message(request_data) + self.assertEqual(rcode.to_text(), "NOERROR") + + # ACL using TSIG: key name doesn't match; should be rejected + self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + {"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"} + ]) + self.xfrsess._tsig_key_ring.add(TSIG_KEY) + [rcode, msg] = self.xfrsess._parse_query_message(request_data) + self.assertEqual(rcode.to_text(), "REFUSED") + + # ACL using TSIG: no TSIG; should be rejected + self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + {"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"} + ]) + self.xfrsess._tsig_key_ring.add(TSIG_KEY) + [rcode, msg] = self.xfrsess._parse_query_message(self.mdata) + self.assertEqual(rcode.to_text(), "REFUSED") + + # + # ACL using IP + TSIG: both should match + # + self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + {"ALL": [{"key": "example.com"}, {"from": "192.0.2.1"}], + "action": "ACCEPT"}, + {"action": "REJECT"} + ]) + # both matches + self.xfrsess._remote = ('192.0.2.1', 12345) + [rcode, msg] = self.xfrsess._parse_query_message(request_data) + self.assertEqual(rcode.to_text(), "NOERROR") + # TSIG matches, but address doesn't + self.xfrsess._remote = ('192.0.2.2', 12345) + [rcode, msg] = self.xfrsess._parse_query_message(request_data) + self.assertEqual(rcode.to_text(), "REFUSED") + # Address matches, but TSIG doesn't (not included) + self.xfrsess._remote = ('192.0.2.1', 12345) + [rcode, msg] = self.xfrsess._parse_query_message(self.mdata) + self.assertEqual(rcode.to_text(), "REFUSED") + # Neither address nor TSIG matches + self.xfrsess._remote = ('192.0.2.2', 12345) + [rcode, msg] = self.xfrsess._parse_query_message(self.mdata) + self.assertEqual(rcode.to_text(), "REFUSED") + def test_get_query_zone_name(self): msg = self.getmsg() self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.") diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 2e94369142..fe42c5485b 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -147,7 +147,8 @@ class XfroutSession(): if rcode == Rcode.NOERROR(): # ACL checks acl_result = self._acl.execute( - isc.acl.dns.RequestContext(self._remote)) + isc.acl.dns.RequestContext(self._remote, + msg.get_tsig_record())) if acl_result == DROP: logger.info(XFROUT_QUERY_DROPPED, self._get_query_zone_name(msg), From 6ba745463f9f54496a2f9c2b1a407ab40844bbd4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 19 Jul 2011 16:05:28 -0700 Subject: [PATCH 238/974] [trac1104] distcheck fixes --- src/lib/acl/Makefile.am | 2 +- src/lib/acl/dns.cc | 1 + src/lib/python/isc/acl/Makefile.am | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/acl/Makefile.am b/src/lib/acl/Makefile.am index f211025343..92b7869742 100644 --- a/src/lib/acl/Makefile.am +++ b/src/lib/acl/Makefile.am @@ -19,7 +19,7 @@ libacl_la_LIBADD += $(top_builddir)/src/lib/util/libutil.la # DNS specialized one lib_LTLIBRARIES += libdnsacl.la -libdnsacl_la_SOURCES = dns.h dns.cc +libdnsacl_la_SOURCES = dns.h dns.cc dnsname_check.h libdnsacl_la_LIBADD = libacl.la libdnsacl_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index 679c709304..c843460f38 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -27,6 +27,7 @@ #include #include +#include #include #include diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am index 9e6b40a8da..b1afa155f0 100644 --- a/src/lib/python/isc/acl/Makefile.am +++ b/src/lib/python/isc/acl/Makefile.am @@ -34,7 +34,7 @@ _dns_la_LDFLAGS += -module _dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la _dns_la_LIBADD += $(PYTHON_LIB) -EXTRA_DIST = acl.py dns.py +EXTRA_DIST = acl.py _dns.py EXTRA_DIST += acl_inc.cc EXTRA_DIST += dnsacl_inc.cc dns_requestacl_inc.cc dns_requestcontext_inc.cc EXTRA_DIST += dns_requestloader_inc.cc From c0a78a899ad3d96bcfe15715e957eebdb71ecca4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 19 Jul 2011 16:53:09 -0700 Subject: [PATCH 239/974] [trac1104] overall documentation update --- src/lib/acl/dns.cc | 4 ++- src/lib/acl/dns.h | 16 ++++++---- src/lib/acl/dnsname_check.h | 31 +++++++++++++++++-- .../python/isc/acl/dns_requestcontext_inc.cc | 19 +++++++----- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index c843460f38..b9cf91f7f8 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -58,7 +58,9 @@ namespace dns { /// The specialization of \c NameCheck for access control with /// \c RequestContext. /// -/// TBD +/// It returns \c true if the request contains a TSIG record and its key +/// (owner) name is equal to the name stored in the check; otherwise +/// it returns \c false. template<> bool NameCheck::matches(const RequestContext& request) const { diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h index d41c2b12b7..426c9614c8 100644 --- a/src/lib/acl/dns.h +++ b/src/lib/acl/dns.h @@ -57,9 +57,9 @@ namespace dns { * used only for a very short period as stated above. * * Based on the minimalist philosophy, the initial implementation only - * maintains the remote (source) IP address of the request. The plan is - * to add more parameters of the request. A scheduled next step is to - * support the TSIG key (if it's included in the request). Other possibilities + * maintains the remote (source) IP address of the request and (optionally) + * the TSIG record included in the request. We may add more parameters of + * the request as we see the need for them. Possible additional parameters * are the local (destination) IP address, the remote and local port numbers, * various fields of the DNS request (e.g. a particular header flag value). */ @@ -72,8 +72,10 @@ struct RequestContext { /// \exception None /// /// \parameter remote_address_param The remote IP address - explicit RequestContext(const IPAddress& remote_address_param, - const isc::dns::TSIGRecord* tsig_param) : + /// \parameter tsig_param A valid pointer to the TSIG record included in + /// the request or NULL if the request doesn't contain a TSIG. + RequestContext(const IPAddress& remote_address_param, + const isc::dns::TSIGRecord* tsig_param) : remote_address(remote_address_param), tsig(tsig_param) {} @@ -90,7 +92,9 @@ struct RequestContext { /// \brief The remote IP address (eg. the client's IP address). const IPAddress& remote_address; - /// TBD + /// \brief The TSIG record included in the request message, if any. + /// + /// If the request doesn't include a TSIG, this member will be NULL. const isc::dns::TSIGRecord* const tsig; //@} }; diff --git a/src/lib/acl/dnsname_check.h b/src/lib/acl/dnsname_check.h index 30ee6c6f0b..7498d99f64 100644 --- a/src/lib/acl/dnsname_check.h +++ b/src/lib/acl/dnsname_check.h @@ -23,24 +23,49 @@ namespace isc { namespace acl { namespace dns { +/// ACL check for DNS names +/// +/// This class is intended to perform a match between a domain name +/// specified in an ACL and a given name. The primary usage of this class +/// is an ACL match for TSIG keys, where an ACL would contain a list of +/// acceptable key names and the \c match() method would compare the owner +/// name of a TSIG record against the specified names. +/// +/// This class could be used for other kinds of names such as the query name +/// of normal DNS queries. +/// +/// The class is templated on the type of a context structure passed to the +/// matches() method, and a template specialisation for that method must be +/// supplied for the class to be used. template class NameCheck : public Check { public: + /// The constructor + /// + /// \exception std::bad_alloc Resource allocation fails in copying the + /// name + /// + /// \param name The domain name to be matched in \c matches(). NameCheck(const isc::dns::Name& name) : name_(name) {} - /// \brief Destructor + /// Destructor virtual ~NameCheck() {} - /// \brief The check itself + /// The check method /// /// Matches the passed argument to the condition stored here. Different - /// specialisations must be provided for different argument types, and the + /// specializations must be provided for different argument types, and the /// program will fail to compile if a required specialisation is not /// provided. /// /// \param context Information to be matched virtual bool matches(const Context& context) const; + /// Returns the name specified on construction. + /// + /// This is mainly for testing purposes. + /// + /// \exception None const isc::dns::Name& getName() const { return (name_); } private: diff --git a/src/lib/python/isc/acl/dns_requestcontext_inc.cc b/src/lib/python/isc/acl/dns_requestcontext_inc.cc index 9e80e1ff3b..f71bc599ee 100644 --- a/src/lib/python/isc/acl/dns_requestcontext_inc.cc +++ b/src/lib/python/isc/acl/dns_requestcontext_inc.cc @@ -5,18 +5,18 @@ DNS request to be checked.\n\ This plays the role of ACL context for the RequestACL object.\n\ \n\ Based on the minimalist philosophy, the initial implementation only\n\ -maintains the remote (source) IP address of the request. The plan is\n\ -to add more parameters of the request. A scheduled next step is to\n\ -support the TSIG key (if it's included in the request). Other\n\ -possibilities are the local (destination) IP address, the remote and\n\ -local port numbers, various fields of the DNS request (e.g. a\n\ -particular header flag value).\n\ +maintains the remote (source) IP address of the request and\n\ +(optionally) the TSIG record included in the request. We may add more\n\ +parameters of the request as we see the need for them. Possible\n\ +additional parameters are the local (destination) IP address, the\n\ +remote and local port numbers, various fields of the DNS request (e.g.\n\ +a particular header flag value).\n\ \n\ -RequestContext(remote_address)\n\ +RequestContext(remote_address, tsig)\n\ \n\ In this initial implementation, the constructor only takes a\n\ remote IP address in the form of a socket address as used in the\n\ - Python socket module.\n\ + Python socket module, and optionally a pydnspp.TSIGRecord object.\n\ \n\ Exceptions:\n\ isc.acl.ACLError Normally shouldn't happen, but still possible\n\ @@ -25,6 +25,9 @@ RequestContext(remote_address)\n\ \n\ Parameters:\n\ remote_address The remote IP address\n\ + tsig The TSIG record included in the request message, if any.\n\ + If the request doesn't include a TSIG, this will be None.\n\ + If this parameter is omitted None will be assumed.\n\ \n\ "; } // unnamed namespace From 67f6e4baa87b5555f3bc13919707a3f3180d57f4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 19 Jul 2011 17:27:16 -0700 Subject: [PATCH 240/974] [trac1104] added necessary paths for ACLs --- src/bin/bind10/run_bind10.sh.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in index 40205934b5..bb44ca099a 100755 --- a/src/bin/bind10/run_bind10.sh.in +++ b/src/bin/bind10/run_bind10.sh.in @@ -23,14 +23,14 @@ 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/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:$PATH export PATH -PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config +PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs: export PYTHONPATH # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@ if test $SET_ENV_LIBRARY_PATH = yes; then - @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ + @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ export @ENV_LIBRARY_PATH@ fi From 7b55eb02488353672fad7160148a40e581cb5c80 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Wed, 20 Jul 2011 15:38:27 -0400 Subject: [PATCH 241/974] [trac1155] changes to make build work in Solaris 10 x86 --- configure.ac | 2 +- ext/asio/asio/impl/error_code.ipp | 2 ++ src/lib/dns/rrtype-placeholder.h | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 48a79d262c..65ca5f9ec9 100644 --- a/configure.ac +++ b/configure.ac @@ -266,7 +266,7 @@ AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) # gcc specific settings: if test "X$GXX" = "Xyes"; then -B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" +B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare -Wno-missing-braces" case "$host" in *-solaris*) MULTITHREADING_FLAG=-pthreads diff --git a/ext/asio/asio/impl/error_code.ipp b/ext/asio/asio/impl/error_code.ipp index ed37a17dd3..b0566ed6c9 100644 --- a/ext/asio/asio/impl/error_code.ipp +++ b/ext/asio/asio/impl/error_code.ipp @@ -11,6 +11,8 @@ #ifndef ASIO_IMPL_ERROR_CODE_IPP #define ASIO_IMPL_ERROR_CODE_IPP +#include + #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) diff --git a/src/lib/dns/rrtype-placeholder.h b/src/lib/dns/rrtype-placeholder.h index 1cb028c177..f298acd32e 100644 --- a/src/lib/dns/rrtype-placeholder.h +++ b/src/lib/dns/rrtype-placeholder.h @@ -22,6 +22,10 @@ #include +#if defined(__sun) && defined(DS) +# undef DS +#endif + namespace isc { namespace util { class InputBuffer; From 489f9a3bf2078969f746a47a49fdc17d94f898d3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 21 Jul 2011 16:00:48 -0700 Subject: [PATCH 242/974] [trac1104] removed redundant TSIG key addition to the key ring. also added assertEqual checks to catch such naive redundant copies in case that happens again. --- src/bin/xfrout/tests/xfrout_test.py.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index 44ae1d3a9d..62c7708620 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -137,7 +137,8 @@ class TestXfroutSession(unittest.TestCase): self.assertEqual(rcode.to_text(), "NOTAUTH") self.assertTrue(self.xfrsess._tsig_ctx is not None) # NOERROR - self.xfrsess._tsig_key_ring.add(TSIG_KEY) + self.assertEqual(TSIGKeyRing.SUCCESS, + self.xfrsess._tsig_key_ring.add(TSIG_KEY)) [rcode, msg] = self.xfrsess._parse_query_message(request_data) self.assertEqual(rcode.to_text(), "NOERROR") self.assertTrue(self.xfrsess._tsig_ctx is not None) @@ -176,7 +177,8 @@ class TestXfroutSession(unittest.TestCase): self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ {"key": "example.com", "action": "ACCEPT"}, {"action": "REJECT"} ]) - self.xfrsess._tsig_key_ring.add(TSIG_KEY) + self.assertEqual(TSIGKeyRing.SUCCESS, + self.xfrsess._tsig_key_ring.add(TSIG_KEY)) [rcode, msg] = self.xfrsess._parse_query_message(request_data) self.assertEqual(rcode.to_text(), "NOERROR") @@ -184,7 +186,6 @@ class TestXfroutSession(unittest.TestCase): self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ {"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"} ]) - self.xfrsess._tsig_key_ring.add(TSIG_KEY) [rcode, msg] = self.xfrsess._parse_query_message(request_data) self.assertEqual(rcode.to_text(), "REFUSED") @@ -192,7 +193,6 @@ class TestXfroutSession(unittest.TestCase): self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ {"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"} ]) - self.xfrsess._tsig_key_ring.add(TSIG_KEY) [rcode, msg] = self.xfrsess._parse_query_message(self.mdata) self.assertEqual(rcode.to_text(), "REFUSED") From 6a204908cb3f11ba7635d5e0a97a196856fb5748 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 21 Jul 2011 16:03:00 -0700 Subject: [PATCH 243/974] [trac1104] fixed test domain name --- src/lib/acl/tests/dnsname_check_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/acl/tests/dnsname_check_unittest.cc b/src/lib/acl/tests/dnsname_check_unittest.cc index 0f6ffd088e..95b531460f 100644 --- a/src/lib/acl/tests/dnsname_check_unittest.cc +++ b/src/lib/acl/tests/dnsname_check_unittest.cc @@ -53,7 +53,7 @@ TEST(DNSNameCheck, match) { EXPECT_TRUE(check.matches(Name("EXAMPLE.COM"))); // this is exact match. so super/sub domains don't match - EXPECT_FALSE(check.matches(Name("org"))); + EXPECT_FALSE(check.matches(Name("com"))); EXPECT_FALSE(check.matches(Name("www.example.com"))); } } // Unnamed namespace From 49d5415d994ab0807daeaacf5e30f9186ca72ff5 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Thu, 21 Jul 2011 19:03:57 -0400 Subject: [PATCH 244/974] [trac1155] -Wno-missing-braces is made conditional on Solaris --- configure.ac | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 65ca5f9ec9..369f973755 100644 --- a/configure.ac +++ b/configure.ac @@ -266,10 +266,12 @@ AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) # gcc specific settings: if test "X$GXX" = "Xyes"; then -B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare -Wno-missing-braces" +B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" case "$host" in *-solaris*) MULTITHREADING_FLAG=-pthreads + # In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces + B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces" ;; *) MULTITHREADING_FLAG=-pthread From 6300d968db6e857e199cf8e4701988bf2f9136a2 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Thu, 21 Jul 2011 19:04:52 -0400 Subject: [PATCH 245/974] [trac1155] a comment about and strerror() added --- ext/asio/asio/impl/error_code.ipp | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/asio/asio/impl/error_code.ipp b/ext/asio/asio/impl/error_code.ipp index b0566ed6c9..218c09ba41 100644 --- a/ext/asio/asio/impl/error_code.ipp +++ b/ext/asio/asio/impl/error_code.ipp @@ -11,6 +11,7 @@ #ifndef ASIO_IMPL_ERROR_CODE_IPP #define ASIO_IMPL_ERROR_CODE_IPP +// strerror() needs #include #if defined(_MSC_VER) && (_MSC_VER >= 1200) From af698f41e199e4942d818accb0cc0ad7589785e8 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Thu, 21 Jul 2011 19:05:46 -0400 Subject: [PATCH 246/974] [trac1155] a comment about Solaris and DS added --- src/lib/dns/rrtype-placeholder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/dns/rrtype-placeholder.h b/src/lib/dns/rrtype-placeholder.h index f298acd32e..dad1b2b5ab 100644 --- a/src/lib/dns/rrtype-placeholder.h +++ b/src/lib/dns/rrtype-placeholder.h @@ -22,6 +22,7 @@ #include +// Solaris x86 defines DS in , which gets pulled in by Boost #if defined(__sun) && defined(DS) # undef DS #endif From 8d8c3bc259f8b549a2fbace562afb0984cd427ba Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 21 Jul 2011 16:11:54 -0700 Subject: [PATCH 247/974] [trac1104] updated the head note (which was a niave copy of the logging module implementation that uses a similar techinique). --- src/lib/python/isc/acl/_dns.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/lib/python/isc/acl/_dns.py b/src/lib/python/isc/acl/_dns.py index 3bef6e369b..a645a7bc18 100644 --- a/src/lib/python/isc/acl/_dns.py +++ b/src/lib/python/isc/acl/_dns.py @@ -13,14 +13,10 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# This file is not installed. The log.so is installed into the right place. -# It is only to find it in the .libs directory when we run as a test or -# from the build directory. -# But as nobody gives us the builddir explicitly (and we can't use generation -# from .in file, as it would put us into the builddir and we wouldn't be found) -# we guess from current directory. Any idea for something better? This should -# be enough for the tests, but would it work for B10_FROM_SOURCE as well? -# Should we look there? Or define something in bind10_config? +# This file is not installed; The .so version will be installed into the right +# place at installation time. +# This helper script is only to find it in the .libs directory when we run +# as a test or from the build directory. import os import sys From 566d284cd664a78255f5fbc8881ee8996f835960 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 21 Jul 2011 16:13:34 -0700 Subject: [PATCH 248/974] [trac1104] some minor editroial fixes to comments --- src/lib/python/isc/acl/dns_requestcontext_python.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/python/isc/acl/dns_requestcontext_python.cc b/src/lib/python/isc/acl/dns_requestcontext_python.cc index 41d92f1b6b..7f33f59592 100644 --- a/src/lib/python/isc/acl/dns_requestcontext_python.cc +++ b/src/lib/python/isc/acl/dns_requestcontext_python.cc @@ -136,7 +136,7 @@ private: // wire format. If some evil or buggy python program directly calls // us with bogus data, validation in libdns++ will trigger an // exception, which will be caught and converted to a Python exception - // RequestContext_init(). + // in RequestContext_init(). isc::util::InputBuffer b(tsig_data, tsig_len); const Name key_name(b); const RRType tsig_type(b.readUint16()); @@ -220,7 +220,7 @@ RequestContext_init(PyObject* po_self, PyObject* args, PyObject*) { &remote_flowinfo, &remote_zoneid, &tsig_data, &tsig_len)) { - // We need to clear the error in case the first call to PareTuple + // We need to clear the error in case the first call to ParseTuple // fails. PyErr_Clear(); From 253a3fad875abba510e13a3112b6176b9e272e84 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 21 Jul 2011 18:04:43 -0700 Subject: [PATCH 249/974] [trac1003] some trivial and editorial changes --- src/lib/config/ccsession.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc index 9f4a5c63a4..2890e22685 100644 --- a/src/lib/config/ccsession.cc +++ b/src/lib/config/ccsession.cc @@ -192,7 +192,7 @@ ConstElementPtr getValueOrDefault(ConstElementPtr config_part, // letter of the name read in is lower-cased. // // In this way, you configure resolver logging with the name "resolver" and in -// the printed output it becomes "b10-resolver". +// the printed output it becomes "b10-resolver". // // To allow for (a) people using b10-resolver in the configuration instead of // "resolver" and (b) that fact that during the resolution of wildcards in @@ -335,7 +335,7 @@ getRelatedLoggers(ConstElementPtr loggers) { } } - // Mow find the wildcard names (the one that start with "*"). + // Now find the wildcard names (the one that start with "*"). BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { std::string cur_name = cur_logger->get("name")->stringValue(); // If name is '*', or starts with '*.', replace * with root @@ -366,7 +366,7 @@ getRelatedLoggers(ConstElementPtr loggers) { } } } - return result; + return (result); } void From 07e015d587c487ce1934144abe59010b8f588c81 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Fri, 22 Jul 2011 13:24:06 +0100 Subject: [PATCH 250/974] [trac1003] Modifications as a result of review. --- src/lib/config/ccsession.cc | 61 +++++++++++---------- src/lib/config/config_messages.mes | 4 +- src/lib/config/tests/ccsession_unittests.cc | 11 ++-- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc index 2890e22685..ac8507700f 100644 --- a/src/lib/config/ccsession.cc +++ b/src/lib/config/ccsession.cc @@ -180,31 +180,30 @@ ConstElementPtr getValueOrDefault(ConstElementPtr config_part, // Prefix name with "b10-". // -// Root logger names are based on the name of the binary they're from (e.g. -// b10-resolver). This, however, is not how they appear internally (in for -// instance bindctl, where a module name is based on what is specified in -// the .spec file (e.g. Resolver)). +// In BIND 10, modules have names taken from the .spec file, which are typically +// names starting with a capital letter (e.g. "Resolver", "Auth" etc.). The +// names of the associated binaries are derived from the module names, being +// prefixed "b10-" and having the first letter of the module name lower-cased +// (e.g. "b10-resolver", "b10-auth"). (It is a required convention that there +// be this relationship between the names.) // -// This function prefixes the name read in the configuration with 'b10-" and -// leaves the module code as it is. (It is now a required convention that the -// name from the specfile and the actual binary name should match). To take -// account of the use of capital letters in module names in bindctl, the first -// letter of the name read in is lower-cased. +// Within the binaries the root loggers are named after the binaries themselves. +// (The reason for this is that the name of the logger is included in the +// message logged, so making it clear which message comes from which BIND 10 +// process.) As logging is configured using module names, the configuration code +// has to match these with the corresponding logger names. This function +// converts a module name to a root logger name by lowercasing the first letter +// of the module name and prepending "b10-". // -// In this way, you configure resolver logging with the name "resolver" and in -// the printed output it becomes "b10-resolver". +// \param instring String to convert. (This may be empty, in which case +// "b10-" will be returned.) // -// To allow for (a) people using b10-resolver in the configuration instead of -// "resolver" and (b) that fact that during the resolution of wildcards in - -// -// \param instring String to prefix. Lowercase the first character and apply -// the prefix. If empty, "b10-" is returned. +// \return Converted string. std::string b10Prefix(const std::string& instring) { std::string result = instring; if (!result.empty()) { - result[0] = static_cast(tolower(result[0])); + result[0] = tolower(result[0]); } return (std::string("b10-") + result); } @@ -282,18 +281,20 @@ readLoggersConf(std::vector& specs, specs.push_back(logger_spec); } -// Copies the map for a logger, changing the of the logger. This is -// used because the logger being copied is "const", but we want to -// change a top-level name, so need to create a new one. - -ElementPtr +// Copies the map for a logger, changing the name of the logger in the process. +// This is used because the map being copied is "const", so in order to +// change the name we need to create a new one. +// +// \param cur_logger Logger being copied. +// \param new_name New value of the "name" element at the top level. +// +// \return Pointer to the map with the updated element. +ConstElementPtr copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) { + // Since we'll only be updating one first-level element and subsequent + // use won't change the contents of the map, a shallow map copy is enough. ElementPtr new_logger(Element::createMap()); - - // since we'll only be updating one first-level element, - // and we return as const again, a shallow map copy is - // enough new_logger->setValue(cur_logger->mapValue()); new_logger->set("name", Element::create(new_name)); @@ -394,7 +395,7 @@ ModuleSpec ModuleCCSession::readModuleSpecification(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) { @@ -461,7 +462,7 @@ ModuleCCSession::ModuleCCSession( LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str()); isc_throw(CCSessionInitError, answer->str()); } - + setLocalConfig(Element::fromJSON("{}")); // get any stored configuration from the manager if (config_handler_) { @@ -587,7 +588,7 @@ int ModuleCCSession::checkCommand() { ConstElementPtr 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")) { diff --git a/src/lib/config/config_messages.mes b/src/lib/config/config_messages.mes index 53cb4101ea..c439eddbca 100644 --- a/src/lib/config/config_messages.mes +++ b/src/lib/config/config_messages.mes @@ -41,7 +41,7 @@ running configuration manager. This is a debug message. When processing the "loggers" part of the configuration file, the configuration library found an entry for the named logger that matches the logger specification for the program. The logging -configuration for the program will updated with the information. +configuration for the program will be updated with the information. % CONFIG_LOG_IGNORE_EXPLICIT ignoring logging configuration for explicitly-named logger %1 This is a debug message. When processing the "loggers" part of the @@ -60,7 +60,7 @@ This is a debug message. When processing the "loggers" part of the configuration file, the configuration library found the named wildcard entry (one containing the "*" character) that matches a logger specification in the program. The logging configuration for the program -will updated with the information. +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 diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc index bf8cc3dbeb..5ea4f32e3e 100644 --- a/src/lib/config/tests/ccsession_unittests.cc +++ b/src/lib/config/tests/ccsession_unittests.cc @@ -21,7 +21,6 @@ #include #include -#include #include @@ -45,20 +44,21 @@ el(const std::string& str) { class CCSessionTest : public ::testing::Test { protected: - CCSessionTest() : session(el("[]"), el("[]"), el("[]")) { + CCSessionTest() : session(el("[]"), el("[]"), el("[]")), + root_name(isc::log::getRootLoggerName()) + { // upon creation of a ModuleCCSession, the class // sends its specification to the config manager. // it expects an ok answer back, so everytime we // create a ModuleCCSession, we must set an initial // ok answer. session.getMessages()->add(createAnswer()); - root_name = isc::log::getRootLoggerName(); } ~CCSessionTest() { isc::log::setRootLoggerName(root_name); } FakeSession session; - std::string root_name; + const std::string root_name; }; TEST_F(CCSessionTest, createAnswer) { @@ -693,7 +693,8 @@ TEST(LogConfigTest, relatedLoggersTest) { doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" }," " { \"name\": \"some_module\", \"severity\": \"WARN\"}]", "[ { \"name\": \"b10-test\", \"severity\": \"DEBUG\"} ]"); - + doRelatedLoggersTest("[ { \"name\": \"b10-test\" }]", + "[]"); // make sure 'bad' things like '*foo.x' or '*lib' are ignored // (cfgmgr should have already caught it in the logconfig plugin // check, and is responsible for reporting the error) From 0711c996f017cabe220dd291500bb1b202f21e1f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 22 Jul 2011 14:59:02 -0700 Subject: [PATCH 251/974] [master] changelog entry for #1104 --- ChangeLog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog b/ChangeLog index 8f86551d31..41c9faaaad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +275. [func] jinmei + Added support for TSIG key matching in ACLs. The xfrout ACL can + now refer to TSIG key names using the "key" attribute. For + example, the following specifies an ACL that allows zone transfer + if and only if the request is signed with a TSIG of a key name + "key.example": + > config set Xfrout/query_acl[0] {"action": "ACCEPT", \ + "key": "key.example"} + (Trac #1104, git 9b2e89cabb6191db86f88ee717f7abc4171fa979) + 274. [bug] naokikambe add unittests for functions xml_handler, xsd_handler and xsl_handler respectively to make sure their behaviors are correct, regardless of From aa7400d4aa132f50a982739e1e8b9752d418b97f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 22 Jul 2011 22:18:52 -0700 Subject: [PATCH 252/974] [trac1060] first step of trivial refactoring: rename some classes --- src/bin/auth/auth_config.cc | 2 +- src/bin/auth/command.cc | 10 +- src/bin/auth/query.cc | 62 +-- src/bin/auth/query.h | 22 +- src/bin/auth/tests/command_unittest.cc | 32 +- src/bin/auth/tests/config_unittest.cc | 4 +- src/bin/auth/tests/query_unittest.cc | 32 +- src/lib/datasrc/memory_datasrc.cc | 40 +- src/lib/datasrc/memory_datasrc.h | 39 +- .../datasrc/tests/memory_datasrc_unittest.cc | 505 +++++++++--------- src/lib/datasrc/tests/zonetable_unittest.cc | 34 +- src/lib/datasrc/zone.h | 20 +- src/lib/datasrc/zonetable.cc | 12 +- src/lib/datasrc/zonetable.h | 6 +- 14 files changed, 424 insertions(+), 396 deletions(-) diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc index 2943cb5bc5..11a25fa4df 100644 --- a/src/bin/auth/auth_config.cc +++ b/src/bin/auth/auth_config.cc @@ -163,7 +163,7 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) { isc_throw(AuthConfigError, "Missing zone file for zone: " << origin->str()); } - shared_ptr new_zone(new MemoryZone(rrclass_, + shared_ptr new_zone(new MemoryZoneFinder(rrclass_, Name(origin->stringValue()))); const result::Result result = memory_datasrc_->addZone(new_zone); if (result == result::EXIST) { diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc index fe3d7291c7..d6579aa8ed 100644 --- a/src/bin/auth/command.cc +++ b/src/bin/auth/command.cc @@ -136,8 +136,8 @@ public: // that doesn't block other server operations. // TODO: we may (should?) want to check the "last load time" and // the timestamp of the file and skip loading if the file isn't newer. - shared_ptr newzone(new MemoryZone(oldzone->getClass(), - oldzone->getOrigin())); + shared_ptr newzone( + new MemoryZoneFinder(oldzone->getClass(), oldzone->getOrigin())); newzone->load(oldzone->getFileName()); oldzone->swap(*newzone); LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE) @@ -145,7 +145,8 @@ public: } private: - shared_ptr oldzone; // zone to be updated with the new file. + // zone finder to be updated with the new file. + shared_ptr oldzone; // A helper private method to parse and validate command parameters. // On success, it sets 'oldzone' to the zone to be updated. @@ -194,7 +195,8 @@ private: " is not found in data source"); } - oldzone = boost::dynamic_pointer_cast(result.zone); + oldzone = boost::dynamic_pointer_cast( + result.zone_finder); return (true); } diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc index 323f89077c..fd18b88cbc 100644 --- a/src/bin/auth/query.cc +++ b/src/bin/auth/query.cc @@ -31,14 +31,14 @@ namespace isc { namespace auth { void -Query::getAdditional(const Zone& zone, const RRset& rrset) const { +Query::getAdditional(const ZoneFinder& zone, const RRset& rrset) const { RdataIteratorPtr rdata_iterator(rrset.getRdataIterator()); for (; !rdata_iterator->isLast(); rdata_iterator->next()) { const Rdata& rdata(rdata_iterator->getCurrent()); if (rrset.getType() == RRType::NS()) { // Need to perform the search in the "GLUE OK" mode. const generic::NS& ns = dynamic_cast(rdata); - findAddrs(zone, ns.getNSName(), Zone::FIND_GLUE_OK); + findAddrs(zone, ns.getNSName(), ZoneFinder::FIND_GLUE_OK); } else if (rrset.getType() == RRType::MX()) { const generic::MX& mx(dynamic_cast(rdata)); findAddrs(zone, mx.getMXName()); @@ -47,8 +47,8 @@ Query::getAdditional(const Zone& zone, const RRset& rrset) const { } void -Query::findAddrs(const Zone& zone, const Name& qname, - const Zone::FindOptions options) const +Query::findAddrs(const ZoneFinder& zone, const Name& qname, + const ZoneFinder::FindOptions options) const { // Out of zone name NameComparisonResult result = zone.getOrigin().compare(qname); @@ -66,9 +66,9 @@ Query::findAddrs(const Zone& zone, const Name& qname, // Find A rrset if (qname_ != qname || qtype_ != RRType::A()) { - Zone::FindResult a_result = zone.find(qname, RRType::A(), NULL, + ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL, options); - if (a_result.code == Zone::SUCCESS) { + if (a_result.code == ZoneFinder::SUCCESS) { response_.addRRset(Message::SECTION_ADDITIONAL, boost::const_pointer_cast(a_result.rrset)); } @@ -76,9 +76,9 @@ Query::findAddrs(const Zone& zone, const Name& qname, // Find AAAA rrset if (qname_ != qname || qtype_ != RRType::AAAA()) { - Zone::FindResult aaaa_result = + ZoneFinder::FindResult aaaa_result = zone.find(qname, RRType::AAAA(), NULL, options); - if (aaaa_result.code == Zone::SUCCESS) { + if (aaaa_result.code == ZoneFinder::SUCCESS) { response_.addRRset(Message::SECTION_ADDITIONAL, boost::const_pointer_cast(aaaa_result.rrset)); } @@ -86,10 +86,10 @@ Query::findAddrs(const Zone& zone, const Name& qname, } void -Query::putSOA(const Zone& zone) const { - Zone::FindResult soa_result(zone.find(zone.getOrigin(), +Query::putSOA(const ZoneFinder& zone) const { + ZoneFinder::FindResult soa_result(zone.find(zone.getOrigin(), RRType::SOA())); - if (soa_result.code != Zone::SUCCESS) { + if (soa_result.code != ZoneFinder::SUCCESS) { isc_throw(NoSOA, "There's no SOA record in zone " << zone.getOrigin().toText()); } else { @@ -104,11 +104,12 @@ Query::putSOA(const Zone& zone) const { } void -Query::getAuthAdditional(const Zone& zone) const { +Query::getAuthAdditional(const ZoneFinder& zone) const { // Fill in authority and addtional sections. - Zone::FindResult ns_result = zone.find(zone.getOrigin(), RRType::NS()); + ZoneFinder::FindResult ns_result = zone.find(zone.getOrigin(), + RRType::NS()); // zone origin name should have NS records - if (ns_result.code != Zone::SUCCESS) { + if (ns_result.code != ZoneFinder::SUCCESS) { isc_throw(NoApexNS, "There's no apex NS records in zone " << zone.getOrigin().toText()); } else { @@ -145,11 +146,10 @@ Query::process() const { while (keep_doing) { keep_doing = false; std::auto_ptr target(qtype_is_any ? new RRsetList : NULL); - const Zone::FindResult db_result(result.zone->find(qname_, qtype_, - target.get())); - + const ZoneFinder::FindResult db_result( + result.zone_finder->find(qname_, qtype_, target.get())); switch (db_result.code) { - case Zone::DNAME: { + case ZoneFinder::DNAME: { // First, put the dname into the answer response_.addRRset(Message::SECTION_ANSWER, boost::const_pointer_cast(db_result.rrset)); @@ -191,7 +191,7 @@ Query::process() const { response_.addRRset(Message::SECTION_ANSWER, cname); break; } - case Zone::CNAME: + case ZoneFinder::CNAME: /* * We don't do chaining yet. Therefore handling a CNAME is * mostly the same as handling SUCCESS, but we didn't get @@ -204,46 +204,46 @@ Query::process() const { response_.addRRset(Message::SECTION_ANSWER, boost::const_pointer_cast(db_result.rrset)); break; - case Zone::SUCCESS: + case ZoneFinder::SUCCESS: if (qtype_is_any) { // If quety type is ANY, insert all RRs under the domain // into answer section. BOOST_FOREACH(RRsetPtr rrset, *target) { response_.addRRset(Message::SECTION_ANSWER, rrset); // Handle additional for answer section - getAdditional(*result.zone, *rrset.get()); + getAdditional(*result.zone_finder, *rrset.get()); } } else { response_.addRRset(Message::SECTION_ANSWER, boost::const_pointer_cast(db_result.rrset)); // Handle additional for answer section - getAdditional(*result.zone, *db_result.rrset); + getAdditional(*result.zone_finder, *db_result.rrset); } // If apex NS records haven't been provided in the answer // section, insert apex NS records into the authority section // and AAAA/A RRS of each of the NS RDATA into the additional // section. - if (qname_ != result.zone->getOrigin() || - db_result.code != Zone::SUCCESS || + if (qname_ != result.zone_finder->getOrigin() || + db_result.code != ZoneFinder::SUCCESS || (qtype_ != RRType::NS() && !qtype_is_any)) { - getAuthAdditional(*result.zone); + getAuthAdditional(*result.zone_finder); } break; - case Zone::DELEGATION: + case ZoneFinder::DELEGATION: response_.setHeaderFlag(Message::HEADERFLAG_AA, false); response_.addRRset(Message::SECTION_AUTHORITY, boost::const_pointer_cast(db_result.rrset)); - getAdditional(*result.zone, *db_result.rrset); + getAdditional(*result.zone_finder, *db_result.rrset); break; - case Zone::NXDOMAIN: + case ZoneFinder::NXDOMAIN: // Just empty answer with SOA in authority section response_.setRcode(Rcode::NXDOMAIN()); - putSOA(*result.zone); + putSOA(*result.zone_finder); break; - case Zone::NXRRSET: + case ZoneFinder::NXRRSET: // Just empty answer with SOA in authority section - putSOA(*result.zone); + putSOA(*result.zone_finder); break; } } diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h index e0c6323e7b..921265fd8c 100644 --- a/src/bin/auth/query.h +++ b/src/bin/auth/query.h @@ -71,7 +71,7 @@ private: /// Adds a SOA of the zone into the authority zone of response_. /// Can throw NoSOA. /// - void putSOA(const isc::datasrc::Zone& zone) const; + void putSOA(const isc::datasrc::ZoneFinder& zone) const; /// \brief Look up additional data (i.e., address records for the names /// included in NS or MX records). @@ -83,11 +83,11 @@ private: /// This method may throw a exception because its underlying methods may /// throw exceptions. /// - /// \param zone The Zone wherein the additional data to the query is bo be + /// \param zone The ZoneFinder wherein the additional data to the query is bo be /// found. /// \param rrset The RRset (i.e., NS or MX rrset) which require additional /// processing. - void getAdditional(const isc::datasrc::Zone& zone, + void getAdditional(const isc::datasrc::ZoneFinder& zone, const isc::dns::RRset& rrset) const; /// \brief Find address records for a specified name. @@ -102,18 +102,18 @@ private: /// The glue records must exactly match the name in the NS RDATA, without /// CNAME or wildcard processing. /// - /// \param zone The \c Zone wherein the address records is to be found. + /// \param zone The \c ZoneFinder wherein the address records is to be found. /// \param qname The name in rrset RDATA. /// \param options The search options. - void findAddrs(const isc::datasrc::Zone& zone, + void findAddrs(const isc::datasrc::ZoneFinder& zone, const isc::dns::Name& qname, - const isc::datasrc::Zone::FindOptions options - = isc::datasrc::Zone::FIND_DEFAULT) const; + const isc::datasrc::ZoneFinder::FindOptions options + = isc::datasrc::ZoneFinder::FIND_DEFAULT) const; - /// \brief Look up \c Zone's NS and address records for the NS RDATA + /// \brief Look up \c ZoneFinder's NS and address records for the NS RDATA /// (domain name) for authoritative answer. /// - /// On returning an authoritative answer, insert the \c Zone's NS into the + /// On returning an authoritative answer, insert the \c ZoneFinder's NS into the /// authority section and AAAA/A RRs of each of the NS RDATA into the /// additional section. /// @@ -126,9 +126,9 @@ private: /// include AAAA/A RRs under a zone cut in additional section. (BIND 9 /// excludes under-cut RRs; NSD include them.) /// - /// \param zone The \c Zone wherein the additional data to the query is to + /// \param zone The \c ZoneFinder wherein the additional data to the query is to /// be found. - void getAuthAdditional(const isc::datasrc::Zone& zone) const; + void getAuthAdditional(const isc::datasrc::ZoneFinder& zone) const; public: /// Constructor from query parameters. diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index 2fc8052196..b7e6752c30 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -111,17 +111,17 @@ TEST_F(AuthCommandTest, shutdown) { void zoneChecks(AuthSrv& server) { EXPECT_TRUE(server.getMemoryDataSrc(RRClass::IN())); - EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> - findZone(Name("ns.test1.example")).zone-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + findZone(Name("ns.test1.example")).zone_finder-> find(Name("ns.test1.example"), RRType::A()).code); - EXPECT_EQ(Zone::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> - findZone(Name("ns.test1.example")).zone-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> + findZone(Name("ns.test1.example")).zone_finder-> find(Name("ns.test1.example"), RRType::AAAA()).code); - EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> - findZone(Name("ns.test2.example")).zone-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + findZone(Name("ns.test2.example")).zone_finder-> find(Name("ns.test2.example"), RRType::A()).code); - EXPECT_EQ(Zone::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> - findZone(Name("ns.test2.example")).zone-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> + findZone(Name("ns.test2.example")).zone_finder-> find(Name("ns.test2.example"), RRType::AAAA()).code); } @@ -148,20 +148,20 @@ configureZones(AuthSrv& server) { void newZoneChecks(AuthSrv& server) { EXPECT_TRUE(server.getMemoryDataSrc(RRClass::IN())); - EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> - findZone(Name("ns.test1.example")).zone-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + findZone(Name("ns.test1.example")).zone_finder-> find(Name("ns.test1.example"), RRType::A()).code); // now test1.example should have ns/AAAA - EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> - findZone(Name("ns.test1.example")).zone-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + findZone(Name("ns.test1.example")).zone_finder-> find(Name("ns.test1.example"), RRType::AAAA()).code); // test2.example shouldn't change - EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> - findZone(Name("ns.test2.example")).zone-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + findZone(Name("ns.test2.example")).zone_finder-> find(Name("ns.test2.example"), RRType::A()).code); - EXPECT_EQ(Zone::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> - findZone(Name("ns.test2.example")).zone-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> + findZone(Name("ns.test2.example")).zone_finder-> find(Name("ns.test2.example"), RRType::AAAA()).code); } diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc index 0890c55a02..28275085d0 100644 --- a/src/bin/auth/tests/config_unittest.cc +++ b/src/bin/auth/tests/config_unittest.cc @@ -181,8 +181,8 @@ TEST_F(MemoryDatasrcConfigTest, addOneZone) { EXPECT_NO_THROW(parser->commit()); EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount()); // Check it actually loaded something - EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(rrclass)->findZone( - Name("ns.example.com.")).zone->find(Name("ns.example.com."), + EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(rrclass)->findZone( + Name("ns.example.com.")).zone_finder->find(Name("ns.example.com."), RRType::A()).code); } diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index c68b672c8a..bd167374e3 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -103,9 +103,9 @@ const char* const other_zone_rrs = // will result in DNAME. // This mock zone doesn't handle empty non terminal nodes (if we need to test // such cases find() should have specialized code for it). -class MockZone : public Zone { +class MockZoneFinder : public ZoneFinder { public: - MockZone() : + MockZoneFinder() : origin_(Name("example.com")), delegation_name_("delegation.example.com"), dname_name_("dname.example.com"), @@ -120,7 +120,7 @@ public: other_zone_rrs; masterLoad(zone_stream, origin_, rrclass_, - boost::bind(&MockZone::loadRRset, this, _1)); + boost::bind(&MockZoneFinder::loadRRset, this, _1)); } virtual const isc::dns::Name& getOrigin() const { return (origin_); } virtual const isc::dns::RRClass& getClass() const { return (rrclass_); } @@ -163,9 +163,9 @@ private: const RRClass rrclass_; }; -Zone::FindResult -MockZone::find(const Name& name, const RRType& type, - RRsetList* target, const FindOptions options) const +ZoneFinder::FindResult +MockZoneFinder::find(const Name& name, const RRType& type, + RRsetList* target, const FindOptions options) const { // Emulating a broken zone: mandatory apex RRs are missing if specifically // configured so (which are rare cases). @@ -233,10 +233,10 @@ protected: response.setRcode(Rcode::NOERROR()); response.setOpcode(Opcode::QUERY()); // create and add a matching zone. - mock_zone = new MockZone(); - memory_datasrc.addZone(ZonePtr(mock_zone)); + mock_finder = new MockZoneFinder(); + memory_datasrc.addZone(ZoneFinderPtr(mock_finder)); } - MockZone* mock_zone; + MockZoneFinder* mock_finder; MemoryDataSrc memory_datasrc; const Name qname; const RRClass qclass; @@ -346,7 +346,7 @@ TEST_F(QueryTest, apexAnyMatch) { "example.com. 3600 IN NS glue.delegation.example.com.\n" "example.com. 3600 IN NS noglue.example.com.\n" "example.com. 3600 IN NS example.net.\n", - NULL, ns_addrs_txt, mock_zone->getOrigin()); + NULL, ns_addrs_txt, mock_finder->getOrigin()); } TEST_F(QueryTest, mxANYMatch) { @@ -368,7 +368,7 @@ TEST_F(QueryTest, nodomainANY) { EXPECT_NO_THROW(Query(memory_datasrc, Name("nxdomain.example.com"), RRType::ANY(), response).process()); responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0, - NULL, soa_txt, NULL, mock_zone->getOrigin()); + NULL, soa_txt, NULL, mock_finder->getOrigin()); } // This tests that when we need to look up Zone's apex NS records for @@ -376,7 +376,7 @@ TEST_F(QueryTest, nodomainANY) { // throw in that case. TEST_F(QueryTest, noApexNS) { // Disable apex NS record - mock_zone->setApexNSFlag(false); + mock_finder->setApexNSFlag(false); EXPECT_THROW(Query(memory_datasrc, Name("noglue.example.com"), qtype, response).process(), Query::NoApexNS); @@ -395,7 +395,7 @@ TEST_F(QueryTest, nxdomain) { EXPECT_NO_THROW(Query(memory_datasrc, Name("nxdomain.example.com"), qtype, response).process()); responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0, - NULL, soa_txt, NULL, mock_zone->getOrigin()); + NULL, soa_txt, NULL, mock_finder->getOrigin()); } TEST_F(QueryTest, nxrrset) { @@ -403,7 +403,7 @@ TEST_F(QueryTest, nxrrset) { RRType::TXT(), response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0, - NULL, soa_txt, NULL, mock_zone->getOrigin()); + NULL, soa_txt, NULL, mock_finder->getOrigin()); } /* @@ -412,7 +412,7 @@ TEST_F(QueryTest, nxrrset) { */ TEST_F(QueryTest, noSOA) { // disable zone's SOA RR. - mock_zone->setSOAFlag(false); + mock_finder->setSOAFlag(false); // The NX Domain EXPECT_THROW(Query(memory_datasrc, Name("nxdomain.example.com"), @@ -620,7 +620,7 @@ TEST_F(QueryTest, DNAME_NX_RRSET) { RRType::TXT(), response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0, - NULL, soa_txt, NULL, mock_zone->getOrigin()); + NULL, soa_txt, NULL, mock_finder->getOrigin()); } /* diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 65650004a0..40bdb545e2 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -32,10 +32,10 @@ using namespace isc::dns; namespace isc { namespace datasrc { -// Private data and hidden methods of MemoryZone -struct MemoryZone::MemoryZoneImpl { +// Private data and hidden methods of MemoryZoneFinder +struct MemoryZoneFinder::MemoryZoneFinderImpl { // Constructor - MemoryZoneImpl(const RRClass& zone_class, const Name& origin) : + MemoryZoneFinderImpl(const RRClass& zone_class, const Name& origin) : zone_class_(zone_class), origin_(origin), origin_data_(NULL), domains_(true) { @@ -223,7 +223,7 @@ struct MemoryZone::MemoryZoneImpl { * Implementation of longer methods. We put them here, because the * access is without the impl_-> and it will get inlined anyway. */ - // Implementation of MemoryZone::add + // Implementation of MemoryZoneFinder::add result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) { // Sanitize input. This will cause an exception to be thrown // if the input RRset is empty. @@ -409,7 +409,7 @@ struct MemoryZone::MemoryZoneImpl { } } - // Implementation of MemoryZone::find + // Implementation of MemoryZoneFinder::find FindResult find(const Name& name, RRType type, RRsetList* target, const FindOptions options) const { @@ -593,50 +593,50 @@ struct MemoryZone::MemoryZoneImpl { } }; -MemoryZone::MemoryZone(const RRClass& zone_class, const Name& origin) : - impl_(new MemoryZoneImpl(zone_class, origin)) +MemoryZoneFinder::MemoryZoneFinder(const RRClass& zone_class, const Name& origin) : + impl_(new MemoryZoneFinderImpl(zone_class, origin)) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin). arg(zone_class); } -MemoryZone::~MemoryZone() { +MemoryZoneFinder::~MemoryZoneFinder() { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_DESTROY).arg(getOrigin()). arg(getClass()); delete impl_; } const Name& -MemoryZone::getOrigin() const { +MemoryZoneFinder::getOrigin() const { return (impl_->origin_); } const RRClass& -MemoryZone::getClass() const { +MemoryZoneFinder::getClass() const { return (impl_->zone_class_); } -Zone::FindResult -MemoryZone::find(const Name& name, const RRType& type, +ZoneFinder::FindResult +MemoryZoneFinder::find(const Name& name, const RRType& type, RRsetList* target, const FindOptions options) const { return (impl_->find(name, type, target, options)); } result::Result -MemoryZone::add(const ConstRRsetPtr& rrset) { +MemoryZoneFinder::add(const ConstRRsetPtr& rrset) { return (impl_->add(rrset, &impl_->domains_)); } void -MemoryZone::load(const string& filename) { +MemoryZoneFinder::load(const string& filename) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()). arg(filename); // Load it into a temporary tree - MemoryZoneImpl::DomainTree tmp; + MemoryZoneFinderImpl::DomainTree tmp; masterLoad(filename.c_str(), getOrigin(), getClass(), - boost::bind(&MemoryZoneImpl::addFromLoad, impl_, _1, &tmp)); + boost::bind(&MemoryZoneFinderImpl::addFromLoad, impl_, _1, &tmp)); // If it went well, put it inside impl_->file_name_ = filename; tmp.swap(impl_->domains_); @@ -644,14 +644,14 @@ MemoryZone::load(const string& filename) { } void -MemoryZone::swap(MemoryZone& zone) { +MemoryZoneFinder::swap(MemoryZoneFinder& zone) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()). arg(zone.getOrigin()); std::swap(impl_, zone.impl_); } const string -MemoryZone::getFileName() const { +MemoryZoneFinder::getFileName() const { return (impl_->file_name_); } @@ -659,7 +659,7 @@ MemoryZone::getFileName() const { /// interface. /// /// For now, \c MemoryDataSrc only contains a \c ZoneTable object, which -/// consists of (pointers to) \c MemoryZone objects, we may add more +/// consists of (pointers to) \c MemoryZoneFinder objects, we may add more /// member variables later for new features. class MemoryDataSrc::MemoryDataSrcImpl { public: @@ -681,7 +681,7 @@ MemoryDataSrc::getZoneCount() const { } result::Result -MemoryDataSrc::addZone(ZonePtr zone) { +MemoryDataSrc::addZone(ZoneFinderPtr zone) { if (!zone) { isc_throw(InvalidParameter, "Null pointer is passed to MemoryDataSrc::addZone()"); diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 99bb4e81bc..3b7eaad1be 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -17,6 +17,8 @@ #include +#include + #include namespace isc { @@ -28,17 +30,16 @@ class RRsetList; namespace datasrc { /// A derived zone class intended to be used with the memory data source. -class MemoryZone : public Zone { +/// +/// Conceptually this "finder" maintains a local in-memory copy of all RRs +/// of a single zone from some kind of source (right now it's a textual +/// master file, but it could also be another data source with a database +/// backend). This is why the class has methods like \c load() or \c add(). +/// +/// This class is non copyable. +class MemoryZoneFinder : boost::noncopyable, public ZoneFinder { /// /// \name Constructors and Destructor. - /// - /// \b Note: - /// The copy constructor and the assignment operator are intentionally - /// defined as private, making this class non copyable. - //@{ -private: - MemoryZone(const MemoryZone& source); - MemoryZone& operator=(const MemoryZone& source); public: /// \brief Constructor from zone parameters. /// @@ -48,10 +49,11 @@ public: /// /// \param rrclass The RR class of the zone. /// \param origin The origin name of the zone. - MemoryZone(const isc::dns::RRClass& rrclass, const isc::dns::Name& origin); + MemoryZoneFinder(const isc::dns::RRClass& rrclass, + const isc::dns::Name& origin); /// The destructor. - virtual ~MemoryZone(); + virtual ~MemoryZoneFinder(); //@} /// \brief Returns the origin of the zone. @@ -170,13 +172,13 @@ public: /// /// \param zone Another \c MemoryZone object which is to be swapped with /// \c this zone. - void swap(MemoryZone& zone); + void swap(MemoryZoneFinder& zone); private: /// \name Hidden private data //@{ - struct MemoryZoneImpl; - MemoryZoneImpl* impl_; + struct MemoryZoneFinderImpl; + MemoryZoneFinderImpl* impl_; //@} }; @@ -226,11 +228,12 @@ public: /// See the description of \c find() for the semantics of the member /// variables. struct FindResult { - FindResult(result::Result param_code, const ZonePtr param_zone) : - code(param_code), zone(param_zone) + FindResult(result::Result param_code, + const ZoneFinderPtr param_zone_finder) : + code(param_code), zone_finder(param_zone_finder) {} const result::Result code; - const ZonePtr zone; + const ZoneFinderPtr zone_finder; }; /// @@ -276,7 +279,7 @@ public: /// added to the memory data source. /// \return \c result::EXIST The memory data source already /// stores a zone that has the same origin. - result::Result addZone(ZonePtr zone); + result::Result addZone(ZoneFinderPtr zone); /// Find a \c Zone that best matches the given name in the \c MemoryDataSrc. /// diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 83fbb58da5..ab9c799e32 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -53,108 +53,116 @@ protected: TEST_F(MemoryDataSrcTest, add_find_Zone) { // test add zone // Bogus zone (NULL) - EXPECT_THROW(memory_datasrc.addZone(ZonePtr()), isc::InvalidParameter); + EXPECT_THROW(memory_datasrc.addZone(ZoneFinderPtr()), + isc::InvalidParameter); // add zones with different names one by one EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::IN(), Name("a"))))); + ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), + Name("a"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::CH(), Name("b"))))); + ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("b"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::IN(), Name("c"))))); + ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("c"))))); // add zones with the same name suffix EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::CH(), + ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("x.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::CH(), + ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("o.w.y.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::CH(), + ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("p.w.y.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::IN(), + ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("q.w.y.d.e.f"))))); // add super zone and its subzone EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::CH(), Name("g.h"))))); + ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), + Name("g.h"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::IN(), Name("i.g.h"))))); + ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), + Name("i.g.h"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::IN(), + ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("z.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::IN(), + ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("j.z.d.e.f"))))); // different zone class isn't allowed. EXPECT_EQ(result::EXIST, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::CH(), + ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("q.w.y.d.e.f"))))); // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, memory_datasrc.addZone( - ZonePtr(new MemoryZone(RRClass::IN(), + ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("Q.W.Y.d.E.f"))))); // test find zone EXPECT_EQ(result::SUCCESS, memory_datasrc.findZone(Name("a")).code); EXPECT_EQ(Name("a"), - memory_datasrc.findZone(Name("a")).zone->getOrigin()); + memory_datasrc.findZone(Name("a")).zone_finder->getOrigin()); EXPECT_EQ(result::SUCCESS, memory_datasrc.findZone(Name("j.z.d.e.f")).code); EXPECT_EQ(Name("j.z.d.e.f"), - memory_datasrc.findZone(Name("j.z.d.e.f")).zone->getOrigin()); + memory_datasrc.findZone(Name("j.z.d.e.f")).zone_finder->getOrigin()); // NOTFOUND EXPECT_EQ(result::NOTFOUND, memory_datasrc.findZone(Name("d.e.f")).code); - EXPECT_EQ(ConstZonePtr(), memory_datasrc.findZone(Name("d.e.f")).zone); + EXPECT_EQ(ConstZoneFinderPtr(), + memory_datasrc.findZone(Name("d.e.f")).zone_finder); EXPECT_EQ(result::NOTFOUND, memory_datasrc.findZone(Name("w.y.d.e.f")).code); - EXPECT_EQ(ConstZonePtr(), - memory_datasrc.findZone(Name("w.y.d.e.f")).zone); + EXPECT_EQ(ConstZoneFinderPtr(), + memory_datasrc.findZone(Name("w.y.d.e.f")).zone_finder); // there's no exact match. the result should be the longest match, // and the code should be PARTIALMATCH. EXPECT_EQ(result::PARTIALMATCH, memory_datasrc.findZone(Name("j.g.h")).code); EXPECT_EQ(Name("g.h"), - memory_datasrc.findZone(Name("g.h")).zone->getOrigin()); + memory_datasrc.findZone(Name("g.h")).zone_finder->getOrigin()); EXPECT_EQ(result::PARTIALMATCH, memory_datasrc.findZone(Name("z.i.g.h")).code); EXPECT_EQ(Name("i.g.h"), - memory_datasrc.findZone(Name("z.i.g.h")).zone->getOrigin()); + memory_datasrc.findZone(Name("z.i.g.h")).zone_finder->getOrigin()); } TEST_F(MemoryDataSrcTest, getZoneCount) { EXPECT_EQ(0, memory_datasrc.getZoneCount()); memory_datasrc.addZone( - ZonePtr(new MemoryZone(rrclass, Name("example.com")))); + ZoneFinderPtr(new MemoryZoneFinder(rrclass, + Name("example.com")))); EXPECT_EQ(1, memory_datasrc.getZoneCount()); // duplicate add. counter shouldn't change memory_datasrc.addZone( - ZonePtr(new MemoryZone(rrclass, Name("example.com")))); + ZoneFinderPtr(new MemoryZoneFinder(rrclass, + Name("example.com")))); EXPECT_EQ(1, memory_datasrc.getZoneCount()); // add one more memory_datasrc.addZone( - ZonePtr(new MemoryZone(rrclass, Name("example.org")))); + ZoneFinderPtr(new MemoryZoneFinder(rrclass, + Name("example.org")))); EXPECT_EQ(2, memory_datasrc.getZoneCount()); } -// A helper callback of masterLoad() used in MemoryZoneTest. +// A helper callback of masterLoad() used in MemoryZoneFinderTest. void setRRset(RRsetPtr rrset, vector::iterator& it) { *(*it) = rrset; ++it; } -/// \brief Test fixture for the MemoryZone class -class MemoryZoneTest : public ::testing::Test { +/// \brief Test fixture for the MemoryZoneFinder class +class MemoryZoneFinderTest : public ::testing::Test { // A straightforward pair of textual RR(set) and a RRsetPtr variable // to store the RRset. Used to build test data below. struct RRsetData { @@ -162,10 +170,10 @@ class MemoryZoneTest : public ::testing::Test { RRsetPtr* rrset; }; public: - MemoryZoneTest() : + MemoryZoneFinderTest() : class_(RRClass::IN()), origin_("example.org"), - zone_(class_, origin_) + zone_finder_(class_, origin_) { // Build test RRsets. Below, we construct an RRset for // each textual RR(s) of zone_data, and assign it to the corresponding @@ -225,7 +233,7 @@ public: const RRClass class_; const Name origin_; // The zone to torture by tests - MemoryZone zone_; + MemoryZoneFinder zone_finder_; /* * Some RRsets to put inside the zone. @@ -274,29 +282,31 @@ public: * \param check_answer Should a check against equality of the answer be * done? * \param answer The expected rrset, if any should be returned. - * \param zone Check different MemoryZone object than zone_ (if NULL, + * \param zone Check different MemoryZoneFinder object than zone_ (if NULL, * uses zone_) * \param check_wild_answer Checks that the answer has the same RRs, type * class and TTL as the eqxpected answer and that the name corresponds * to the one searched. It is meant for checking answers for wildcard * queries. */ - void findTest(const Name& name, const RRType& rrtype, Zone::Result result, + void findTest(const Name& name, const RRType& rrtype, + ZoneFinder::Result result, bool check_answer = true, const ConstRRsetPtr& answer = ConstRRsetPtr(), RRsetList* target = NULL, - MemoryZone* zone = NULL, - Zone::FindOptions options = Zone::FIND_DEFAULT, + MemoryZoneFinder* zone_finder = NULL, + ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT, bool check_wild_answer = false) { - if (!zone) { - zone = &zone_; + if (zone_finder == NULL) { + zone_finder = &zone_finder_; } // The whole block is inside, because we need to check the result and // we can't assign to FindResult EXPECT_NO_THROW({ - Zone::FindResult find_result(zone->find(name, rrtype, target, - options)); + ZoneFinder::FindResult find_result(zone_finder->find( + name, rrtype, + target, options)); // Check it returns correct answers EXPECT_EQ(result, find_result.code); if (check_answer) { @@ -337,14 +347,14 @@ public: }; /** - * \brief Test MemoryZone::MemoryZone constructor. + * \brief Test MemoryZoneFinder::MemoryZoneFinder constructor. * * Takes the created zone and checks its properties they are the same * as passed parameters. */ -TEST_F(MemoryZoneTest, constructor) { - ASSERT_EQ(class_, zone_.getClass()); - ASSERT_EQ(origin_, zone_.getOrigin()); +TEST_F(MemoryZoneFinderTest, constructor) { + ASSERT_EQ(class_, zone_finder_.getClass()); + ASSERT_EQ(origin_, zone_finder_.getOrigin()); } /** * \brief Test adding. @@ -352,174 +362,178 @@ TEST_F(MemoryZoneTest, constructor) { * We test that it throws at the correct moments and the correct exceptions. * And we test the return value. */ -TEST_F(MemoryZoneTest, add) { +TEST_F(MemoryZoneFinderTest, add) { // This one does not belong to this zone - EXPECT_THROW(zone_.add(rr_out_), MemoryZone::OutOfZone); + EXPECT_THROW(zone_finder_.add(rr_out_), MemoryZoneFinder::OutOfZone); // Test null pointer - EXPECT_THROW(zone_.add(ConstRRsetPtr()), MemoryZone::NullRRset); + EXPECT_THROW(zone_finder_.add(ConstRRsetPtr()), + MemoryZoneFinder::NullRRset); // Now put all the data we have there. It should throw nothing - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_aaaa_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_a_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_aaaa_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_))); // Try putting there something twice, it should be rejected - EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_))); - EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_a_))); + EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_finder_.add(rr_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_finder_.add(rr_ns_a_))); } -TEST_F(MemoryZoneTest, addMultipleCNAMEs) { +TEST_F(MemoryZoneFinderTest, addMultipleCNAMEs) { rr_cname_->addRdata(generic::CNAME("canonical2.example.org.")); - EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError); + EXPECT_THROW(zone_finder_.add(rr_cname_), MemoryZoneFinder::AddError); } -TEST_F(MemoryZoneTest, addCNAMEThenOther) { - EXPECT_EQ(SUCCESS, zone_.add(rr_cname_)); - EXPECT_THROW(zone_.add(rr_cname_a_), MemoryZone::AddError); +TEST_F(MemoryZoneFinderTest, addCNAMEThenOther) { + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_)); + EXPECT_THROW(zone_finder_.add(rr_cname_a_), MemoryZoneFinder::AddError); } -TEST_F(MemoryZoneTest, addOtherThenCNAME) { - EXPECT_EQ(SUCCESS, zone_.add(rr_cname_a_)); - EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError); +TEST_F(MemoryZoneFinderTest, addOtherThenCNAME) { + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_a_)); + EXPECT_THROW(zone_finder_.add(rr_cname_), MemoryZoneFinder::AddError); } -TEST_F(MemoryZoneTest, findCNAME) { +TEST_F(MemoryZoneFinderTest, findCNAME) { // install CNAME RR - EXPECT_EQ(SUCCESS, zone_.add(rr_cname_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_)); // Find A RR of the same. Should match the CNAME - findTest(rr_cname_->getName(), RRType::NS(), Zone::CNAME, true, rr_cname_); + findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true, + rr_cname_); // Find the CNAME itself. Should result in normal SUCCESS - findTest(rr_cname_->getName(), RRType::CNAME(), Zone::SUCCESS, true, + findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true, rr_cname_); } -TEST_F(MemoryZoneTest, findCNAMEUnderZoneCut) { +TEST_F(MemoryZoneFinderTest, findCNAMEUnderZoneCut) { // There's nothing special when we find a CNAME under a zone cut // (with FIND_GLUE_OK). The behavior is different from BIND 9, // so we test this case explicitly. - EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)); RRsetPtr rr_cname_under_cut_(new RRset(Name("cname.child.example.org"), class_, RRType::CNAME(), RRTTL(300))); - EXPECT_EQ(SUCCESS, zone_.add(rr_cname_under_cut_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_under_cut_)); findTest(Name("cname.child.example.org"), RRType::AAAA(), - Zone::CNAME, true, rr_cname_under_cut_, NULL, NULL, - Zone::FIND_GLUE_OK); + ZoneFinder::CNAME, true, rr_cname_under_cut_, NULL, NULL, + ZoneFinder::FIND_GLUE_OK); } // Two DNAMEs at single domain are disallowed by RFC 2672, section 3) // Having a CNAME there is disallowed too, but it is tested by // addOtherThenCNAME and addCNAMEThenOther. -TEST_F(MemoryZoneTest, addMultipleDNAMEs) { +TEST_F(MemoryZoneFinderTest, addMultipleDNAMEs) { rr_dname_->addRdata(generic::DNAME("target2.example.org.")); - EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError); + EXPECT_THROW(zone_finder_.add(rr_dname_), MemoryZoneFinder::AddError); } /* * These two tests ensure that we can't have DNAME and NS at the same * node with the exception of the apex of zone (forbidden by RFC 2672) */ -TEST_F(MemoryZoneTest, addDNAMEThenNS) { - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_))); - EXPECT_THROW(zone_.add(rr_dname_ns_), MemoryZone::AddError); +TEST_F(MemoryZoneFinderTest, addDNAMEThenNS) { + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_))); + EXPECT_THROW(zone_finder_.add(rr_dname_ns_), MemoryZoneFinder::AddError); } -TEST_F(MemoryZoneTest, addNSThenDNAME) { - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_ns_))); - EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError); +TEST_F(MemoryZoneFinderTest, addNSThenDNAME) { + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_ns_))); + EXPECT_THROW(zone_finder_.add(rr_dname_), MemoryZoneFinder::AddError); } // It is allowed to have NS and DNAME at apex -TEST_F(MemoryZoneTest, DNAMEAndNSAtApex) { - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_apex_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); +TEST_F(MemoryZoneFinderTest, DNAMEAndNSAtApex) { + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_apex_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); // The NS should be possible to be found, below should be DNAME, not // delegation - findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_); - findTest(rr_child_ns_->getName(), RRType::A(), Zone::DNAME, true, + findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_); + findTest(rr_child_ns_->getName(), RRType::A(), ZoneFinder::DNAME, true, rr_dname_apex_); } -TEST_F(MemoryZoneTest, NSAndDNAMEAtApex) { - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_apex_))); +TEST_F(MemoryZoneFinderTest, NSAndDNAMEAtApex) { + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_apex_))); } // TODO: Test (and implement) adding data under DNAME. That is forbidden by // 2672 as well. // Search under a DNAME record. It should return the DNAME -TEST_F(MemoryZoneTest, findBelowDNAME) { - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_))); - findTest(Name("below.dname.example.org"), RRType::A(), Zone::DNAME, true, - rr_dname_); +TEST_F(MemoryZoneFinderTest, findBelowDNAME) { + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_))); + findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME, + true, rr_dname_); } // Search at the domain with DNAME. It should act as DNAME isn't there, DNAME // influences only the data below (see RFC 2672, section 3) -TEST_F(MemoryZoneTest, findAtDNAME) { - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_a_))); +TEST_F(MemoryZoneFinderTest, findAtDNAME) { + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_a_))); const Name dname_name(rr_dname_->getName()); - findTest(dname_name, RRType::A(), Zone::SUCCESS, true, rr_dname_a_); - findTest(dname_name, RRType::DNAME(), Zone::SUCCESS, true, rr_dname_); - findTest(dname_name, RRType::TXT(), Zone::NXRRSET, true); + findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_); + findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true, + rr_dname_); + findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true); } // Try searching something that is both under NS and DNAME, without and with // GLUE_OK mode (it should stop at the NS and DNAME respectively). -TEST_F(MemoryZoneTest, DNAMEUnderNS) { - zone_.add(rr_child_ns_); - zone_.add(rr_child_dname_); +TEST_F(MemoryZoneFinderTest, DNAMEUnderNS) { + zone_finder_.add(rr_child_ns_); + zone_finder_.add(rr_child_dname_); Name lowName("below.dname.child.example.org."); - findTest(lowName, RRType::A(), Zone::DELEGATION, true, rr_child_ns_); - findTest(lowName, RRType::A(), Zone::DNAME, true, rr_child_dname_, NULL, - NULL, Zone::FIND_GLUE_OK); + findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_); + findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_, + NULL, NULL, ZoneFinder::FIND_GLUE_OK); } // Test adding child zones and zone cut handling -TEST_F(MemoryZoneTest, delegationNS) { +TEST_F(MemoryZoneFinderTest, delegationNS) { // add in-zone data - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); // install a zone cut - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_))); // below the zone cut - findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION, - true, rr_child_ns_); + findTest(Name("www.child.example.org"), RRType::A(), + ZoneFinder::DELEGATION, true, rr_child_ns_); // at the zone cut - findTest(Name("child.example.org"), RRType::A(), Zone::DELEGATION, + findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_); - findTest(Name("child.example.org"), RRType::NS(), Zone::DELEGATION, + findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION, true, rr_child_ns_); // finding NS for the apex (origin) node. This must not be confused // with delegation due to the existence of an NS RR. - findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_); + findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_); // unusual case of "nested delegation": the highest cut should be used. - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_ns_))); findTest(Name("www.grand.child.example.org"), RRType::A(), - Zone::DELEGATION, true, rr_child_ns_); // note: !rr_grandchild_ns_ + // note: !rr_grandchild_ns_ + ZoneFinder::DELEGATION, true, rr_child_ns_); } -TEST_F(MemoryZoneTest, findAny) { - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_glue_))); +TEST_F(MemoryZoneFinderTest, findAny) { + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_))); // origin RRsetList origin_rrsets; - findTest(origin_, RRType::ANY(), Zone::SUCCESS, true, + findTest(origin_, RRType::ANY(), ZoneFinder::SUCCESS, true, ConstRRsetPtr(), &origin_rrsets); EXPECT_EQ(2, origin_rrsets.size()); EXPECT_EQ(rr_a_, origin_rrsets.findRRset(RRType::A(), RRClass::IN())); @@ -527,13 +541,13 @@ TEST_F(MemoryZoneTest, findAny) { // out zone name RRsetList out_rrsets; - findTest(Name("example.com"), RRType::ANY(), Zone::NXDOMAIN, true, + findTest(Name("example.com"), RRType::ANY(), ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), &out_rrsets); EXPECT_EQ(0, out_rrsets.size()); RRsetList glue_child_rrsets; - findTest(rr_child_glue_->getName(), RRType::ANY(), Zone::SUCCESS, true, - ConstRRsetPtr(), &glue_child_rrsets); + findTest(rr_child_glue_->getName(), RRType::ANY(), ZoneFinder::SUCCESS, + true, ConstRRsetPtr(), &glue_child_rrsets); EXPECT_EQ(rr_child_glue_, glue_child_rrsets.findRRset(RRType::A(), RRClass::IN())); EXPECT_EQ(1, glue_child_rrsets.size()); @@ -542,59 +556,60 @@ TEST_F(MemoryZoneTest, findAny) { // been implemented // add zone cut - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_))); // zone cut RRsetList child_rrsets; - findTest(rr_child_ns_->getName(), RRType::ANY(), Zone::DELEGATION, true, - rr_child_ns_, &child_rrsets); + findTest(rr_child_ns_->getName(), RRType::ANY(), ZoneFinder::DELEGATION, + true, rr_child_ns_, &child_rrsets); EXPECT_EQ(0, child_rrsets.size()); // glue for this zone cut RRsetList new_glue_child_rrsets; - findTest(rr_child_glue_->getName(), RRType::ANY(), Zone::DELEGATION, true, - rr_child_ns_, &new_glue_child_rrsets); + findTest(rr_child_glue_->getName(), RRType::ANY(), ZoneFinder::DELEGATION, + true, rr_child_ns_, &new_glue_child_rrsets); EXPECT_EQ(0, new_glue_child_rrsets.size()); } -TEST_F(MemoryZoneTest, glue) { +TEST_F(MemoryZoneFinderTest, glue) { // install zone data: // a zone cut - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_))); // glue for this cut - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_glue_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_))); // a nested zone cut (unusual) - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_ns_))); // glue under the deeper zone cut - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_glue_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_glue_))); // by default glue is hidden due to the zone cut - findTest(rr_child_glue_->getName(), RRType::A(), Zone::DELEGATION, true, - rr_child_ns_); + findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION, + true, rr_child_ns_); // If we do it in the "glue OK" mode, we should find the exact match. - findTest(rr_child_glue_->getName(), RRType::A(), Zone::SUCCESS, true, - rr_child_glue_, NULL, NULL, Zone::FIND_GLUE_OK); + findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true, + rr_child_glue_, NULL, NULL, ZoneFinder::FIND_GLUE_OK); // glue OK + NXRRSET case - findTest(rr_child_glue_->getName(), RRType::AAAA(), Zone::NXRRSET, true, - ConstRRsetPtr(), NULL, NULL, Zone::FIND_GLUE_OK); + findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET, + true, ConstRRsetPtr(), NULL, NULL, ZoneFinder::FIND_GLUE_OK); // glue OK + NXDOMAIN case - findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION, - true, rr_child_ns_, NULL, NULL, Zone::FIND_GLUE_OK); + findTest(Name("www.child.example.org"), RRType::A(), + ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL, + ZoneFinder::FIND_GLUE_OK); // nested cut case. The glue should be found. findTest(rr_grandchild_glue_->getName(), RRType::AAAA(), - Zone::SUCCESS, - true, rr_grandchild_glue_, NULL, NULL, Zone::FIND_GLUE_OK); + ZoneFinder::SUCCESS, + true, rr_grandchild_glue_, NULL, NULL, ZoneFinder::FIND_GLUE_OK); // A non-existent name in nested cut. This should result in delegation // at the highest zone cut. findTest(Name("www.grand.child.example.org"), RRType::TXT(), - Zone::DELEGATION, true, rr_child_ns_, NULL, NULL, - Zone::FIND_GLUE_OK); + ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL, + ZoneFinder::FIND_GLUE_OK); } /** @@ -604,28 +619,29 @@ TEST_F(MemoryZoneTest, glue) { * \todo This doesn't do any kind of CNAME and so on. If it isn't * directly there, it just tells it doesn't exist. */ -TEST_F(MemoryZoneTest, find) { +TEST_F(MemoryZoneFinderTest, find) { // Fill some data inside // Now put all the data we have there. It should throw nothing - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_aaaa_))); - EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_a_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_aaaa_))); + EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_))); // These two should be successful - findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_); - findTest(rr_ns_a_->getName(), RRType::A(), Zone::SUCCESS, true, rr_ns_a_); + findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_); + findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true, + rr_ns_a_); // These domain exist but don't have the provided RRType - findTest(origin_, RRType::AAAA(), Zone::NXRRSET); - findTest(rr_ns_a_->getName(), RRType::NS(), Zone::NXRRSET); + findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET); + findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET); // These domains don't exist (and one is out of the zone) - findTest(Name("nothere.example.org"), RRType::A(), Zone::NXDOMAIN); - findTest(Name("example.net"), RRType::A(), Zone::NXDOMAIN); + findTest(Name("nothere.example.org"), RRType::A(), ZoneFinder::NXDOMAIN); + findTest(Name("example.net"), RRType::A(), ZoneFinder::NXDOMAIN); } -TEST_F(MemoryZoneTest, emptyNode) { +TEST_F(MemoryZoneFinderTest, emptyNode) { /* * The backend RBTree for this test should look like as follows: * example.org @@ -645,52 +661,53 @@ TEST_F(MemoryZoneTest, emptyNode) { for (int i = 0; names[i] != NULL; ++i) { ConstRRsetPtr rrset(new RRset(Name(names[i]), class_, RRType::A(), RRTTL(300))); - EXPECT_EQ(SUCCESS, zone_.add(rrset)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rrset)); } // empty node matching, easy case: the node for 'baz' exists with // no data. - findTest(Name("baz.example.org"), RRType::A(), Zone::NXRRSET); + findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET); // empty node matching, a trickier case: the node for 'foo' is part of // "x.foo", which should be considered an empty node. - findTest(Name("foo.example.org"), RRType::A(), Zone::NXRRSET); + findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET); // "org" is contained in "example.org", but it shouldn't be treated as // NXRRSET because it's out of zone. // Note: basically we don't expect such a query to be performed (the common // operation is to identify the best matching zone first then perform // search it), but we shouldn't be confused even in the unexpected case. - findTest(Name("org"), RRType::A(), Zone::NXDOMAIN); + findTest(Name("org"), RRType::A(), ZoneFinder::NXDOMAIN); } -TEST_F(MemoryZoneTest, load) { +TEST_F(MemoryZoneFinderTest, load) { // Put some data inside the zone - EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_.add(rr_ns_))); + EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_finder_.add(rr_ns_))); // Loading with different origin should fail - EXPECT_THROW(zone_.load(TEST_DATA_DIR "/root.zone"), MasterLoadError); + EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/root.zone"), + MasterLoadError); // See the original data is still there, survived the exception - findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_); + findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_); // Create correct zone - MemoryZone rootzone(class_, Name(".")); + MemoryZoneFinder rootzone(class_, Name(".")); // Try putting something inside EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, rootzone.add(rr_ns_aaaa_))); // Load the zone. It should overwrite/remove the above RRset EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone")); // Now see there are some rrsets (we don't look inside, though) - findTest(Name("."), RRType::SOA(), Zone::SUCCESS, false, ConstRRsetPtr(), - NULL, &rootzone); - findTest(Name("."), RRType::NS(), Zone::SUCCESS, false, ConstRRsetPtr(), - NULL, &rootzone); - findTest(Name("a.root-servers.net."), RRType::A(), Zone::SUCCESS, false, - ConstRRsetPtr(), NULL, &rootzone); + findTest(Name("."), RRType::SOA(), ZoneFinder::SUCCESS, false, + ConstRRsetPtr(), NULL, &rootzone); + findTest(Name("."), RRType::NS(), ZoneFinder::SUCCESS, false, + ConstRRsetPtr(), NULL, &rootzone); + findTest(Name("a.root-servers.net."), RRType::A(), ZoneFinder::SUCCESS, + false, ConstRRsetPtr(), NULL, &rootzone); // But this should no longer be here - findTest(rr_ns_a_->getName(), RRType::AAAA(), Zone::NXDOMAIN, true, + findTest(rr_ns_a_->getName(), RRType::AAAA(), ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), NULL, &rootzone); // Try loading zone that is wrong in a different way - EXPECT_THROW(zone_.load(TEST_DATA_DIR "/duplicate_rrset.zone"), + EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/duplicate_rrset.zone"), MasterLoadError); } @@ -698,7 +715,7 @@ TEST_F(MemoryZoneTest, load) { * Test that puts a (simple) wildcard into the zone and checks we can * correctly find the data. */ -TEST_F(MemoryZoneTest, wildcard) { +TEST_F(MemoryZoneFinderTest, wildcard) { /* * example.org. * | @@ -706,40 +723,41 @@ TEST_F(MemoryZoneTest, wildcard) { * | * * */ - EXPECT_EQ(SUCCESS, zone_.add(rr_wild_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_)); // Search at the parent. The parent will not have the A, but it will // be in the wildcard (so check the wildcard isn't matched at the parent) { SCOPED_TRACE("Search at parrent"); - findTest(Name("wild.example.org"), RRType::A(), Zone::NXRRSET); + findTest(Name("wild.example.org"), RRType::A(), ZoneFinder::NXRRSET); } // Search the original name of wildcard { SCOPED_TRACE("Search directly at *"); - findTest(Name("*.wild.example.org"), RRType::A(), Zone::SUCCESS, true, - rr_wild_); + findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS, + true, rr_wild_); } // Search "created" name. { SCOPED_TRACE("Search at created child"); - findTest(Name("a.wild.example.org"), RRType::A(), Zone::SUCCESS, false, - rr_wild_, NULL, NULL, Zone::FIND_DEFAULT, true); + findTest(Name("a.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS, + false, rr_wild_, NULL, NULL, ZoneFinder::FIND_DEFAULT, true); } // Search another created name, this time little bit lower { SCOPED_TRACE("Search at created grand-child"); - findTest(Name("a.b.wild.example.org"), RRType::A(), Zone::SUCCESS, - false, rr_wild_, NULL, NULL, Zone::FIND_DEFAULT, true); + findTest(Name("a.b.wild.example.org"), RRType::A(), + ZoneFinder::SUCCESS, false, rr_wild_, NULL, NULL, + ZoneFinder::FIND_DEFAULT, true); } - EXPECT_EQ(SUCCESS, zone_.add(rr_under_wild_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_under_wild_)); { SCOPED_TRACE("Search under non-wildcard"); findTest(Name("bar.foo.wild.example.org"), RRType::A(), - Zone::NXDOMAIN); + ZoneFinder::NXDOMAIN); } } @@ -750,33 +768,34 @@ TEST_F(MemoryZoneTest, wildcard) { * - When the query is in another zone. That is, delegation cancels * the wildcard defaults." */ -TEST_F(MemoryZoneTest, delegatedWildcard) { - EXPECT_EQ(SUCCESS, zone_.add(rr_child_wild_)); - EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_)); +TEST_F(MemoryZoneFinderTest, delegatedWildcard) { + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_wild_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)); { SCOPED_TRACE("Looking under delegation point"); - findTest(Name("a.child.example.org"), RRType::A(), Zone::DELEGATION, - true, rr_child_ns_); + findTest(Name("a.child.example.org"), RRType::A(), + ZoneFinder::DELEGATION, true, rr_child_ns_); } { SCOPED_TRACE("Looking under delegation point in GLUE_OK mode"); - findTest(Name("a.child.example.org"), RRType::A(), Zone::DELEGATION, - true, rr_child_ns_, NULL, NULL, Zone::FIND_GLUE_OK); + findTest(Name("a.child.example.org"), RRType::A(), + ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL, + ZoneFinder::FIND_GLUE_OK); } } // Tests combination of wildcard and ANY. -TEST_F(MemoryZoneTest, anyWildcard) { - EXPECT_EQ(SUCCESS, zone_.add(rr_wild_)); +TEST_F(MemoryZoneFinderTest, anyWildcard) { + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_)); // First try directly the name (normal match) { SCOPED_TRACE("Asking direcly for *"); RRsetList target; - findTest(Name("*.wild.example.org"), RRType::ANY(), Zone::SUCCESS, - true, ConstRRsetPtr(), &target); + findTest(Name("*.wild.example.org"), RRType::ANY(), + ZoneFinder::SUCCESS, true, ConstRRsetPtr(), &target); ASSERT_EQ(1, target.size()); EXPECT_EQ(RRType::A(), (*target.begin())->getType()); EXPECT_EQ(Name("*.wild.example.org"), (*target.begin())->getName()); @@ -786,8 +805,8 @@ TEST_F(MemoryZoneTest, anyWildcard) { { SCOPED_TRACE("Asking in the wild way"); RRsetList target; - findTest(Name("a.wild.example.org"), RRType::ANY(), Zone::SUCCESS, - true, ConstRRsetPtr(), &target); + findTest(Name("a.wild.example.org"), RRType::ANY(), + ZoneFinder::SUCCESS, true, ConstRRsetPtr(), &target); ASSERT_EQ(1, target.size()); EXPECT_EQ(RRType::A(), (*target.begin())->getType()); EXPECT_EQ(Name("a.wild.example.org"), (*target.begin())->getName()); @@ -796,56 +815,56 @@ TEST_F(MemoryZoneTest, anyWildcard) { // Test there's nothing in the wildcard in the middle if we load // wild.*.foo.example.org. -TEST_F(MemoryZoneTest, emptyWildcard) { +TEST_F(MemoryZoneFinderTest, emptyWildcard) { /* * example.org. * foo * * * wild */ - EXPECT_EQ(SUCCESS, zone_.add(rr_emptywild_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_emptywild_)); { SCOPED_TRACE("Asking for the original record under wildcard"); - findTest(Name("wild.*.foo.example.org"), RRType::A(), Zone::SUCCESS, - true, rr_emptywild_); + findTest(Name("wild.*.foo.example.org"), RRType::A(), + ZoneFinder::SUCCESS, true, rr_emptywild_); } { SCOPED_TRACE("Asking for A record"); - findTest(Name("a.foo.example.org"), RRType::A(), Zone::NXRRSET); - findTest(Name("*.foo.example.org"), RRType::A(), Zone::NXRRSET); - findTest(Name("foo.example.org"), RRType::A(), Zone::NXRRSET); + findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET); + findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET); + findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET); } { SCOPED_TRACE("Asking for ANY record"); RRsetList normalTarget; - findTest(Name("*.foo.example.org"), RRType::ANY(), Zone::NXRRSET, true, - ConstRRsetPtr(), &normalTarget); + findTest(Name("*.foo.example.org"), RRType::ANY(), ZoneFinder::NXRRSET, + true, ConstRRsetPtr(), &normalTarget); EXPECT_EQ(0, normalTarget.size()); RRsetList wildTarget; - findTest(Name("a.foo.example.org"), RRType::ANY(), Zone::NXRRSET, true, - ConstRRsetPtr(), &wildTarget); + findTest(Name("a.foo.example.org"), RRType::ANY(), + ZoneFinder::NXRRSET, true, ConstRRsetPtr(), &wildTarget); EXPECT_EQ(0, wildTarget.size()); } { SCOPED_TRACE("Asking on the non-terminal"); findTest(Name("wild.bar.foo.example.org"), RRType::A(), - Zone::NXRRSET); + ZoneFinder::NXRRSET); } } // Same as emptyWildcard, but with multiple * in the path. -TEST_F(MemoryZoneTest, nestedEmptyWildcard) { - EXPECT_EQ(SUCCESS, zone_.add(rr_nested_emptywild_)); +TEST_F(MemoryZoneFinderTest, nestedEmptyWildcard) { + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nested_emptywild_)); { SCOPED_TRACE("Asking for the original record under wildcards"); findTest(Name("wild.*.foo.*.bar.example.org"), RRType::A(), - Zone::SUCCESS, true, rr_nested_emptywild_); + ZoneFinder::SUCCESS, true, rr_nested_emptywild_); } { @@ -860,7 +879,7 @@ TEST_F(MemoryZoneTest, nestedEmptyWildcard) { for (const char** name(names); *name != NULL; ++ name) { SCOPED_TRACE(string("Node ") + *name); - findTest(Name(*name), RRType::A(), Zone::NXRRSET); + findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET); } } @@ -878,7 +897,7 @@ TEST_F(MemoryZoneTest, nestedEmptyWildcard) { for (const char** name(names); *name != NULL; ++ name) { SCOPED_TRACE(string("Node ") + *name); - findTest(Name(*name), RRType::A(), Zone::NXRRSET); + findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET); } } @@ -889,7 +908,7 @@ TEST_F(MemoryZoneTest, nestedEmptyWildcard) { SCOPED_TRACE(string("Node ") + *name); RRsetList target; - findTest(Name(*name), RRType::ANY(), Zone::NXRRSET, true, + findTest(Name(*name), RRType::ANY(), ZoneFinder::NXRRSET, true, ConstRRsetPtr(), &target); EXPECT_EQ(0, target.size()); } @@ -899,21 +918,21 @@ TEST_F(MemoryZoneTest, nestedEmptyWildcard) { // We run this part twice from the below test, in two slightly different // situations void -MemoryZoneTest::doCancelWildcardTest() { +MemoryZoneFinderTest::doCancelWildcardTest() { // These should be canceled { SCOPED_TRACE("Canceled under foo.wild.example.org"); findTest(Name("aaa.foo.wild.example.org"), RRType::A(), - Zone::NXDOMAIN); + ZoneFinder::NXDOMAIN); findTest(Name("zzz.foo.wild.example.org"), RRType::A(), - Zone::NXDOMAIN); + ZoneFinder::NXDOMAIN); } // This is existing, non-wildcard domain, shouldn't wildcard at all { SCOPED_TRACE("Existing domain under foo.wild.example.org"); - findTest(Name("bar.foo.wild.example.org"), RRType::A(), Zone::SUCCESS, - true, rr_not_wild_); + findTest(Name("bar.foo.wild.example.org"), RRType::A(), + ZoneFinder::SUCCESS, true, rr_not_wild_); } // These should be caught by the wildcard @@ -930,15 +949,16 @@ MemoryZoneTest::doCancelWildcardTest() { for (const char** name(names); *name != NULL; ++ name) { SCOPED_TRACE(string("Node ") + *name); - findTest(Name(*name), RRType::A(), Zone::SUCCESS, false, rr_wild_, - NULL, NULL, Zone::FIND_DEFAULT, true); + findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false, + rr_wild_, NULL, NULL, ZoneFinder::FIND_DEFAULT, true); } } // This shouldn't be wildcarded, it's an existing domain { SCOPED_TRACE("The foo.wild.example.org itself"); - findTest(Name("foo.wild.example.org"), RRType::A(), Zone::NXRRSET); + findTest(Name("foo.wild.example.org"), RRType::A(), + ZoneFinder::NXRRSET); } } @@ -952,9 +972,9 @@ MemoryZoneTest::doCancelWildcardTest() { * Tests few cases "around" the canceled wildcard match, to see something that * shouldn't be canceled isn't. */ -TEST_F(MemoryZoneTest, cancelWildcard) { - EXPECT_EQ(SUCCESS, zone_.add(rr_wild_)); - EXPECT_EQ(SUCCESS, zone_.add(rr_not_wild_)); +TEST_F(MemoryZoneFinderTest, cancelWildcard) { + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_)); { SCOPED_TRACE("Runnig with single entry under foo.wild.example.org"); @@ -964,30 +984,30 @@ TEST_F(MemoryZoneTest, cancelWildcard) { // Try putting another one under foo.wild.... // The result should be the same but it will be done in another way in the // code, because the foo.wild.example.org will exist in the tree. - EXPECT_EQ(SUCCESS, zone_.add(rr_not_wild_another_)); + EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_another_)); { SCOPED_TRACE("Runnig with two entries under foo.wild.example.org"); doCancelWildcardTest(); } } -TEST_F(MemoryZoneTest, loadBadWildcard) { +TEST_F(MemoryZoneFinderTest, loadBadWildcard) { // We reject loading the zone if it contains a wildcard name for // NS or DNAME. - EXPECT_THROW(zone_.add(rr_nswild_), MemoryZone::AddError); - EXPECT_THROW(zone_.add(rr_dnamewild_), MemoryZone::AddError); + EXPECT_THROW(zone_finder_.add(rr_nswild_), MemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_dnamewild_), MemoryZoneFinder::AddError); } -TEST_F(MemoryZoneTest, swap) { +TEST_F(MemoryZoneFinderTest, swap) { // build one zone with some data - MemoryZone zone1(class_, origin_); + MemoryZoneFinder zone1(class_, origin_); EXPECT_EQ(result::SUCCESS, zone1.add(rr_ns_)); EXPECT_EQ(result::SUCCESS, zone1.add(rr_ns_aaaa_)); // build another zone of a different RR class with some other data const Name other_origin("version.bind"); ASSERT_NE(origin_, other_origin); // make sure these two are different - MemoryZone zone2(RRClass::CH(), other_origin); + MemoryZoneFinder zone2(RRClass::CH(), other_origin); EXPECT_EQ(result::SUCCESS, zone2.add(RRsetPtr(new RRset(Name("version.bind"), RRClass::CH(), RRType::TXT(), @@ -999,26 +1019,27 @@ TEST_F(MemoryZoneTest, swap) { EXPECT_EQ(RRClass::CH(), zone1.getClass()); EXPECT_EQ(RRClass::IN(), zone2.getClass()); // make sure the zone data is swapped, too - findTest(origin_, RRType::NS(), Zone::NXDOMAIN, false, ConstRRsetPtr(), - NULL, &zone1); - findTest(other_origin, RRType::TXT(), Zone::SUCCESS, false, + findTest(origin_, RRType::NS(), ZoneFinder::NXDOMAIN, false, ConstRRsetPtr(), NULL, &zone1); - findTest(origin_, RRType::NS(), Zone::SUCCESS, false, ConstRRsetPtr(), - NULL, &zone2); - findTest(other_origin, RRType::TXT(), Zone::NXDOMAIN, false, + findTest(other_origin, RRType::TXT(), ZoneFinder::SUCCESS, false, + ConstRRsetPtr(), NULL, &zone1); + findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, false, + ConstRRsetPtr(), NULL, &zone2); + findTest(other_origin, RRType::TXT(), ZoneFinder::NXDOMAIN, false, ConstRRsetPtr(), NULL, &zone2); } -TEST_F(MemoryZoneTest, getFileName) { +TEST_F(MemoryZoneFinderTest, getFileName) { // for an empty zone the file name should also be empty. - EXPECT_TRUE(zone_.getFileName().empty()); + EXPECT_TRUE(zone_finder_.getFileName().empty()); // if loading a zone fails the file name shouldn't be set. - EXPECT_THROW(zone_.load(TEST_DATA_DIR "/root.zone"), MasterLoadError); - EXPECT_TRUE(zone_.getFileName().empty()); + EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/root.zone"), + MasterLoadError); + EXPECT_TRUE(zone_finder_.getFileName().empty()); // after a successful load, the specified file name should be set - MemoryZone rootzone(class_, Name(".")); + MemoryZoneFinder rootzone(class_, Name(".")); EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone")); EXPECT_EQ(TEST_DATA_DIR "/root.zone", rootzone.getFileName()); // overriding load, which will fail @@ -1028,8 +1049,8 @@ TEST_F(MemoryZoneTest, getFileName) { EXPECT_EQ(TEST_DATA_DIR "/root.zone", rootzone.getFileName()); // After swap, file names should also be swapped. - zone_.swap(rootzone); - EXPECT_EQ(TEST_DATA_DIR "/root.zone", zone_.getFileName()); + zone_finder_.swap(rootzone); + EXPECT_EQ(TEST_DATA_DIR "/root.zone", zone_finder_.getFileName()); EXPECT_TRUE(rootzone.getFileName().empty()); } diff --git a/src/lib/datasrc/tests/zonetable_unittest.cc b/src/lib/datasrc/tests/zonetable_unittest.cc index a117176ad2..ec13382d61 100644 --- a/src/lib/datasrc/tests/zonetable_unittest.cc +++ b/src/lib/datasrc/tests/zonetable_unittest.cc @@ -28,31 +28,32 @@ using namespace isc::datasrc; namespace { TEST(ZoneTest, init) { - MemoryZone zone(RRClass::IN(), Name("example.com")); + MemoryZoneFinder zone(RRClass::IN(), Name("example.com")); EXPECT_EQ(Name("example.com"), zone.getOrigin()); EXPECT_EQ(RRClass::IN(), zone.getClass()); - MemoryZone ch_zone(RRClass::CH(), Name("example")); + MemoryZoneFinder ch_zone(RRClass::CH(), Name("example")); EXPECT_EQ(Name("example"), ch_zone.getOrigin()); EXPECT_EQ(RRClass::CH(), ch_zone.getClass()); } TEST(ZoneTest, find) { - MemoryZone zone(RRClass::IN(), Name("example.com")); - EXPECT_EQ(Zone::NXDOMAIN, + MemoryZoneFinder zone(RRClass::IN(), Name("example.com")); + EXPECT_EQ(ZoneFinder::NXDOMAIN, zone.find(Name("www.example.com"), RRType::A()).code); } class ZoneTableTest : public ::testing::Test { protected: - ZoneTableTest() : zone1(new MemoryZone(RRClass::IN(), - Name("example.com"))), - zone2(new MemoryZone(RRClass::IN(), - Name("example.net"))), - zone3(new MemoryZone(RRClass::IN(), Name("example"))) + ZoneTableTest() : zone1(new MemoryZoneFinder(RRClass::IN(), + Name("example.com"))), + zone2(new MemoryZoneFinder(RRClass::IN(), + Name("example.net"))), + zone3(new MemoryZoneFinder(RRClass::IN(), + Name("example"))) {} ZoneTable zone_table; - ZonePtr zone1, zone2, zone3; + ZoneFinderPtr zone1, zone2, zone3; }; TEST_F(ZoneTableTest, addZone) { @@ -60,7 +61,8 @@ TEST_F(ZoneTableTest, addZone) { EXPECT_EQ(result::EXIST, zone_table.addZone(zone1)); // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, zone_table.addZone( - ZonePtr(new MemoryZone(RRClass::IN(), Name("EXAMPLE.COM"))))); + ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), + Name("EXAMPLE.COM"))))); EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone2)); EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone3)); @@ -68,11 +70,11 @@ TEST_F(ZoneTableTest, addZone) { // Zone table is indexed only by name. Duplicate origin name with // different zone class isn't allowed. EXPECT_EQ(result::EXIST, zone_table.addZone( - ZonePtr(new MemoryZone(RRClass::CH(), - Name("example.com"))))); + ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), + Name("example.com"))))); /// Bogus zone (NULL) - EXPECT_THROW(zone_table.addZone(ZonePtr()), isc::InvalidParameter); + EXPECT_THROW(zone_table.addZone(ZoneFinderPtr()), isc::InvalidParameter); } TEST_F(ZoneTableTest, DISABLED_removeZone) { @@ -95,7 +97,7 @@ TEST_F(ZoneTableTest, findZone) { EXPECT_EQ(result::NOTFOUND, zone_table.findZone(Name("example.org")).code); - EXPECT_EQ(ConstZonePtr(), + EXPECT_EQ(ConstZoneFinderPtr(), zone_table.findZone(Name("example.org")).zone); // there's no exact match. the result should be the longest match, @@ -107,7 +109,7 @@ TEST_F(ZoneTableTest, findZone) { // make sure the partial match is indeed the longest match by adding // a zone with a shorter origin and query again. - ZonePtr zone_com(new MemoryZone(RRClass::IN(), Name("com"))); + ZoneFinderPtr zone_com(new MemoryZoneFinder(RRClass::IN(), Name("com"))); EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone_com)); EXPECT_EQ(Name("example.com"), zone_table.findZone(Name("www.example.com")).zone->getOrigin()); diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 1252c94f8b..69785f0227 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -27,7 +27,7 @@ namespace datasrc { /// a DNS zone as part of data source. /// /// At the moment this is provided mainly for making the \c ZoneTable class -/// and the authoritative query logic testable, and only provides a minimal +/// and the authoritative query logic testable, and only provides a minimal /// set of features. /// This is why this class is defined in the same header file, but it may /// have to move to a separate header file when we understand what is @@ -53,9 +53,9 @@ namespace datasrc { /// /// Note: Unlike some other abstract base classes we don't name the /// class beginning with "Abstract". This is because we want to have -/// commonly used definitions such as \c Result and \c ZonePtr, and we want -/// to make them look more intuitive. -class Zone { +/// commonly used definitions such as \c Result and \c ZoneFinderPtr, and we +/// want to make them look more intuitive. +class ZoneFinder { public: /// Result codes of the \c find() method. /// @@ -119,10 +119,10 @@ protected: /// /// This is intentionally defined as \c protected as this base class should /// never be instantiated (except as part of a derived class). - Zone() {} + ZoneFinder() {} public: /// The destructor. - virtual ~Zone() {} + virtual ~ZoneFinder() {} //@} /// @@ -201,11 +201,11 @@ public: //@} }; -/// \brief A pointer-like type pointing to a \c Zone object. -typedef boost::shared_ptr ZonePtr; +/// \brief A pointer-like type pointing to a \c ZoneFinder object. +typedef boost::shared_ptr ZoneFinderPtr; -/// \brief A pointer-like type pointing to a \c Zone object. -typedef boost::shared_ptr ConstZonePtr; +/// \brief A pointer-like type pointing to a \c ZoneFinder object. +typedef boost::shared_ptr ConstZoneFinderPtr; } } diff --git a/src/lib/datasrc/zonetable.cc b/src/lib/datasrc/zonetable.cc index bc09286563..644861cc2c 100644 --- a/src/lib/datasrc/zonetable.cc +++ b/src/lib/datasrc/zonetable.cc @@ -28,8 +28,8 @@ namespace datasrc { /// \short Private data and implementation of ZoneTable struct ZoneTable::ZoneTableImpl { // Type aliases to make it shorter - typedef RBTree ZoneTree; - typedef RBNode ZoneNode; + typedef RBTree ZoneTree; + typedef RBNode ZoneNode; // The actual storage ZoneTree zones_; @@ -40,7 +40,7 @@ struct ZoneTable::ZoneTableImpl { */ // Implementation of ZoneTable::addZone - result::Result addZone(ZonePtr zone) { + result::Result addZone(ZoneFinderPtr zone) { // Sanity check if (!zone) { isc_throw(InvalidParameter, @@ -85,12 +85,12 @@ struct ZoneTable::ZoneTableImpl { break; // We have no data there, so translate the pointer to NULL as well case ZoneTree::NOTFOUND: - return (FindResult(result::NOTFOUND, ZonePtr())); + return (FindResult(result::NOTFOUND, ZoneFinderPtr())); // Can Not Happen default: assert(0); // Because of warning - return (FindResult(result::NOTFOUND, ZonePtr())); + return (FindResult(result::NOTFOUND, ZoneFinderPtr())); } // Can Not Happen (remember, NOTFOUND is handled) @@ -108,7 +108,7 @@ ZoneTable::~ZoneTable() { } result::Result -ZoneTable::addZone(ZonePtr zone) { +ZoneTable::addZone(ZoneFinderPtr zone) { return (impl_->addZone(zone)); } diff --git a/src/lib/datasrc/zonetable.h b/src/lib/datasrc/zonetable.h index 5b873d1a07..5a3448045d 100644 --- a/src/lib/datasrc/zonetable.h +++ b/src/lib/datasrc/zonetable.h @@ -41,11 +41,11 @@ namespace datasrc { class ZoneTable { public: struct FindResult { - FindResult(result::Result param_code, const ZonePtr param_zone) : + FindResult(result::Result param_code, const ZoneFinderPtr param_zone) : code(param_code), zone(param_zone) {} const result::Result code; - const ZonePtr zone; + const ZoneFinderPtr zone; }; /// /// \name Constructors and Destructor. @@ -83,7 +83,7 @@ public: /// added to the zone table. /// \return \c result::EXIST The zone table already contains /// zone of the same origin. - result::Result addZone(ZonePtr zone); + result::Result addZone(ZoneFinderPtr zone); /// Remove a \c Zone of the given origin name from the \c ZoneTable. /// From bf5fbf4c58d67a25c68efea6608ec2b8e89c7597 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 22 Jul 2011 22:19:36 -0700 Subject: [PATCH 253/974] [trac1060] an incomplete template of DataSourceClient class --- src/lib/datasrc/client.h | 99 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/lib/datasrc/client.h diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h new file mode 100644 index 0000000000..2e34706b54 --- /dev/null +++ b/src/lib/datasrc/client.h @@ -0,0 +1,99 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __DATA_SOURCE_CLIENT_H +#define __DATA_SOURCE_CLIENT_H 1 + +namespace isc { +namespace datasrc { + +/// \brief TBD +/// +/// naming note: somehow redundant with the namespace of "datasrc", but +/// namespaces are often omitted with 'using' directives. In that case +/// "Client" would be too generic. So we name it with some redundancy. +/// On the other hand, concrete derived classes are generally not expected +/// to be referenced directly from other modules and applications, so +/// we'll give them more concise names such as InMemoryClient. +/// +/// This class is not copyable. +class DataSourceClient : boost::noncopyable { +public: + /// \brief A helper structure to represent the search result of + /// \c find(). + /// + /// This is a straightforward pair of the result code and a share pointer + /// to the found zone to represent the result of \c find(). + /// We use this in order to avoid overloading the return value for both + /// the result code ("success" or "not found") and the found object, + /// i.e., avoid using \c NULL to mean "not found", etc. + /// + /// This is a simple value class with no internal state, so for + /// convenience we allow the applications to refer to the members + /// directly. + /// + /// See the description of \c find() for the semantics of the member + /// variables. + struct FindResult { + FindResult(result::Result param_code, + const ZoneFinderPtr param_zone_finder) : + code(param_code), zone_finder(param_zone_finder) + {} + const result::Result code; + const ZoneFinderPtr zone_finder; + }; + + /// + /// \name Constructors and Destructor. + /// +protected: + /// Default constructor. + /// + /// \exception + /// This constructor internally involves resource allocation, and if + /// it fails, a corresponding standard exception will be thrown. + /// It never throws an exception otherwise. + DataSourceClient() {} + +public: + /// The destructor. + virtual ~DataSourceClient() {} + //@} + + /// Find a \c Zone that best matches the given name via this client. + /// + /// It searches the internal storage for a \c Zone that gives the + /// longest match against \c name, and returns the result in the + /// form of a \c FindResult object as follows: + /// - \c code: The result code of the operation. + /// - \c result::SUCCESS: A zone that gives an exact match + // is found + /// - \c result::PARTIALMATCH: A zone whose origin is a + // super domain of \c name is found (but there is no exact match) + /// - \c result::NOTFOUND: For all other cases. + /// - \c zone: Pointer to the found \c ZoneFinder object if one + // is found; otherwise \c NULL. + /// + /// This method never throws an exception. + /// + /// \param name A domain name for which the search is performed. + /// \return A \c FindResult object enclosing the search result (see above). + virtual FindResult findZone(const isc::dns::Name& name) const = 0; +}; +} +} +#endif // DATA_SOURCE_CLIENT_H +// Local Variables: +// mode: c++ +// End: From be388eb699a8517595ea921082b5ded2d1450dcc Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 25 Jul 2011 09:54:52 +0200 Subject: [PATCH 254/974] [trac926] remove test spec part --- src/bin/xfrin/xfrin.spec | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec index cbe15be9a1..a3e62cefc4 100644 --- a/src/bin/xfrin/xfrin.spec +++ b/src/bin/xfrin/xfrin.spec @@ -46,38 +46,6 @@ } ] } - }, - { "item_name": "new_zones", - "item_type": "named_set", - "item_optional": false, - "item_default": {}, - "named_set_item_spec": { - "item_name": "zone", - "item_type": "map", - "item_default": {}, - "map_item_spec": [ - { "item_name": "class", - "item_type": "string", - "item_optional": false, - "item_default": "IN" - }, - { - "item_name": "master_addr", - "item_type": "string", - "item_optional": false, - "item_default": "" - }, - { "item_name": "master_port", - "item_type": "integer", - "item_optional": false, - "item_default": 53 - }, - { "item_name": "tsig_key", - "item_type": "string", - "item_optional": true - } - ] - } } ], "commands": [ From 4c485d0b112721d3a2b2939ab61db14b7608c98c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 25 Jul 2011 10:52:14 +0200 Subject: [PATCH 255/974] [trac800] Interface and tests for the sockcreator parser --- configure.ac | 2 +- src/bin/bind10/Makefile.am | 10 +- src/bin/bind10/__init__.py | 0 .../bind10/{bind10.py.in => bind10_src.py.in} | 0 src/bin/bind10/sockcreator.py | 78 +++++++ src/bin/bind10/tests/Makefile.am | 4 +- src/bin/bind10/tests/bind10_test.py.in | 2 +- src/bin/bind10/tests/sockcreator_test.py | 193 ++++++++++++++++++ 8 files changed, 282 insertions(+), 7 deletions(-) create mode 100644 src/bin/bind10/__init__.py rename src/bin/bind10/{bind10.py.in => bind10_src.py.in} (100%) create mode 100644 src/bin/bind10/sockcreator.py create mode 100644 src/bin/bind10/tests/sockcreator_test.py diff --git a/configure.ac b/configure.ac index 48a79d262c..9ee534829e 100644 --- a/configure.ac +++ b/configure.ac @@ -902,7 +902,7 @@ AC_OUTPUT([doc/version.ent src/bin/zonemgr/run_b10-zonemgr.sh src/bin/stats/stats.py src/bin/stats/stats_httpd.py - src/bin/bind10/bind10.py + src/bin/bind10/bind10_src.py src/bin/bind10/run_bind10.sh src/bin/bind10/tests/bind10_test.py src/bin/bindctl/run_bindctl.sh diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am index 126c429e44..1a5ce64a54 100644 --- a/src/bin/bind10/Makefile.am +++ b/src/bin/bind10/Makefile.am @@ -1,7 +1,11 @@ SUBDIRS = . tests sbin_SCRIPTS = bind10 -CLEANFILES = bind10 bind10.pyc bind10_messages.py bind10_messages.pyc +CLEANFILES = bind10 bind10_src.pyc bind10_messages.py bind10_messages.pyc \ + sockcreator.pyc + +python_PYTHON = __init__.py sockcreator.py +pythondir = $(pyexecdir)/bind10 pkglibexecdir = $(libexecdir)/@PACKAGE@ pyexec_DATA = bind10_messages.py @@ -24,9 +28,9 @@ bind10_messages.py: bind10_messages.mes $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/bind10/bind10_messages.mes # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix -bind10: bind10.py +bind10: bind10_src.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ - -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bind10.py >$@ + -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bind10_src.py >$@ chmod a+x $@ pytest: diff --git a/src/bin/bind10/__init__.py b/src/bin/bind10/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10_src.py.in similarity index 100% rename from src/bin/bind10/bind10.py.in rename to src/bin/bind10/bind10_src.py.in diff --git a/src/bin/bind10/sockcreator.py b/src/bin/bind10/sockcreator.py new file mode 100644 index 0000000000..667c22f293 --- /dev/null +++ b/src/bin/bind10/sockcreator.py @@ -0,0 +1,78 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# 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. + +""" +Module that comunicates with the priviledget socket creator (b10-sockcreator). +""" + +class CreatorError(Exception): + """ + Exception for socket creator related errors. + + It has two members: fatal and errno and they are just holding the values + passed to the __init__ function. + """ + + def __init__(self, message, fatal, errno=None): + """ + Creates the exception. The message argument is the usual string. + The fatal one tells if the error is fatal (eg. the creator crashed) + and errno is the errno value returned from socket creator, if + applicable. + """ + Exception.__init__(self, message) + self.fatal = fatal + self.errno = errno + +class Parser: + """ + This class knows the sockcreator language. It creates commands, sends them + and receives the answers and parses them. + + It does not start it, the communication channel must be provided. + + In theory, anything here can throw a fatal CreatorError exception, but it + happens only in case something like the creator process crashes. Any other + occations are mentioned explicitly. + """ + + def __init__(self, creator_socket): + """ + Creates the parser. The creator_socket is socket to the socket creator + process that will be used for communication. However, the object must + have a read_fd() method to read the file descriptor. This slightly + unusual modification of socket object is used to easy up testing. + """ + pass # TODO Implement + + def terminate(self): + """ + Asks the creator process to terminate and waits for it to close the + socket. Does not return anything. + """ + pass # TODO Implement + + def get_socket(self, address, port, socktype): + """ + Asks the socket creator process to create a socket. Pass an address + (the isc.net.IPaddr object), port number and socket type (either + string "UDP", "TCP" or constant socket.SOCK_DGRAM or + socket.SOCK_STREAM. + + Blocks until it is provided by the socket creator process (which + should be fast, as it is on localhost) and returns the file descriptor + number. It raises a CreatorError exception if the creation fails. + """ + pass # TODO Implement diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index 3d8d57a195..3fe357e264 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -1,7 +1,7 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ #PYTESTS = args_test.py bind10_test.py # NOTE: this has a generated test found in the builddir -PYTESTS = bind10_test.py +PYTESTS = bind10_test.py sockcreator_test.py EXTRA_DIST = $(PYTESTS) # If necessary (rare cases), explicitly specify paths to dynamic libraries @@ -21,7 +21,7 @@ endif for pytest in $(PYTESTS) ; do \ 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/bin/bind10 \ + env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10 \ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ done diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 9d794a6a08..6b871334b8 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -13,7 +13,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file, _BASETIME +from bind10_src import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file, _BASETIME # XXX: environment tests are currently disabled, due to the preprocessor # setup that we have now complicating the environment diff --git a/src/bin/bind10/tests/sockcreator_test.py b/src/bin/bind10/tests/sockcreator_test.py new file mode 100644 index 0000000000..4e9390387a --- /dev/null +++ b/src/bin/bind10/tests/sockcreator_test.py @@ -0,0 +1,193 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# 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. + +""" +Tests for the bind10.sockcreator module. +""" + +import unittest +import struct +import socket +from bind10.sockcreator import Parser, CreatorError +from isc.net.addr import IPAddr + +class FakeCreator: + """ + Class emulating the socket to the socket creator. It can be given expected + data to receive (and check) and responses to give to the Parser class + during testing. + """ + + class InvalidPlan(Exception): + """ + Raised when someone wants to recv when sending is planned or vice + versa. + """ + pass + + class InvalidData(Exception): + """ + Raises when the data passed to sendall are not the same as expected. + """ + pass + + def __init__(self, plan): + """ + Create the object. The plan variable contains list of expected actions, + in form: + + [('r', 'Data to return from recv'), ('s', 'Data expected on sendall'), + , ('d', 'File descriptor number to return from read_sock'), ('e', + None), ...] + + It modifies the array as it goes. + """ + self.__plan = plan + + def __get_plan(self, expected): + if len(self.__plan) == 0: + raise InvalidPlan('Nothing more planned') + (kind, data) = self.__plan[0] + if kind == 'e': + raise socket.error('False socket error') + if kind != expected: + raise InvalidPlan('Planned ' + kind + ', but ' + expected + + 'requested') + return data + + def recv(self, maxsize): + """ + Emulate recv. Returs maxsize bytes from the current recv plan. If + there are data left from previous recv call, it is used first. + + If no recv is planned, raises InvalidPlan. + """ + data = self.__get_plan('r') + result, rest = data[:maxsize], data[maxsize:] + if len(rest) > 0: + self.__plan[0] = ('r', rest) + else: + self.__plan.pop(0) + return result + + def read_fd(self): + """ + Emulate the reading of file descriptor. Returns one from a plan. + + It raises InvalidPlan if no socket is planned now. + """ + fd = self.__get_plan('f') + self.__plan.pop(0) + return fd + + def sendall(self, data): + """ + Checks that the data passed are correct according to plan. It raises + InvalidData if the data differs or InvalidPlan when sendall is not + expected. + """ + planned = self.__get_plan('s') + dlen = len(data) + prefix, rest = planned[:dlen], planned[dlen:] + if prefix != data: + raise InvalidData('Expected "' + str(prefix)+ '", got "' + + str(data) + '"') + if len(rest) > 0: + self.__plan[0] = ('s', rest) + else: + self.__plan.pop(0) + def all_used(self): + """ + Returns if the whole plan was consumed. + """ + return len(self.__plan) == 0 + +class ParserTests(unittest.TestCase): + """ + Testcases for the Parser class. + """ + def test_terminate(self): + """ + Test if the command to terminate is correct and it waits for reading the + EOF. + """ + creator = FakeCreator([('s', b'T'), ('r', b'')]) + parser = Parser(creator) + self.assertEqual(None, parser.terminate()) + self.assertTrue(creator.all_used()) + + def test_crash(self): + """ + Tests that the parser correctly raises exception when it crashes + unexpectedly. + """ + creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'')]) + parser = Parser(creator) + with self.assertRaises(CreatorError) as cm: + parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP') + self.assertTrue(creator.all_used()) + # Is the exception correct? + self.assertTrue(cm.exception.fatal) + self.assertEqual(None, cm.exception.errno) + + def test_error(self): + """ + Tests that the parser correctly raises non-fatal exception when + the socket can not be created. + """ + # We split the int to see if it can cope with data coming in + # different packets + intpart = struct.pack('@i', 42) + creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'ES' + + intpart[:1]), ('r', intpart[1:])]) + parser = Parser(creator) + with self.assertRaises(CreatorError) as cm: + parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP') + self.assertTrue(creator.all_used()) + # Is the exception correct? + self.assertFalse(cm.exception.fatal) + self.assertEqual(42, cm.exception.errno) + + def __error(self, plan): + creator = FakeCreator(plan) + parser = Parser(creator) + with self.assertRaises(CreatorError) as cm: + parser.get_socket(IPAddr('0.0.0.0'), 0, socket.SOCK_DGRAM) + self.assertTrue(creator.all_used()) + self.assertTrue(cm.exception.fatal) + + def test_error_send(self): + self.__error([('e', None)]) + + def test_error_recv(self): + self.__error([('s', b'SU4\0\0\0\0\0\0'), ('e', None)]) + + def test_error_read_fd(self): + self.__error([('s', b'SU4\0\0\0\0\0\0'), ('r', b'S'), ('e', None)]) + + def __create(self, addr, socktype, encoded): + creator = FakeCreator([('s', b'S' + encoded), ('r', b'S'), ('f', 42)]) + parser = Parser(creator) + self.assertEqual(42, parser.get_socket(IPAddr(addr), 42, socktype)) + + def test_create1(self): + self.__create('192.0.2.0', 'UDP', b'U4\0\x2A\xC0\0\x02\0') + + def test_create2(self): + self.__create('2001:db8::', socket.SOCK_STREAM, + b'T6\0\x2A\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\0\0') + +if __name__ == '__main__': + unittest.main() From 517c31a58af1f7b97f308e77caeb8cbe9ef99cf1 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 25 Jul 2011 12:44:09 +0200 Subject: [PATCH 256/974] [trac800] The parser --- src/bin/bind10/sockcreator.py | 81 ++++++++++++++++++++++- src/bin/bind10/tests/sockcreator_test.py | 84 +++++++++++++++++++++++- src/bin/sockcreator/README | 2 +- 3 files changed, 160 insertions(+), 7 deletions(-) diff --git a/src/bin/bind10/sockcreator.py b/src/bin/bind10/sockcreator.py index 667c22f293..44e1d8e8ef 100644 --- a/src/bin/bind10/sockcreator.py +++ b/src/bin/bind10/sockcreator.py @@ -13,6 +13,9 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +import socket +import struct + """ Module that comunicates with the priviledget socket creator (b10-sockcreator). """ @@ -55,14 +58,26 @@ class Parser: have a read_fd() method to read the file descriptor. This slightly unusual modification of socket object is used to easy up testing. """ - pass # TODO Implement + self.__socket = creator_socket def terminate(self): """ Asks the creator process to terminate and waits for it to close the socket. Does not return anything. """ - pass # TODO Implement + if self.__socket is None: + raise CreatorError('Terminated already', True) + try: + self.__socket.sendall(b'T') + # Wait for an EOF - it will return empty data + eof = self.__socket.recv(1) + if len(eof) != 0: + raise CreatorError('Protocol error - data after terminated', + True) + self.__socket = None + except socket.error as se: + self.__socket = None + raise CreatorError(str(se), True) def get_socket(self, address, port, socktype): """ @@ -75,4 +90,64 @@ class Parser: should be fast, as it is on localhost) and returns the file descriptor number. It raises a CreatorError exception if the creation fails. """ - pass # TODO Implement + if self.__socket is None: + raise CreatorError('Socket requested on terminated creator', True) + # First, assemble the request from parts + data = b'S' + if socktype == 'UDP' or socktype == socket.SOCK_DGRAM: + data += b'U' + elif socktype == 'TCP' or socktype == socket.SOCK_STREAM: + data += b'T' + else: + raise ValueError('Unknown socket type: ' + str(socktype)) + if address.family == socket.AF_INET: + data += b'4' + elif address.family == socket.AF_INET6: + data += b'6' + else: + raise ValueError('Unknown address family in address') + data += struct.pack('!H', port) + data += address.addr + try: + # Send the request + self.__socket.sendall(data) + answer = self.__socket.recv(1) + if answer == b'S': + # Success! + return self.__socket.read_fd() + elif answer == b'E': + # There was an error, read the error as well + error = self.__socket.recv(1) + errno = struct.unpack('i', + self.__read_all(len(struct.pack('i', + 0)))) + if error == b'S': + cause = 'socket' + elif error == b'B': + cause = 'bind' + else: + self.__socket = None + raise CreatorError('Unknown error cause' + str(answer), True) + raise CreatorError('Error creating socket on ' + cause, False, + errno[0]) + else: + self.__socket = None + raise CreatorError('Unknown response ' + str(answer), True) + except socket.error as se: + self.__socket = None + raise CreatorError(str(se), True) + + def __read_all(self, length): + """ + Keeps reading until length data is read or EOF or error happens. + + EOF is considered error as well and throws. + """ + result = b'' + while len(result) < length: + data = self.__socket.recv(length - len(result)) + if len(data) == 0: + self.__socket = None + raise CreatorError('Unexpected EOF', True) + result += data + return result diff --git a/src/bin/bind10/tests/sockcreator_test.py b/src/bin/bind10/tests/sockcreator_test.py index 4e9390387a..fee691fc25 100644 --- a/src/bin/bind10/tests/sockcreator_test.py +++ b/src/bin/bind10/tests/sockcreator_test.py @@ -61,6 +61,7 @@ class FakeCreator: raise InvalidPlan('Nothing more planned') (kind, data) = self.__plan[0] if kind == 'e': + self.__plan.pop(0) raise socket.error('False socket error') if kind != expected: raise InvalidPlan('Planned ' + kind + ', but ' + expected + @@ -108,6 +109,7 @@ class FakeCreator: self.__plan[0] = ('s', rest) else: self.__plan.pop(0) + def all_used(self): """ Returns if the whole plan was consumed. @@ -118,15 +120,65 @@ class ParserTests(unittest.TestCase): """ Testcases for the Parser class. """ + def __terminate(self): + creator = FakeCreator([('s', b'T'), ('r', b'')]) + parser = Parser(creator) + self.assertEqual(None, parser.terminate()) + self.assertTrue(creator.all_used()) + return parser + def test_terminate(self): """ Test if the command to terminate is correct and it waits for reading the EOF. """ - creator = FakeCreator([('s', b'T'), ('r', b'')]) + self.__terminate() + + def test_terminate_error1(self): + """ + Test it reports an exception when there's error terminating the creator. + This one raises an error when receiving the EOF. + """ + creator = FakeCreator([('s', b'T'), ('e', None)]) parser = Parser(creator) - self.assertEqual(None, parser.terminate()) - self.assertTrue(creator.all_used()) + with self.assertRaises(CreatorError) as cm: + parser.terminate() + self.assertTrue(cm.exception.fatal) + self.assertEqual(None, cm.exception.errno) + + def test_terminate_error2(self): + """ + Test it reports an exception when there's error terminating the creator. + This one raises an error when sending data. + """ + creator = FakeCreator([('e', None)]) + parser = Parser(creator) + with self.assertRaises(CreatorError) as cm: + parser.terminate() + self.assertTrue(cm.exception.fatal) + self.assertEqual(None, cm.exception.errno) + + def test_terminate_twice(self): + """ + Test we can't terminate twice. + """ + parser = self.__terminate() + with self.assertRaises(CreatorError) as cm: + parser.terminate() + self.assertTrue(cm.exception.fatal) + self.assertEqual(None, cm.exception.errno) + + def test_terminate_error3(self): + """ + Test it reports an exception when there's error terminating the creator. + This one sends data when it should have terminated. + """ + creator = FakeCreator([('s', b'T'), ('r', b'Extra data')]) + parser = Parser(creator) + with self.assertRaises(CreatorError) as cm: + parser.terminate() + self.assertTrue(cm.exception.fatal) + self.assertEqual(None, cm.exception.errno) def test_crash(self): """ @@ -189,5 +241,31 @@ class ParserTests(unittest.TestCase): self.__create('2001:db8::', socket.SOCK_STREAM, b'T6\0\x2A\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\0\0') + def test_create_terminated(self): + """ + Test we can't request sockets after it was terminated. + """ + parser = self.__terminate() + with self.assertRaises(CreatorError) as cm: + parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP') + self.assertTrue(cm.exception.fatal) + self.assertEqual(None, cm.exception.errno) + + def test_invalid_socktype(self): + """ + Test invalid socket type is rejected + """ + self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket, + IPAddr('0.0.0.0'), 42, 'RAW') + + def test_invalid_family(self): + """ + Test it rejects invalid address family. + """ + addr = IPAddr('0.0.0.0') + addr.family = 'Nonsense' + self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket, + addr, 42, socket.SOCK_DGRAM) + if __name__ == '__main__': unittest.main() diff --git a/src/bin/sockcreator/README b/src/bin/sockcreator/README index 4dbbee726e..e142d191d7 100644 --- a/src/bin/sockcreator/README +++ b/src/bin/sockcreator/README @@ -3,7 +3,7 @@ The socket creator The only thing we need higher rights than standard user is binding sockets to ports lower than 1024. So we will have a separate process that keeps the -rights, while the rests drop them for security reasons. +rights, while the rest drops them for security reasons. This process is the socket creator. Its goal is to be as simple as possible and to contain as little code as possible to minimise the amount of code From d9e757fb15b711464cfc8ba344f2563f3e2b9195 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 25 Jul 2011 13:48:15 +0200 Subject: [PATCH 257/974] [trac800] Provide logging for the sockcreator parser --- src/bin/bind10/bind10_messages.mes | 34 ++++++++++++++++++++++++ src/bin/bind10/sockcreator.py | 17 +++++++++++- src/bin/bind10/tests/sockcreator_test.py | 9 ++++++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 3f5f637415..392b6c7057 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -155,3 +155,37 @@ the message channel. An unknown child process has exited. The PID is printed, but no further action will be taken by the boss process. +% BIND10_SOCKCREATOR_INIT initializing socket creator parser +The boss module initializes routines for parsing the socket creator +protocol. + +% BIND10_SOCKCREATOR_TERMINATE terminating socket creator +The boss module sends a request to terminate to the socket creator. + +% BIND10_SOCKET_GET requesting socket [%1]:%2 of type %3 from the creator +The boss forwards a request for a socket to the socket creator. + +% BIND10_SOCKCREATOR_EOF eof while expecting data from socket creator +There should be more data from the socket creator, but it closed the socket. +It probably crashed. + +% BIND10_SOCKCREATOR_BAD_RESPONSE unknown response for socket request: %1 +The boss requested a socket from the creator, but the answer is unknown. This +looks like programmer error. + +% BIND10_SOCKET_ERROR error on %1 call in the creator: %2/%3 +The socket creator failed to create the requested socket. It failed on the +indicated OS API function with given error. + +% BIND10_SOCKCREATOR_BAD_CAUSE unknown error cause from socket creator: %1 +The socket creator reported an error when creating a socket. But the function +which failed is unknown (not one of 'S' for socket or 'B' for bind). + +% BIND10_SOCKET_CREATED successfully created socket %1 +The socket creator successfully created and sent a requested socket, it has +the given file number. + +% BIND10_SOCKCREATOR_TRANSPORT_ERROR transport error when talking to the socket creator: %1 +Either sending or receiving data from the socket creator failed with the given +error. The creator probably crashed or some serious OS-level problem happened, +as the communication happens only on local host. diff --git a/src/bin/bind10/sockcreator.py b/src/bin/bind10/sockcreator.py index 44e1d8e8ef..36815efdb8 100644 --- a/src/bin/bind10/sockcreator.py +++ b/src/bin/bind10/sockcreator.py @@ -15,6 +15,10 @@ import socket import struct +import os +from bind10_messages import * + +logger = isc.log.Logger("boss") """ Module that comunicates with the priviledget socket creator (b10-sockcreator). @@ -59,6 +63,7 @@ class Parser: unusual modification of socket object is used to easy up testing. """ self.__socket = creator_socket + logger.info(BIND10_SOCKCREATOR_INIT) def terminate(self): """ @@ -67,6 +72,7 @@ class Parser: """ if self.__socket is None: raise CreatorError('Terminated already', True) + logger.info(BIND10_SOCKCREATOR_TERMINATE) try: self.__socket.sendall(b'T') # Wait for an EOF - it will return empty data @@ -93,6 +99,7 @@ class Parser: if self.__socket is None: raise CreatorError('Socket requested on terminated creator', True) # First, assemble the request from parts + logger.info(BIND10_SOCKET_GET, address, port, socktype) data = b'S' if socktype == 'UDP' or socktype == socket.SOCK_DGRAM: data += b'U' @@ -114,7 +121,9 @@ class Parser: answer = self.__socket.recv(1) if answer == b'S': # Success! - return self.__socket.read_fd() + result = self.__socket.read_fd() + logger.info(BIND10_SOCKET_CREATED, result) + return result elif answer == b'E': # There was an error, read the error as well error = self.__socket.recv(1) @@ -127,14 +136,19 @@ class Parser: cause = 'bind' else: self.__socket = None + logger.fatal(BIND10_SOCKCREATOR_BAD_CAUSE, error) raise CreatorError('Unknown error cause' + str(answer), True) + logger.error(BIND10_SOCKET_ERROR, cause, errno[0], + os.strerror(errno[0])) raise CreatorError('Error creating socket on ' + cause, False, errno[0]) else: self.__socket = None + logger.fatal(BIND10_SOCKCREATOR_BAD_RESPONSE, answer) raise CreatorError('Unknown response ' + str(answer), True) except socket.error as se: self.__socket = None + logger.fatal(BIND10_SOCKCREATOR_TRANSPORT_ERROR, str(se)) raise CreatorError(str(se), True) def __read_all(self, length): @@ -148,6 +162,7 @@ class Parser: data = self.__socket.recv(length - len(result)) if len(data) == 0: self.__socket = None + logger.fatal(BIND10_SOCKCREATOR_EOF) raise CreatorError('Unexpected EOF', True) result += data return result diff --git a/src/bin/bind10/tests/sockcreator_test.py b/src/bin/bind10/tests/sockcreator_test.py index fee691fc25..d61254a8bb 100644 --- a/src/bin/bind10/tests/sockcreator_test.py +++ b/src/bin/bind10/tests/sockcreator_test.py @@ -22,6 +22,7 @@ import struct import socket from bind10.sockcreator import Parser, CreatorError from isc.net.addr import IPAddr +import isc.log class FakeCreator: """ @@ -262,10 +263,16 @@ class ParserTests(unittest.TestCase): """ Test it rejects invalid address family. """ + # Note: this produces a bad logger output, since this address + # can not be converted to string, so the original message with + # placeholders is output. This should not happen in practice, so + # it is harmless. addr = IPAddr('0.0.0.0') - addr.family = 'Nonsense' + addr.family = 42 self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket, addr, 42, socket.SOCK_DGRAM) if __name__ == '__main__': + isc.log.init("bind10") # FIXME Should this be needed? + isc.log.resetUnitTestRootLogger() unittest.main() From 3da7e8747dcea9b45c8bc4c17b946be7d5ff9576 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 25 Jul 2011 14:40:06 +0200 Subject: [PATCH 258/974] [trac800] WrappedSocket --- src/bin/bind10/sockcreator.py | 27 +++++++++++++++++- src/bin/bind10/tests/Makefile.am | 2 +- src/bin/bind10/tests/sockcreator_test.py | 36 +++++++++++++++++++++++- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/bin/bind10/sockcreator.py b/src/bin/bind10/sockcreator.py index 36815efdb8..0bf750fa26 100644 --- a/src/bin/bind10/sockcreator.py +++ b/src/bin/bind10/sockcreator.py @@ -17,6 +17,7 @@ import socket import struct import os from bind10_messages import * +from libutil_io_python import recv_fd logger = isc.log.Logger("boss") @@ -60,7 +61,10 @@ class Parser: Creates the parser. The creator_socket is socket to the socket creator process that will be used for communication. However, the object must have a read_fd() method to read the file descriptor. This slightly - unusual modification of socket object is used to easy up testing. + unusual trick with modifying an object is used to easy up testing. + + You can use WrappedSocket in production code to add the method to any + ordinary socket. """ self.__socket = creator_socket logger.info(BIND10_SOCKCREATOR_INIT) @@ -166,3 +170,24 @@ class Parser: raise CreatorError('Unexpected EOF', True) result += data return result + +class WrappedSocket: + """ + This class wraps a socket and adds a read_fd method, so it can be used + for the Parser class conveniently. It simply copies all it's guts into + itself and implements the method. + """ + def __init__(self, socket): + # Copy whatever can be copied from the socket + for name in dir(socket): + if name not in ['__class__', '__weakref__']: + setattr(self, name, getattr(socket, name)) + # Keep the socket, so we can prevent it from being garbage-collected + # and closed before we are removed ourself + self.__orig_socket = socket + + def read_fd(self): + """ + Read the file descriptor from the socket. + """ + return recv_fd(self.fileno()) diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index 3fe357e264..1cbd841012 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -21,7 +21,7 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10 \ + env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ done diff --git a/src/bin/bind10/tests/sockcreator_test.py b/src/bin/bind10/tests/sockcreator_test.py index d61254a8bb..c863034392 100644 --- a/src/bin/bind10/tests/sockcreator_test.py +++ b/src/bin/bind10/tests/sockcreator_test.py @@ -20,9 +20,10 @@ Tests for the bind10.sockcreator module. import unittest import struct import socket -from bind10.sockcreator import Parser, CreatorError from isc.net.addr import IPAddr import isc.log +from libutil_io_python import send_fd +from bind10.sockcreator import Parser, CreatorError, WrappedSocket class FakeCreator: """ @@ -272,6 +273,39 @@ class ParserTests(unittest.TestCase): self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket, addr, 42, socket.SOCK_DGRAM) +class WrapTests(unittest.TestCase): + """ + Tests for the wrap_socket function. + """ + def test_wrap(self): + # We construct two pairs of socket. The receiving side of one pair will + # be wrapped. Then we send one of the other pair through this pair and + # check the received one can be used as a socket + + # The transport socket + (t1, t2) = socket.socketpair() + # The payload socket + (p1, p2) = socket.socketpair() + + t2 = WrappedSocket(t2) + + # Transfer the descriptor + send_fd(t1.fileno(), p1.fileno()) + p1 = socket.fromfd(t2.read_fd(), socket.AF_UNIX, socket.SOCK_STREAM) + + # Now, pass some data trough the socket + p1.send(b'A') + data = p2.recv(1) + self.assertEqual(b'A', data) + + # Test the wrapping didn't hurt the socket's usual methods + t1.send(b'B') + data = t2.recv(1) + self.assertEqual(b'B', data) + t2.send(b'C') + data = t1.recv(1) + self.assertEqual(b'C', data) + if __name__ == '__main__': isc.log.init("bind10") # FIXME Should this be needed? isc.log.resetUnitTestRootLogger() From 9517f61cb8ad4f8074b5e6e33c663ca9ed581908 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 25 Jul 2011 18:02:06 +0200 Subject: [PATCH 259/974] [trac800] Actually starting the creator --- src/bin/bind10/bind10_messages.mes | 9 +++++++ src/bin/bind10/bind10_src.py.in | 34 +++++++++++++++++++++++++- src/bin/bind10/sockcreator.py | 31 +++++++++++++++++++++++ src/bin/bind10/tests/bind10_test.py.in | 8 ++++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 392b6c7057..a596147a08 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -189,3 +189,12 @@ the given file number. Either sending or receiving data from the socket creator failed with the given error. The creator probably crashed or some serious OS-level problem happened, as the communication happens only on local host. + +% BIND10_SOCKCREATOR_CRASHED the socket creator crashed +The socket creator terminated unexpectadly. It is not possible to restart it +(because the boss already gave up root privileges), so the system is going +to terminate. + +% BIND10_SOCKCREATOR_KILL killing the socket creator +The socket creator is being terminated the aggressive way, by sending it +sigkill. This should not happen usually. diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index a624383da6..bbb17a2347 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -67,6 +67,7 @@ import isc.util.process import isc.net.parse import isc.log from bind10_messages import * +import bind10.sockcreator isc.log.init("b10-boss") logger = isc.log.Logger("boss") @@ -248,6 +249,7 @@ class BoB: self.config_filename = config_filename self.cmdctl_port = cmdctl_port self.brittle = brittle + self.sockcreator = None def config_handler(self, new_config): # If this is initial update, don't do anything now, leave it to startup @@ -333,6 +335,20 @@ class BoB: "Unknown command") return answer + def start_creator(self): + self.curproc = 'b10-sockcreator' + self.sockcreator = bind10.sockcreator.Creator("@@LIBEXECDIR@@:" + + os.environ['PATH']) + + def stop_creator(self, kill=False): + if self.sockcreator is None: + return + if kill: + self.sockcreator.kill() + else: + self.sockcreator.terminate() + self.sockcreator = None + def kill_started_processes(self): """ Called as part of the exception handling when a process fails to @@ -341,6 +357,8 @@ class BoB: """ logger.info(BIND10_KILLING_ALL_PROCESSES) + self.stop_creator(True) + for pid in self.processes: logger.info(BIND10_KILL_PROCESS, self.processes[pid].name) self.processes[pid].process.kill() @@ -571,6 +589,11 @@ class BoB: Starts up all the processes. Any exception generated during the starting of the processes is handled by the caller. """ + # The socket creator first, as it is the only thing that needs root + self.start_creator() + # TODO: Once everything uses the socket creator, we can drop root + # privileges right now + c_channel_env = self.c_channel_env self.start_msgq(c_channel_env) self.start_cfgmgr(c_channel_env) @@ -660,6 +683,8 @@ class BoB: self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr") self.cc_session.group_sendmsg(cmd, "Stats", "Stats") self.cc_session.group_sendmsg(cmd, "StatsHttpd", "StatsHttpd") + # Terminate the creator last + self.stop_creator() def stop_process(self, process, recipient): """ @@ -746,7 +771,14 @@ class BoB: # XXX: should be impossible to get any other error here raise if pid == 0: break - if pid in self.processes: + if self.sockcreator is not None and self.sockcreator.pid() == pid: + # This is the socket creator, started and terminated + # differently. This can't be restarted. + if self.runnable: + logger.fatal(BIND10_SOCKCREATOR_CRASHED) + self.sockcreator = None + self.runnable = False + elif pid in self.processes: # One of the processes we know about. Get information on it. proc_info = self.processes.pop(pid) proc_info.restart_schedule.set_run_stop_time() diff --git a/src/bin/bind10/sockcreator.py b/src/bin/bind10/sockcreator.py index 0bf750fa26..baccc95ed9 100644 --- a/src/bin/bind10/sockcreator.py +++ b/src/bin/bind10/sockcreator.py @@ -16,6 +16,7 @@ import socket import struct import os +import subprocess from bind10_messages import * from libutil_io_python import recv_fd @@ -191,3 +192,33 @@ class WrappedSocket: Read the file descriptor from the socket. """ return recv_fd(self.fileno()) + +# FIXME: Any idea how to test this? Starting an external process doesn't sound +# OK +class Creator(Parser): + """ + This starts the socket creator and allows asking for the sockets. + """ + def __init__(self, path): + (local, remote) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) + # Popen does not like, for some reason, having the same socket for + # stdin as well as stdout, so we dup it before passing it there. + remote2 = socket.fromfd(remote.fileno(), socket.AF_UNIX, + socket.SOCK_STREAM) + env = os.environ + env['PATH'] = path + self.__process = subprocess.Popen(['b10-sockcreator'], env=env, + stdin=remote.fileno(), + stdout=remote2.fileno()) + remote.close() + remote2.close() + Parser.__init__(self, WrappedSocket(local)) + + def pid(self): + return self.__process.pid + + def kill(self): + logger.warn(BIND10_SOCKCREATOR_KILL) + if self.__process is not None: + self.__process.kill() + self.__process = None diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 6b871334b8..077190c865 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -193,6 +193,13 @@ class MockBob(BoB): self.cmdctl = False self.c_channel_env = {} self.processes = { } + self.creator = False + + def start_creator(self): + self.creator = True + + def stop_creator(self, kill=False): + self.creator = False def read_bind10_config(self): # Configuration options are set directly @@ -337,6 +344,7 @@ class TestStartStopProcessesBob(unittest.TestCase): self.assertEqual(bob.msgq, core) self.assertEqual(bob.cfgmgr, core) self.assertEqual(bob.ccsession, core) + self.assertEqual(bob.creator, core) self.assertEqual(bob.auth, auth) self.assertEqual(bob.resolver, resolver) self.assertEqual(bob.xfrout, auth) From 0710846d8d7a38079b9570aeec9abfb94341af79 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 25 Jul 2011 18:15:43 +0200 Subject: [PATCH 260/974] [trac800] Fix tests depending on boss Why do they include boss? --- src/bin/dhcp6/tests/Makefile.am | 2 +- src/bin/dhcp6/tests/dhcp6_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index a35284fbb4..219dcff42a 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -15,7 +15,7 @@ endif check-local: for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \ + env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ $(LIBRARY_PATH_PLACEHOLDER) \ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py index 61ec009606..5ae1f5eba5 100644 --- a/src/bin/dhcp6/tests/dhcp6_test.py +++ b/src/bin/dhcp6/tests/dhcp6_test.py @@ -13,7 +13,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -from bind10 import ProcessInfo, parse_args, dump_pid, unlink_pid_file, _BASETIME +from bind10_src import ProcessInfo, parse_args, dump_pid, unlink_pid_file, _BASETIME import unittest import sys From c62810c526d75363ed4d668bbdb6b21a5a294a7b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 25 Jul 2011 18:38:53 +0200 Subject: [PATCH 261/974] [trac800] Makefile fixes --- configure.ac | 1 + src/bin/bind10/tests/Makefile.am | 1 - .../tests/{sockcreator_test.py => sockcreator_test.py.in} | 3 +++ 3 files changed, 4 insertions(+), 1 deletion(-) rename src/bin/bind10/tests/{sockcreator_test.py => sockcreator_test.py.in} (98%) diff --git a/configure.ac b/configure.ac index 9ee534829e..a554db11a5 100644 --- a/configure.ac +++ b/configure.ac @@ -905,6 +905,7 @@ AC_OUTPUT([doc/version.ent src/bin/bind10/bind10_src.py src/bin/bind10/run_bind10.sh src/bin/bind10/tests/bind10_test.py + src/bin/bind10/tests/sockcreator_test.py src/bin/bindctl/run_bindctl.sh src/bin/bindctl/bindctl_main.py src/bin/bindctl/tests/bindctl_test diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index 1cbd841012..4a40ec89d2 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -2,7 +2,6 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ #PYTESTS = args_test.py bind10_test.py # NOTE: this has a generated test found in the builddir PYTESTS = bind10_test.py sockcreator_test.py -EXTRA_DIST = $(PYTESTS) # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. diff --git a/src/bin/bind10/tests/sockcreator_test.py b/src/bin/bind10/tests/sockcreator_test.py.in similarity index 98% rename from src/bin/bind10/tests/sockcreator_test.py rename to src/bin/bind10/tests/sockcreator_test.py.in index c863034392..7fb522fd91 100644 --- a/src/bin/bind10/tests/sockcreator_test.py +++ b/src/bin/bind10/tests/sockcreator_test.py.in @@ -13,6 +13,9 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# This test file is generated .py.in -> .py just to be in the build dir, +# same as the rest of the tests. Saves a lot of stuff in makefile. + """ Tests for the bind10.sockcreator module. """ From 12186e267fb75a77027dc046f78db6ace99b8571 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 26 Jul 2011 15:00:33 +0200 Subject: [PATCH 262/974] [trac800] fix a few typos --- src/bin/bind10/bind10_messages.mes | 2 +- src/bin/bind10/sockcreator.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index a596147a08..bad17022c0 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -171,7 +171,7 @@ It probably crashed. % BIND10_SOCKCREATOR_BAD_RESPONSE unknown response for socket request: %1 The boss requested a socket from the creator, but the answer is unknown. This -looks like programmer error. +looks like a programmer error. % BIND10_SOCKET_ERROR error on %1 call in the creator: %2/%3 The socket creator failed to create the requested socket. It failed on the diff --git a/src/bin/bind10/sockcreator.py b/src/bin/bind10/sockcreator.py index baccc95ed9..9fcc74e74c 100644 --- a/src/bin/bind10/sockcreator.py +++ b/src/bin/bind10/sockcreator.py @@ -23,7 +23,7 @@ from libutil_io_python import recv_fd logger = isc.log.Logger("boss") """ -Module that comunicates with the priviledget socket creator (b10-sockcreator). +Module that comunicates with the privileged socket creator (b10-sockcreator). """ class CreatorError(Exception): @@ -54,7 +54,7 @@ class Parser: In theory, anything here can throw a fatal CreatorError exception, but it happens only in case something like the creator process crashes. Any other - occations are mentioned explicitly. + occasions are mentioned explicitly. """ def __init__(self, creator_socket): @@ -73,7 +73,9 @@ class Parser: def terminate(self): """ Asks the creator process to terminate and waits for it to close the - socket. Does not return anything. + socket. Does not return anything. Raises a CreatorError if there is + still data on the socket, if there is an error closing the socket, + or if the socket had already been closed. """ if self.__socket is None: raise CreatorError('Terminated already', True) @@ -160,7 +162,7 @@ class Parser: """ Keeps reading until length data is read or EOF or error happens. - EOF is considered error as well and throws. + EOF is considered error as well and throws a CreatorError. """ result = b'' while len(result) < length: @@ -175,7 +177,7 @@ class Parser: class WrappedSocket: """ This class wraps a socket and adds a read_fd method, so it can be used - for the Parser class conveniently. It simply copies all it's guts into + for the Parser class conveniently. It simply copies all its guts into itself and implements the method. """ def __init__(self, socket): From e05a3418c9d6b3f70cdb387d1f30d8ba59733f02 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 26 Jul 2011 15:51:13 +0200 Subject: [PATCH 263/974] [trac801] Creator API --- src/bin/bind10/creatorapi.txt | 80 +++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/bin/bind10/creatorapi.txt diff --git a/src/bin/bind10/creatorapi.txt b/src/bin/bind10/creatorapi.txt new file mode 100644 index 0000000000..a55099e01b --- /dev/null +++ b/src/bin/bind10/creatorapi.txt @@ -0,0 +1,80 @@ +Socket creator API +================== + +This API is between Boss and other modules to allow them requesting of sockets. +For simplicity, we will use the socket creator for all (even non-privileged) +ports for now, but we should have some function where we can abstract it later. + +Goals +----- +* Be able to request a socket of any combination IP/IPv6 UDP/TCP bound to given + port and address (sockets that are not bound to anything can be created + without privileges, therefore are not requested from the socket creator). +* Allow to provide the same socket to multiple modules (eg. multiple running + auth servers). +* Allow releasing the sockets (in case all modules using it give it up, + terminate or crash). +* Allow restricting of the sharing (don't allow shared socket between auth + and recursive, as the packets would often get to the wrong application, + show error instead). +* Get the socket to the application. + +Transport of sockets +-------------------- +It seems we are stuck with current msgq for a while and there's a chance the +new replacement will not be able to send sockets inbound. So, we need another +channel. + +The boss will create a unix-domain socket and listen on it. When something +requests a socket over the command channel and the socket is created, some kind +of token is returned to the application (which will represent the future +socket). The application then connects to the unix-domain socket, sends the +token over the connection (so Boss will know which socket to send there, in case +multiple applications ask for sockets simultaneously) and Boss sends the socket +in return. + +Caching of sockets +------------------ +To allow sending the same socket to multiple application, the Boss process will +hold a cache. Each socket that is created and sent is kept open in Boss and +preserved there as well. A reference count is kept with each of them. + +When another application asks for the same socket, it is simply sent from the +cache instead of creating it again by the creator. + +When application gives the socket willingly (by sending a message over the +command channel), the reference count can be decreased without problems. But +when the application terminates or crashes, we need to decrease it as well. +There's a problem, since we don't know which command channel connection (eg. +lname) belongs to which PID. Furthermore, the applications don't need to be +started by boss. + +There are two possibilities: +* Let the msgq send messages about disconnected clients (eg. group message to + some name). This one is better if we want to migrate to dbus, since dbus + already has this capability as well as sending the sockets inbound (at last it + seems so on unix) and we could get rid of the unix-domain socket completely. +* Keep the unix-domain connections open forever. Boss can remember which socket + was sent to which connection and when the connection closes (because the + application crashed), it can drop all the references on the sockets. This + seems easier to implement. + +The commands +------------ +* Command to release a socket. This one would have single parameter, the token + used to get the socket. After this, boss would decrease its reference count + and if it drops to zero, close its own copy of the socket. This should be used + when the module stops using the socket (and after closes it). +* Command to request a socket. It would have parameters to specify which socket + (IP address, address family, port) and how to allow sharing. Sharing would be + one of: + - None + - Same kind of application + - Any kind of application + And a kind of application would be provided, to decide if the sharing is + possible (eg. if auth allows sharing with the same kind and something else + allows sharing with anything, the sharing is not possible, two auths can). + + It would return either error (the socket can't be created or sharing is not + possible) or the token. Then there would be some time for the application to + pick up the requested socket. From 69336de84b2ae1b5b6a59fa8d817daa1108cea27 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 26 Jul 2011 16:19:51 +0200 Subject: [PATCH 264/974] [trac800] Sort the message definitions --- src/bin/bind10/bind10_messages.mes | 108 ++++++++++++++--------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index bad17022c0..e10bc7c7b7 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -32,15 +32,15 @@ started according to the configuration. The boss process was started with the -u option, to drop root privileges and continue running as the specified user, but the user is unknown. +% BIND10_KILLING_ALL_PROCESSES killing all started processes +The boss module was not able to start every process it needed to start +during startup, and will now kill the processes that did get started. + % BIND10_KILL_PROCESS killing process %1 The boss module is sending a kill signal to process with the given name, as part of the process of killing all started processes during a failed startup, as described for BIND10_KILLING_ALL_PROCESSES -% BIND10_KILLING_ALL_PROCESSES killing all started processes -The boss module was not able to start every process it needed to start -during startup, and will now kill the processes that did get started. - % BIND10_MSGQ_ALREADY_RUNNING msgq daemon already running, cannot start There already appears to be a message bus daemon running. Either an old process was not shut down correctly, and needs to be killed, or @@ -113,12 +113,49 @@ it shall send SIGKILL signals to the processes still alive. All child processes have been stopped, and the boss process will now stop itself. -% BIND10_START_AS_NON_ROOT starting %1 as a user, not root. This might fail. -The given module is being started or restarted without root privileges. -If the module needs these privileges, it may have problems starting. -Note that this issue should be resolved by the pending 'socket-creator' -process; once that has been implemented, modules should not need root -privileges anymore. See tickets #800 and #801 for more information. +% BIND10_SOCKCREATOR_BAD_CAUSE unknown error cause from socket creator: %1 +The socket creator reported an error when creating a socket. But the function +which failed is unknown (not one of 'S' for socket or 'B' for bind). + +% BIND10_SOCKCREATOR_BAD_RESPONSE unknown response for socket request: %1 +The boss requested a socket from the creator, but the answer is unknown. This +looks like a programmer error. + +% BIND10_SOCKCREATOR_CRASHED the socket creator crashed +The socket creator terminated unexpectadly. It is not possible to restart it +(because the boss already gave up root privileges), so the system is going +to terminate. + +% BIND10_SOCKCREATOR_EOF eof while expecting data from socket creator +There should be more data from the socket creator, but it closed the socket. +It probably crashed. + +% BIND10_SOCKCREATOR_INIT initializing socket creator parser +The boss module initializes routines for parsing the socket creator +protocol. + +% BIND10_SOCKCREATOR_KILL killing the socket creator +The socket creator is being terminated the aggressive way, by sending it +sigkill. This should not happen usually. + +% BIND10_SOCKCREATOR_TERMINATE terminating socket creator +The boss module sends a request to terminate to the socket creator. + +% BIND10_SOCKCREATOR_TRANSPORT_ERROR transport error when talking to the socket creator: %1 +Either sending or receiving data from the socket creator failed with the given +error. The creator probably crashed or some serious OS-level problem happened, +as the communication happens only on local host. + +% BIND10_SOCKET_CREATED successfully created socket %1 +The socket creator successfully created and sent a requested socket, it has +the given file number. + +% BIND10_SOCKET_ERROR error on %1 call in the creator: %2/%3 +The socket creator failed to create the requested socket. It failed on the +indicated OS API function with given error. + +% BIND10_SOCKET_GET requesting socket [%1]:%2 of type %3 from the creator +The boss forwards a request for a socket to the socket creator. % BIND10_STARTED_PROCESS started %1 The given process has successfully been started. @@ -147,6 +184,13 @@ All modules have been successfully started, and BIND 10 is now running. There was a fatal error when BIND10 was trying to start. The error is shown, and BIND10 will now shut down. +% BIND10_START_AS_NON_ROOT starting %1 as a user, not root. This might fail. +The given module is being started or restarted without root privileges. +If the module needs these privileges, it may have problems starting. +Note that this issue should be resolved by the pending 'socket-creator' +process; once that has been implemented, modules should not need root +privileges anymore. See tickets #800 and #801 for more information. + % BIND10_STOP_PROCESS asking %1 to shut down The boss module is sending a shutdown command to the given module over the message channel. @@ -154,47 +198,3 @@ the message channel. % BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited An unknown child process has exited. The PID is printed, but no further action will be taken by the boss process. - -% BIND10_SOCKCREATOR_INIT initializing socket creator parser -The boss module initializes routines for parsing the socket creator -protocol. - -% BIND10_SOCKCREATOR_TERMINATE terminating socket creator -The boss module sends a request to terminate to the socket creator. - -% BIND10_SOCKET_GET requesting socket [%1]:%2 of type %3 from the creator -The boss forwards a request for a socket to the socket creator. - -% BIND10_SOCKCREATOR_EOF eof while expecting data from socket creator -There should be more data from the socket creator, but it closed the socket. -It probably crashed. - -% BIND10_SOCKCREATOR_BAD_RESPONSE unknown response for socket request: %1 -The boss requested a socket from the creator, but the answer is unknown. This -looks like a programmer error. - -% BIND10_SOCKET_ERROR error on %1 call in the creator: %2/%3 -The socket creator failed to create the requested socket. It failed on the -indicated OS API function with given error. - -% BIND10_SOCKCREATOR_BAD_CAUSE unknown error cause from socket creator: %1 -The socket creator reported an error when creating a socket. But the function -which failed is unknown (not one of 'S' for socket or 'B' for bind). - -% BIND10_SOCKET_CREATED successfully created socket %1 -The socket creator successfully created and sent a requested socket, it has -the given file number. - -% BIND10_SOCKCREATOR_TRANSPORT_ERROR transport error when talking to the socket creator: %1 -Either sending or receiving data from the socket creator failed with the given -error. The creator probably crashed or some serious OS-level problem happened, -as the communication happens only on local host. - -% BIND10_SOCKCREATOR_CRASHED the socket creator crashed -The socket creator terminated unexpectadly. It is not possible to restart it -(because the boss already gave up root privileges), so the system is going -to terminate. - -% BIND10_SOCKCREATOR_KILL killing the socket creator -The socket creator is being terminated the aggressive way, by sending it -sigkill. This should not happen usually. From cb86d16418ced44b148726104c5c8f9d36a3be49 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 26 Jul 2011 16:46:09 +0200 Subject: [PATCH 265/974] [master] add LDADD for libdns which is needed by the tsig support --- src/lib/acl/tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/acl/tests/Makefile.am b/src/lib/acl/tests/Makefile.am index a08c99cd70..636951199b 100644 --- a/src/lib/acl/tests/Makefile.am +++ b/src/lib/acl/tests/Makefile.am @@ -31,6 +31,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests. run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la +run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la run_unittests_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la From c6ef5865b3fd8e5d5fb8c891467b3722fde4d685 Mon Sep 17 00:00:00 2001 From: reed Date: Tue, 26 Jul 2011 17:04:33 -0500 Subject: [PATCH 266/974] trac1011: a TODO to research for logging docs --- doc/guide/bind10-guide.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 6a4218207a..f297223296 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1498,6 +1498,7 @@ then change those defaults with config set Resolver/forward_addresses[0]/address 2011-06-15 13:48:22.034 + The date and time at which the message was generated. From ba7bc1e14fcf1a223a9a42ede2e9cd7d290c8b61 Mon Sep 17 00:00:00 2001 From: reed Date: Tue, 26 Jul 2011 17:06:50 -0500 Subject: [PATCH 267/974] trac1011: add Logging configuration docs This is a copy and paste from http://bind10.isc.org/wiki/LoggingConfigurationGuide No formatting or cleanup or XML-ization yet. --- doc/guide/bind10-guide.xml | 180 +++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index f297223296..22515c05fb 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1551,6 +1551,186 @@ then change those defaults with config set Resolver/forward_addresses[0]/address +Logging configuration ¶ + +The logging system in BIND 10 is configured through the Logging module. All BIND 10 modules will look at the configuration in Logging to see what should be logged and to where. +Loggers ¶ + +Within BIND 10, a message is logged through a component called a "logger". Different parts of BIND 10 log messages through different loggers, and each logger can be configured independently of one another. + +In the Logging module, you can specify the configuration for zero or more loggers; any that are not specified will take appropriate default values.. + +The three most important elements of a logger configuration are the name (the component that is generating the messages), the severity (what to log), and the output_options (where to log). +name (string) ¶ + +Each logger in the system has a name, the name being that of the component using it to log messages. For instance, if you want to configure logging for the resolver module, you add an entry for a logger named 'Resolver'. This configuration will then be used by the loggers in the Resolver module, and all the libraries used by it. + +If you want to specify logging for one specific library within the module, you set the name to 'module.library'. For example, the logger used by the nameserver address store component has the full name of 'Resolver.nsas'. If there is no entry in Logging for a particular library, it will use the configuration given for the module. + +To illustrate this, suppose you want the cache library to log messages of severity DEBUG, and the rest of the resolver code to log messages of severity INFO. To achieve this you specify two loggers, one with the name 'Resolver' and severity INFO, and one with the name 'Resolver.cache' with severity DEBUG. As there are no entries for other libraries (e.g. the nsas), they will use the configuration for the module ('Resolver'), so giving the desired behavior. + +One special case is that of a module name of '*', which is interpreted as 'any module'. You can set global logging options by using this, including setting the logging configuration for a library that is used by multiple modules (e.g. '*.config" specifies the configuration library code in whatever module is using it). + +If there are multiple logger specifications in the configuration that might match a particular logger, the specification with the more specific logger name takes precedence. For example, if there are entries for for both '*' and 'Resolver', the resolver module - and all libraries it uses - will log messages according to the configuration in the second entry ('Resolver'). All other modules will use the configuration of the first entry ('*'). If there was also a configuration entry for 'Resolver.cache', the cache library within the resolver would use that in preference to the entry for 'Resolver'. + +One final note about the naming. When specifying the module name within a logger, use the name of the module as specified in bindctl, e.g. 'Resolver' for the resolver module, 'Xfrout' for the xfrout module etc. When the message is logged, the message will include the name of the logger generating the message, but with the module name replaced by the name of the process implementing the module (so for example, a message generated by the 'Auth.cache' logger will appear in the output with a logger name of 'b10-auth.cache'). +severity (string) ¶ + +This specifies the category of messages logged. + +Each message is logged with an associated severity which may be one of the following (in descending order of severity): + + FATAL + ERROR + WARN + INFO + DEBUG + +When the severity of a logger is set to one of these values, it will only log messages of that severity, and the severities below it. The severity may also be set to NONE, in which case all messages from that logger are inhibited. +output_options (list) ¶ + +Each logger can have zero or more output_options. These specify where log messages are sent to. These are explained in detail below. + +The other options for a logger are: +debuglevel (integer) ¶ + +When a logger's severity is set to DEBUG, this value specifies what debug messages should be printed. It ranges from 0 (least verbose) to 99 (most verbose). The general classification of debug message types is + +TODO; there's a ticket to determine these levels, see #1074 + +If severity for the logger is not DEBUG, this value is ignored. +additive (true or false) ¶ + +If this is true, the output_options from the parent will be used. For example, if there are two loggers configured; 'Resolver' and 'Resolver.cache', and additive is true in the second, it will write the log messages not only to the destinations specified for 'Resolver.cache', but also to the destinations as specified in the output_options in the logger named Resolver'. + +TODO: check this +Output Options ¶ + +The main settings for an output option are the 'destination' and a value called 'output', the meaning of which depends on the destination that is set. +destination (string) ¶ + +The destination is the type of output. It can be one of: + + * console + * file + * syslog + +output (string) ¶ + +Depending on what is set as the output destination, this value is interpreted as follows: + + * destination is 'console' + 'output' must be one of 'stdout' (messages printed to standard output) or 'stderr' (messages printed to standard error). + + * destination is 'file' + The value of output is interpreted as a file name; log messages will be appended to this file. + + * destination is 'syslog' + The value of output is interpreted as the syslog facility (e.g. 'local0') that should be used for log messages. + +The other options for output_options are: +flush (true of false) ¶ + +Flush buffers after each log message. Doing this will reduce performance but will ensure that if the program terminates abnormally, all messages up to the point of termination are output. +maxsize (integer) ¶ + +Only relevant when destination is file, this is maximum file size of output files in bytes. When the maximum size is reached, the file is renamed (a ".1" is appended to the name - if a ".1" file exists, it is renamed ".2" etc.) and a new file opened. + +If this is 0, no maximum file size is used. +maxver (integer) ¶ + +Maximum number of old log files to keep around when rolling the output file. Only relevant when destination if 'file'. +Example session ¶ + +In this example we want to set the global logging to write to the file /var/log/my_bind10.log, at severity WARN. We want the authoritative server to log at DEBUG with debuglevel 40, to a different file (/tmp/debug_messages). + +Start bindctl + +["login success "] +> config show Logging +Logging/loggers [] list + +By default, no specific loggers are configured, in which case the severity defaults to INFO and the output is written to stderr. + +Let's first add a default logger; + +> config add Logging/loggers +> config show Logging +Logging/loggers/ list (modified) + +The loggers value line changed to indicate that it is no longer an empty list; + +> config show Logging/loggers +Logging/loggers[0]/name "" string (default) +Logging/loggers[0]/severity "INFO" string (default) +Logging/loggers[0]/debuglevel 0 integer (default) +Logging/loggers[0]/additive false boolean (default) +Logging/loggers[0]/output_options [] list (default) + +The name is mandatory, so we must set it. We will also change the severity as well. Let's start with the global logger. + +> config set Logging/loggers[0]/name * +> config set Logging/loggers[0]/severity WARN +> config show Logging/loggers +Logging/loggers[0]/name "*" string (modified) +Logging/loggers[0]/severity "WARN" string (modified) +Logging/loggers[0]/debuglevel 0 integer (default) +Logging/loggers[0]/additive false boolean (default) +Logging/loggers[0]/output_options [] list (default) + +Of course, we need to specify where we want the log messages to go, so we add an entry for an output option. + +> config add Logging/loggers[0]/output_options +> config show Logging/loggers[0]/output_options +Logging/loggers[0]/output_options[0]/destination "console" string (default) +Logging/loggers[0]/output_options[0]/output "stdout" string (default) +Logging/loggers[0]/output_options[0]/flush false boolean (default) +Logging/loggers[0]/output_options[0]/maxsize 0 integer (default) +Logging/loggers[0]/output_options[0]/maxver 0 integer (default) + +These aren't the values we are looking for. + +> config set Logging/loggers[0]/output_options[0]/destination file +> config set Logging/loggers[0]/output_options[0]/output /var/log/bind10.log +> config set Logging/loggers[0]/output_options[0]/maxsize 30000 +> config set Logging/loggers[0]/output_options[0]/maxver 8 + +Which would make the entire configuration for this logger look like: + +> config show all Logging/loggers +Logging/loggers[0]/name "*" string (modified) +Logging/loggers[0]/severity "WARN" string (modified) +Logging/loggers[0]/debuglevel 0 integer (default) +Logging/loggers[0]/additive false boolean (default) +Logging/loggers[0]/output_options[0]/destination "file" string (modified) +Logging/loggers[0]/output_options[0]/output "/var/log/bind10.log" string (modified) +Logging/loggers[0]/output_options[0]/flush false boolean (default) +Logging/loggers[0]/output_options[0]/maxsize 30000 integer (modified) +Logging/loggers[0]/output_options[0]/maxver 8 integer (modified) + +That looks OK, so let's commit it before we add the configuration for the authoritative server's logger. + +> config commit + +Now that we have set it, and checked each value along the way, adding a second entry is quite similar. + +> config add Logging/loggers +> config set Logging/loggers[1]/name Auth +> config set Logging/loggers[1]/severity DEBUG +> config set Logging/loggers[1]/debuglevel 40 +> config add Logging/loggers[1]/output_options +> config set Logging/loggers[1]/output_options[0]/destination file +> config set Logging/loggers[1]/output_options[0]/output /tmp/auth_debug.log +> config commit + +And that's it. Once we have found whatever it was we needed the debug messages for, we can simply remove the second logger to let the authoritative server use the same settings as the rest. + +> config remove Logging/loggers[1] +> config commit + +And every module will now be using the values from the logger named '*'. + + From 7b0201a4f98ee1b1288ae3b074cd1007707b6b21 Mon Sep 17 00:00:00 2001 From: reed Date: Tue, 26 Jul 2011 17:57:53 -0500 Subject: [PATCH 268/974] trac1011: some XML formatting Add some docbook formatting. This is not complete. --- doc/guide/bind10-guide.xml | 441 +++++++++++++++++++++++++++++++++---- 1 file changed, 397 insertions(+), 44 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 22515c05fb..98070c7450 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1550,34 +1550,110 @@ then change those defaults with config set Resolver/forward_addresses[0]/address + -Logging configuration ¶ +
+ Logging configuration -The logging system in BIND 10 is configured through the Logging module. All BIND 10 modules will look at the configuration in Logging to see what should be logged and to where. -Loggers ¶ + -Within BIND 10, a message is logged through a component called a "logger". Different parts of BIND 10 log messages through different loggers, and each logger can be configured independently of one another. + The logging system in BIND 10 is configured through the + Logging module. All BIND 10 modules will look at the + configuration in Logging to see what should be logged and + to where. + + + + + +
+ Loggers + + + + Within BIND 10, a message is logged through a component + called a "logger". Different parts of BIND 10 log messages + through different loggers, and each logger can be configured + independently of one another. + + + + + + In the Logging module, you can specify the configuration + for zero or more loggers; any that are not specified will + take appropriate default values.. + + + + -In the Logging module, you can specify the configuration for zero or more loggers; any that are not specified will take appropriate default values.. The three most important elements of a logger configuration are the name (the component that is generating the messages), the severity (what to log), and the output_options (where to log). -name (string) ¶ + + + + + + +name (string) Each logger in the system has a name, the name being that of the component using it to log messages. For instance, if you want to configure logging for the resolver module, you add an entry for a logger named 'Resolver'. This configuration will then be used by the loggers in the Resolver module, and all the libraries used by it. + + + + + If you want to specify logging for one specific library within the module, you set the name to 'module.library'. For example, the logger used by the nameserver address store component has the full name of 'Resolver.nsas'. If there is no entry in Logging for a particular library, it will use the configuration given for the module. + + + + + To illustrate this, suppose you want the cache library to log messages of severity DEBUG, and the rest of the resolver code to log messages of severity INFO. To achieve this you specify two loggers, one with the name 'Resolver' and severity INFO, and one with the name 'Resolver.cache' with severity DEBUG. As there are no entries for other libraries (e.g. the nsas), they will use the configuration for the module ('Resolver'), so giving the desired behavior. + + + + + One special case is that of a module name of '*', which is interpreted as 'any module'. You can set global logging options by using this, including setting the logging configuration for a library that is used by multiple modules (e.g. '*.config" specifies the configuration library code in whatever module is using it). + + + + + If there are multiple logger specifications in the configuration that might match a particular logger, the specification with the more specific logger name takes precedence. For example, if there are entries for for both '*' and 'Resolver', the resolver module - and all libraries it uses - will log messages according to the configuration in the second entry ('Resolver'). All other modules will use the configuration of the first entry ('*'). If there was also a configuration entry for 'Resolver.cache', the cache library within the resolver would use that in preference to the entry for 'Resolver'. + + + + + One final note about the naming. When specifying the module name within a logger, use the name of the module as specified in bindctl, e.g. 'Resolver' for the resolver module, 'Xfrout' for the xfrout module etc. When the message is logged, the message will include the name of the logger generating the message, but with the module name replaced by the name of the process implementing the module (so for example, a message generated by the 'Auth.cache' logger will appear in the output with a logger name of 'b10-auth.cache'). -severity (string) ¶ + + + + + + +severity (string) + + + + + This specifies the category of messages logged. + + + + + Each message is logged with an associated severity which may be one of the following (in descending order of severity): FATAL @@ -1586,118 +1662,355 @@ Each message is logged with an associated severity which may be one of the follo INFO DEBUG + + + + + When the severity of a logger is set to one of these values, it will only log messages of that severity, and the severities below it. The severity may also be set to NONE, in which case all messages from that logger are inhibited. -output_options (list) ¶ + + + + + + +output_options (list) + + + + + Each logger can have zero or more output_options. These specify where log messages are sent to. These are explained in detail below. + + + + + The other options for a logger are: -debuglevel (integer) ¶ + + + + + + +debuglevel (integer) + + + + + When a logger's severity is set to DEBUG, this value specifies what debug messages should be printed. It ranges from 0 (least verbose) to 99 (most verbose). The general classification of debug message types is + + + + + TODO; there's a ticket to determine these levels, see #1074 + + + + + If severity for the logger is not DEBUG, this value is ignored. -additive (true or false) ¶ + + + + + + +additive (true or false) + + + + + If this is true, the output_options from the parent will be used. For example, if there are two loggers configured; 'Resolver' and 'Resolver.cache', and additive is true in the second, it will write the log messages not only to the destinations specified for 'Resolver.cache', but also to the destinations as specified in the output_options in the logger named Resolver'. + + + + + TODO: check this -Output Options ¶ + + + +
+ +
+ Output Options + + The main settings for an output option are the 'destination' and a value called 'output', the meaning of which depends on the destination that is set. -destination (string) ¶ + + + + + + +destination (string) + + + + + The destination is the type of output. It can be one of: + + + + + * console * file * syslog -output (string) ¶ + + + + + +output (string) + + + + + Depending on what is set as the output destination, this value is interpreted as follows: + + + + + * destination is 'console' 'output' must be one of 'stdout' (messages printed to standard output) or 'stderr' (messages printed to standard error). + + + + + * destination is 'file' The value of output is interpreted as a file name; log messages will be appended to this file. + + + + + * destination is 'syslog' The value of output is interpreted as the syslog facility (e.g. 'local0') that should be used for log messages. + + + + + The other options for output_options are: -flush (true of false) ¶ + + + + + + +flush (true of false) + + + + + Flush buffers after each log message. Doing this will reduce performance but will ensure that if the program terminates abnormally, all messages up to the point of termination are output. -maxsize (integer) ¶ + + + + + + +maxsize (integer) + + + + + Only relevant when destination is file, this is maximum file size of output files in bytes. When the maximum size is reached, the file is renamed (a ".1" is appended to the name - if a ".1" file exists, it is renamed ".2" etc.) and a new file opened. + + + + + If this is 0, no maximum file size is used. -maxver (integer) ¶ + + + + + + +maxver (integer) + + + + + Maximum number of old log files to keep around when rolling the output file. Only relevant when destination if 'file'. -Example session ¶ + + + +
+ +
+ Example session + + In this example we want to set the global logging to write to the file /var/log/my_bind10.log, at severity WARN. We want the authoritative server to log at DEBUG with debuglevel 40, to a different file (/tmp/debug_messages). + + + + + Start bindctl -["login success "] -> config show Logging + + + + + + ["login success "] +> config show Logging Logging/loggers [] list + + + + + + By default, no specific loggers are configured, in which case the severity defaults to INFO and the output is written to stderr. + + + + + Let's first add a default logger; -> config add Logging/loggers -> config show Logging + + + + + + + > config add Logging/loggers +> config show Logging Logging/loggers/ list (modified) + + + + + + The loggers value line changed to indicate that it is no longer an empty list; -> config show Logging/loggers + + + + + > config show Logging/loggers Logging/loggers[0]/name "" string (default) Logging/loggers[0]/severity "INFO" string (default) Logging/loggers[0]/debuglevel 0 integer (default) Logging/loggers[0]/additive false boolean (default) Logging/loggers[0]/output_options [] list (default) + + + + + + The name is mandatory, so we must set it. We will also change the severity as well. Let's start with the global logger. -> config set Logging/loggers[0]/name * -> config set Logging/loggers[0]/severity WARN -> config show Logging/loggers + + + + + + > config set Logging/loggers[0]/name * +> config set Logging/loggers[0]/severity WARN +> config show Logging/loggers Logging/loggers[0]/name "*" string (modified) Logging/loggers[0]/severity "WARN" string (modified) Logging/loggers[0]/debuglevel 0 integer (default) Logging/loggers[0]/additive false boolean (default) Logging/loggers[0]/output_options [] list (default) + + + + + + Of course, we need to specify where we want the log messages to go, so we add an entry for an output option. -> config add Logging/loggers[0]/output_options -> config show Logging/loggers[0]/output_options + + + + + + > config add Logging/loggers[0]/output_options +> config show Logging/loggers[0]/output_options Logging/loggers[0]/output_options[0]/destination "console" string (default) Logging/loggers[0]/output_options[0]/output "stdout" string (default) Logging/loggers[0]/output_options[0]/flush false boolean (default) Logging/loggers[0]/output_options[0]/maxsize 0 integer (default) Logging/loggers[0]/output_options[0]/maxver 0 integer (default) + + + + + + These aren't the values we are looking for. -> config set Logging/loggers[0]/output_options[0]/destination file -> config set Logging/loggers[0]/output_options[0]/output /var/log/bind10.log -> config set Logging/loggers[0]/output_options[0]/maxsize 30000 -> config set Logging/loggers[0]/output_options[0]/maxver 8 + + + + + + > config set Logging/loggers[0]/output_options[0]/destination file +> config set Logging/loggers[0]/output_options[0]/output /var/log/bind10.log +> config set Logging/loggers[0]/output_options[0]/maxsize 30000 +> config set Logging/loggers[0]/output_options[0]/maxver 8 + + + + + Which would make the entire configuration for this logger look like: -> config show all Logging/loggers + + + + + > config show all Logging/loggers Logging/loggers[0]/name "*" string (modified) Logging/loggers[0]/severity "WARN" string (modified) Logging/loggers[0]/debuglevel 0 integer (default) @@ -1707,31 +2020,71 @@ Logging/loggers[0]/output_options[0]/output "/var/log/bind10.log" string (modifi Logging/loggers[0]/output_options[0]/flush false boolean (default) Logging/loggers[0]/output_options[0]/maxsize 30000 integer (modified) Logging/loggers[0]/output_options[0]/maxver 8 integer (modified) + + + + + That looks OK, so let's commit it before we add the configuration for the authoritative server's logger. -> config commit + + + + + + > config commit + + + + + Now that we have set it, and checked each value along the way, adding a second entry is quite similar. -> config add Logging/loggers -> config set Logging/loggers[1]/name Auth -> config set Logging/loggers[1]/severity DEBUG -> config set Logging/loggers[1]/debuglevel 40 -> config add Logging/loggers[1]/output_options -> config set Logging/loggers[1]/output_options[0]/destination file -> config set Logging/loggers[1]/output_options[0]/output /tmp/auth_debug.log -> config commit + + + + + + > config add Logging/loggers +> config set Logging/loggers[1]/name Auth +> config set Logging/loggers[1]/severity DEBUG +> config set Logging/loggers[1]/debuglevel 40 +> config add Logging/loggers[1]/output_options +> config set Logging/loggers[1]/output_options[0]/destination file +> config set Logging/loggers[1]/output_options[0]/output /tmp/auth_debug.log +> config commit + + + + + + And that's it. Once we have found whatever it was we needed the debug messages for, we can simply remove the second logger to let the authoritative server use the same settings as the rest. -> config remove Logging/loggers[1] -> config commit + + + + + + > config remove Logging/loggers[1] +> config commit + + + + + And every module will now be using the values from the logger named '*'. + + +
+ +
- From aa9497f4d2346e7a18cd07b9bf31dfb5832031bc Mon Sep 17 00:00:00 2001 From: reed Date: Tue, 26 Jul 2011 18:17:54 -0500 Subject: [PATCH 269/974] trac1011: more logging doc formatting --- doc/guide/bind10-guide.xml | 257 ++++++++++++++++++++++--------------- 1 file changed, 151 insertions(+), 106 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 98070c7450..4643bfb70e 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1588,53 +1588,98 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - -The three most important elements of a logger configuration are the name (the component that is generating the messages), the severity (what to log), and the output_options (where to log). + The three most important elements of a logger configuration + are the name (the component that is generating the + messages), the severity (what to log), and the output_options + (where to log). - name (string) -Each logger in the system has a name, the name being that of the component using it to log messages. For instance, if you want to configure logging for the resolver module, you add an entry for a logger named 'Resolver'. This configuration will then be used by the loggers in the Resolver module, and all the libraries used by it. + + + + + Each logger in the system has a name, the name being that + of the component using it to log messages. For instance, + if you want to configure logging for the resolver module, + you add an entry for a logger named 'Resolver'. This + configuration will then be used by the loggers in the + Resolver module, and all the libraries used by it. + + + If you want to specify logging for one specific library + within the module, you set the name to 'module.library'. + For example, the logger used by the nameserver address + store component has the full name of 'Resolver.nsas'. If + there is no entry in Logging for a particular library, + it will use the configuration given for the module. -If you want to specify logging for one specific library within the module, you set the name to 'module.library'. For example, the logger used by the nameserver address store component has the full name of 'Resolver.nsas'. If there is no entry in Logging for a particular library, it will use the configuration given for the module. + - -To illustrate this, suppose you want the cache library to log messages of severity DEBUG, and the rest of the resolver code to log messages of severity INFO. To achieve this you specify two loggers, one with the name 'Resolver' and severity INFO, and one with the name 'Resolver.cache' with severity DEBUG. As there are no entries for other libraries (e.g. the nsas), they will use the configuration for the module ('Resolver'), so giving the desired behavior. - + To illustrate this, suppose you want the cache library + to log messages of severity DEBUG, and the rest of the + resolver code to log messages of severity INFO. To achieve + this you specify two loggers, one with the name 'Resolver' + and severity INFO, and one with the name 'Resolver.cache' + with severity DEBUG. As there are no entries for other + libraries (e.g. the nsas), they will use the configuration + for the module ('Resolver'), so giving the desired + behavior. -One special case is that of a module name of '*', which is interpreted as 'any module'. You can set global logging options by using this, including setting the logging configuration for a library that is used by multiple modules (e.g. '*.config" specifies the configuration library code in whatever module is using it). + One special case is that of a module name of '*', which + is interpreted as 'any module'. You can set global logging + options by using this, including setting the logging + configuration for a library that is used by multiple + modules (e.g. '*.config" specifies the configuration + library code in whatever module is using it). - -If there are multiple logger specifications in the configuration that might match a particular logger, the specification with the more specific logger name takes precedence. For example, if there are entries for for both '*' and 'Resolver', the resolver module - and all libraries it uses - will log messages according to the configuration in the second entry ('Resolver'). All other modules will use the configuration of the first entry ('*'). If there was also a configuration entry for 'Resolver.cache', the cache library within the resolver would use that in preference to the entry for 'Resolver'. + If there are multiple logger specifications in the + configuration that might match a particular logger, the + specification with the more specific logger name takes + precedence. For example, if there are entries for for + both '*' and 'Resolver', the resolver module - and all + libraries it uses - will log messages according to the + configuration in the second entry ('Resolver'). All other + modules will use the configuration of the first entry + ('*'). If there was also a configuration entry for + 'Resolver.cache', the cache library within the resolver + would use that in preference to the entry for 'Resolver'. - -One final note about the naming. When specifying the module name within a logger, use the name of the module as specified in bindctl, e.g. 'Resolver' for the resolver module, 'Xfrout' for the xfrout module etc. When the message is logged, the message will include the name of the logger generating the message, but with the module name replaced by the name of the process implementing the module (so for example, a message generated by the 'Auth.cache' logger will appear in the output with a logger name of 'b10-auth.cache'). - + One final note about the naming. When specifying the + module name within a logger, use the name of the module + as specified in bindctl, e.g. 'Resolver' for the resolver + module, 'Xfrout' for the xfrout module etc. When the + message is logged, the message will include the name of + the logger generating the message, but with the module + name replaced by the name of the process implementing + the module (so for example, a message generated by the + 'Auth.cache' logger will appear in the output with a + logger name of 'b10-auth.cache'). @@ -1642,19 +1687,19 @@ One final note about the naming. When specifying the module name within a logger severity (string) + + + + + This specifies the category of messages logged. -This specifies the category of messages logged. - - - - - - -Each message is logged with an associated severity which may be one of the following (in descending order of severity): + Each message is logged with an associated severity which + may be one of the following (in descending order of + severity): FATAL ERROR @@ -1662,13 +1707,17 @@ Each message is logged with an associated severity which may be one of the follo INFO DEBUG - -When the severity of a logger is set to one of these values, it will only log messages of that severity, and the severities below it. The severity may also be set to NONE, in which case all messages from that logger are inhibited. + When the severity of a logger is set to one of these + values, it will only log messages of that severity, and + the severities below it. The severity may also be set to + NONE, in which case all messages from that logger are + inhibited. + @@ -1680,16 +1729,15 @@ output_options (list) - -Each logger can have zero or more output_options. These specify where log messages are sent to. These are explained in detail below. - + Each logger can have zero or more output_options. These + specify where log messages are sent to. These are explained + in detail below. -The other options for a logger are: - + The other options for a logger are: @@ -1701,23 +1749,20 @@ debuglevel (integer) + When a logger's severity is set to DEBUG, this value + specifies what debug messages should be printed. It ranges + from 0 (least verbose) to 99 (most verbose). The general + classification of debug message types is -When a logger's severity is set to DEBUG, this value specifies what debug messages should be printed. It ranges from 0 (least verbose) to 99 (most verbose). The general classification of debug message types is - + - - -TODO; there's a ticket to determine these levels, see #1074 - - - + -If severity for the logger is not DEBUG, this value is ignored. - + If severity for the logger is not DEBUG, this value is ignored. @@ -1725,19 +1770,19 @@ If severity for the logger is not DEBUG, this value is ignored. additive (true or false) - -If this is true, the output_options from the parent will be used. For example, if there are two loggers configured; 'Resolver' and 'Resolver.cache', and additive is true in the second, it will write the log messages not only to the destinations specified for 'Resolver.cache', but also to the destinations as specified in the output_options in the logger named Resolver'. + If this is true, the output_options from the parent will + be used. For example, if there are two loggers configured; + 'Resolver' and 'Resolver.cache', and additive is true in + the second, it will write the log messages not only to + the destinations specified for 'Resolver.cache', but also + to the destinations as specified in the output_options + in the logger named Resolver'. - - - - - -TODO: check this + @@ -1748,8 +1793,9 @@ TODO: check this -The main settings for an output option are the 'destination' and a value called 'output', the meaning of which depends on the destination that is set. - + The main settings for an output option are the 'destination' + and a value called 'output', the meaning of which depends + on the destination that is set. @@ -1757,13 +1803,11 @@ The main settings for an output option are the 'destination' and a value called destination (string) - -The destination is the type of output. It can be one of: - + The destination is the type of output. It can be one of: @@ -1777,30 +1821,29 @@ The destination is the type of output. It can be one of: - output (string) - -Depending on what is set as the output destination, this value is interpreted as follows: + Depending on what is set as the output destination, this + value is interpreted as follows: - * destination is 'console' - 'output' must be one of 'stdout' (messages printed to standard output) or 'stderr' (messages printed to standard error). + 'output' must be one of 'stdout' (messages printed to standard output) or 'stderr' (messages printed to standard error). * destination is 'file' + The value of output is interpreted as a file name; log messages will be appended to this file. @@ -1809,15 +1852,14 @@ Depending on what is set as the output destination, this value is interpreted as * destination is 'syslog' + The value of output is interpreted as the syslog facility (e.g. 'local0') that should be used for log messages. - -The other options for output_options are: - + The other options for output_options are: @@ -1825,13 +1867,14 @@ The other options for output_options are: flush (true of false) - -Flush buffers after each log message. Doing this will reduce performance but will ensure that if the program terminates abnormally, all messages up to the point of termination are output. - + Flush buffers after each log message. Doing this will + reduce performance but will ensure that if the program + terminates abnormally, all messages up to the point of + termination are output. @@ -1839,20 +1882,21 @@ Flush buffers after each log message. Doing this will reduce performance but wil maxsize (integer) + + + + + Only relevant when destination is file, this is maximum + file size of output files in bytes. When the maximum size + is reached, the file is renamed (a ".1" is appended to + the name - if a ".1" file exists, it is renamed ".2" + etc.) and a new file opened. -Only relevant when destination is file, this is maximum file size of output files in bytes. When the maximum size is reached, the file is renamed (a ".1" is appended to the name - if a ".1" file exists, it is renamed ".2" etc.) and a new file opened. - - - - - - -If this is 0, no maximum file size is used. - + If this is 0, no maximum file size is used. @@ -1860,12 +1904,13 @@ If this is 0, no maximum file size is used. maxver (integer) - -Maximum number of old log files to keep around when rolling the output file. Only relevant when destination if 'file'. + Maximum number of old log files to keep around when + rolling the output file. Only relevant when destination + if 'file'. @@ -1876,8 +1921,10 @@ Maximum number of old log files to keep around when rolling the output file. Onl -In this example we want to set the global logging to write to the file /var/log/my_bind10.log, at severity WARN. We want the authoritative server to log at DEBUG with debuglevel 40, to a different file (/tmp/debug_messages). - + In this example we want to set the global logging to + write to the file /var/log/my_bind10.log, at severity + WARN. We want the authoritative server to log at DEBUG + with debuglevel 40, to a different file (/tmp/debug_messages). @@ -1885,7 +1932,6 @@ In this example we want to set the global logging to write to the file /var/log/ Start bindctl - @@ -1895,20 +1941,19 @@ Start bindctl Logging/loggers [] list + + + + + By default, no specific loggers are configured, in which + case the severity defaults to INFO and the output is + written to stderr. -By default, no specific loggers are configured, in which case the severity defaults to INFO and the output is written to stderr. - - - - - - -Let's first add a default logger; - + Let's first add a default logger; @@ -1924,8 +1969,8 @@ Logging/loggers/ list (modified) - -The loggers value line changed to indicate that it is no longer an empty list; + The loggers value line changed to indicate that it is no + longer an empty list; @@ -1943,9 +1988,9 @@ Logging/loggers[0]/output_options [] list (default) - -The name is mandatory, so we must set it. We will also change the severity as well. Let's start with the global logger. - + The name is mandatory, so we must set it. We will also + change the severity as well. Let's start with the global + logger. @@ -1965,9 +2010,8 @@ Logging/loggers[0]/output_options [] list (default) - -Of course, we need to specify where we want the log messages to go, so we add an entry for an output option. - + Of course, we need to specify where we want the log + messages to go, so we add an entry for an output option. @@ -1987,8 +2031,7 @@ Logging/loggers[0]/output_options[0]/maxver 0 integer (default) -These aren't the values we are looking for. - + These aren't the values we are looking for. @@ -2004,7 +2047,8 @@ These aren't the values we are looking for. -Which would make the entire configuration for this logger look like: + Which would make the entire configuration for this logger + look like: @@ -2026,8 +2070,8 @@ Logging/loggers[0]/output_options[0]/maxver 8 integer (modified) -That looks OK, so let's commit it before we add the configuration for the authoritative server's logger. - + That looks OK, so let's commit it before we add the + configuration for the authoritative server's logger. @@ -2035,13 +2079,12 @@ That looks OK, so let's commit it before we add the configuration for the author > config commit - -Now that we have set it, and checked each value along the way, adding a second entry is quite similar. - + Now that we have set it, and checked each value along + the way, adding a second entry is quite similar. @@ -2061,9 +2104,10 @@ Now that we have set it, and checked each value along the way, adding a second e - -And that's it. Once we have found whatever it was we needed the debug messages for, we can simply remove the second logger to let the authoritative server use the same settings as the rest. - + And that's it. Once we have found whatever it was we + needed the debug messages for, we can simply remove the + second logger to let the authoritative server use the + same settings as the rest. @@ -2077,7 +2121,8 @@ And that's it. Once we have found whatever it was we needed the debug messages f -And every module will now be using the values from the logger named '*'. + And every module will now be using the values from the + logger named '*'. From 87a4f24037965ae88435ebe3f887750c500cbfde Mon Sep 17 00:00:00 2001 From: reed Date: Tue, 26 Jul 2011 18:18:49 -0500 Subject: [PATCH 270/974] trac1011: minor punctuation fix --- doc/guide/bind10-guide.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 4643bfb70e..309c35d5fe 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1953,7 +1953,7 @@ Logging/loggers [] list - Let's first add a default logger; + Let's first add a default logger: @@ -1970,7 +1970,7 @@ Logging/loggers/ list (modified) The loggers value line changed to indicate that it is no - longer an empty list; + longer an empty list: From ee4916a2db7ff1217c0af65f03220583b80b4568 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 27 Jul 2011 10:34:41 +0200 Subject: [PATCH 271/974] [master] Fix MacOS tests As always, they can't find libraries needed from C++ python wrappers. --- src/bin/bind10/tests/Makefile.am | 2 +- src/bin/dhcp6/tests/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index 4a40ec89d2..6d758b3e0e 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -7,7 +7,7 @@ PYTESTS = bind10_test.py sockcreator_test.py # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 219dcff42a..4a0e9186de 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -8,7 +8,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS From c18502d5a89af081b1cd4c4b1c112f9458056124 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 27 Jul 2011 11:13:14 +0200 Subject: [PATCH 272/974] [master] Make the tests work with python 3.1 Python 3.1 doesn't get the with self.assertRaises(CreatorError) as cm: right, it doesn't set the cm, as described in documentation. So it is reimplemented without this. --- src/bin/bind10/tests/sockcreator_test.py.in | 88 ++++++++++++--------- 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/src/bin/bind10/tests/sockcreator_test.py.in b/src/bin/bind10/tests/sockcreator_test.py.in index 7fb522fd91..53e7035e51 100644 --- a/src/bin/bind10/tests/sockcreator_test.py.in +++ b/src/bin/bind10/tests/sockcreator_test.py.in @@ -124,6 +124,11 @@ class FakeCreator: class ParserTests(unittest.TestCase): """ Testcases for the Parser class. + + A lot of these test could be done by + `with self.assertRaises(CreatorError) as cm`. But some versions of python + take the scope wrong and don't work, so we use the primitive way of + try-except. """ def __terminate(self): creator = FakeCreator([('s', b'T'), ('r', b'')]) @@ -139,6 +144,17 @@ class ParserTests(unittest.TestCase): """ self.__terminate() + def __terminate_raises(self, parser): + """ + Check that terminate() raises a fatal exception. + """ + try: + parser.terminate() + self.fail("Not raised") + except CreatorError as ce: + self.assertTrue(ce.fatal) + self.assertEqual(None, ce.errno) + def test_terminate_error1(self): """ Test it reports an exception when there's error terminating the creator. @@ -146,10 +162,7 @@ class ParserTests(unittest.TestCase): """ creator = FakeCreator([('s', b'T'), ('e', None)]) parser = Parser(creator) - with self.assertRaises(CreatorError) as cm: - parser.terminate() - self.assertTrue(cm.exception.fatal) - self.assertEqual(None, cm.exception.errno) + self.__terminate_raises(parser) def test_terminate_error2(self): """ @@ -158,20 +171,7 @@ class ParserTests(unittest.TestCase): """ creator = FakeCreator([('e', None)]) parser = Parser(creator) - with self.assertRaises(CreatorError) as cm: - parser.terminate() - self.assertTrue(cm.exception.fatal) - self.assertEqual(None, cm.exception.errno) - - def test_terminate_twice(self): - """ - Test we can't terminate twice. - """ - parser = self.__terminate() - with self.assertRaises(CreatorError) as cm: - parser.terminate() - self.assertTrue(cm.exception.fatal) - self.assertEqual(None, cm.exception.errno) + self.__terminate_raises(parser) def test_terminate_error3(self): """ @@ -180,10 +180,14 @@ class ParserTests(unittest.TestCase): """ creator = FakeCreator([('s', b'T'), ('r', b'Extra data')]) parser = Parser(creator) - with self.assertRaises(CreatorError) as cm: - parser.terminate() - self.assertTrue(cm.exception.fatal) - self.assertEqual(None, cm.exception.errno) + self.__terminate_raises(parser) + + def test_terminate_twice(self): + """ + Test we can't terminate twice. + """ + parser = self.__terminate() + self.__terminate_raises(parser) def test_crash(self): """ @@ -192,12 +196,14 @@ class ParserTests(unittest.TestCase): """ creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'')]) parser = Parser(creator) - with self.assertRaises(CreatorError) as cm: + try: parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP') - self.assertTrue(creator.all_used()) - # Is the exception correct? - self.assertTrue(cm.exception.fatal) - self.assertEqual(None, cm.exception.errno) + self.fail("Not raised") + except CreatorError as ce: + self.assertTrue(creator.all_used()) + # Is the exception correct? + self.assertTrue(ce.fatal) + self.assertEqual(None, ce.errno) def test_error(self): """ @@ -210,20 +216,24 @@ class ParserTests(unittest.TestCase): creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'ES' + intpart[:1]), ('r', intpart[1:])]) parser = Parser(creator) - with self.assertRaises(CreatorError) as cm: + try: parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP') - self.assertTrue(creator.all_used()) - # Is the exception correct? - self.assertFalse(cm.exception.fatal) - self.assertEqual(42, cm.exception.errno) + self.fail("Not raised") + except CreatorError as ce: + self.assertTrue(creator.all_used()) + # Is the exception correct? + self.assertFalse(ce.fatal) + self.assertEqual(42, ce.errno) def __error(self, plan): creator = FakeCreator(plan) parser = Parser(creator) - with self.assertRaises(CreatorError) as cm: + try: parser.get_socket(IPAddr('0.0.0.0'), 0, socket.SOCK_DGRAM) - self.assertTrue(creator.all_used()) - self.assertTrue(cm.exception.fatal) + self.fail("Not raised") + except CreatorError as ce: + self.assertTrue(creator.all_used()) + self.assertTrue(ce.fatal) def test_error_send(self): self.__error([('e', None)]) @@ -251,10 +261,12 @@ class ParserTests(unittest.TestCase): Test we can't request sockets after it was terminated. """ parser = self.__terminate() - with self.assertRaises(CreatorError) as cm: + try: parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP') - self.assertTrue(cm.exception.fatal) - self.assertEqual(None, cm.exception.errno) + self.fail("Not raised") + except CreatorError as ce: + self.assertTrue(ce.fatal) + self.assertEqual(None, ce.errno) def test_invalid_socktype(self): """ From 45dcf93cb43fbd2f52cd432e38a5c17ae2ded61f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 27 Jul 2011 11:59:46 +0200 Subject: [PATCH 273/974] [trac1060] Inherit MemoryDataSrc * Inherit the MemoryDataSrc from DataSourceClient * Small comment cleanup * Makefile fix * Remove extra parts of MemoryDataSrc - FindResult is inherited from MemoryDataSrc and it was the same anyway, just different name. - Remove the private assignment operator and copy constructor, the DataSourceClient is already uncopyable. --- src/lib/datasrc/Makefile.am | 1 + src/lib/datasrc/client.h | 11 +++++--- src/lib/datasrc/memory_datasrc.h | 44 ++++++-------------------------- 3 files changed, 17 insertions(+), 39 deletions(-) diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index 457d5b069b..261baaeb0b 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -21,6 +21,7 @@ libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc libdatasrc_la_SOURCES += zone.h libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc +libdatasrc_la_SOURCES += client.h nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index 2e34706b54..d6adef03a5 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -15,6 +15,8 @@ #ifndef __DATA_SOURCE_CLIENT_H #define __DATA_SOURCE_CLIENT_H 1 +#include + namespace isc { namespace datasrc { @@ -28,6 +30,9 @@ namespace datasrc { /// we'll give them more concise names such as InMemoryClient. /// /// This class is not copyable. +/// +/// \todo This class is not complete. It needs more factory methods, for +/// accessing the whole zone, updating it, loading it, etc. class DataSourceClient : boost::noncopyable { public: /// \brief A helper structure to represent the search result of @@ -78,12 +83,12 @@ public: /// form of a \c FindResult object as follows: /// - \c code: The result code of the operation. /// - \c result::SUCCESS: A zone that gives an exact match - // is found + /// is found /// - \c result::PARTIALMATCH: A zone whose origin is a - // super domain of \c name is found (but there is no exact match) + /// super domain of \c name is found (but there is no exact match) /// - \c result::NOTFOUND: For all other cases. /// - \c zone: Pointer to the found \c ZoneFinder object if one - // is found; otherwise \c NULL. + /// is found; otherwise \c NULL. /// /// This method never throws an exception. /// diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 3b7eaad1be..374198103d 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -20,6 +20,7 @@ #include #include +#include namespace isc { namespace dns { @@ -210,44 +211,13 @@ private: /// The findZone() method takes a domain name and returns the best matching \c /// MemoryZone in the form of (Boost) shared pointer, so that it can provide /// the general interface for all data sources. -class MemoryDataSrc { +class MemoryDataSrc : public DataSourceClient { public: - /// \brief A helper structure to represent the search result of - /// MemoryDataSrc::find(). - /// - /// This is a straightforward pair of the result code and a share pointer - /// to the found zone to represent the result of \c find(). - /// We use this in order to avoid overloading the return value for both - /// the result code ("success" or "not found") and the found object, - /// i.e., avoid using \c NULL to mean "not found", etc. - /// - /// This is a simple value class with no internal state, so for - /// convenience we allow the applications to refer to the members - /// directly. - /// - /// See the description of \c find() for the semantics of the member - /// variables. - struct FindResult { - FindResult(result::Result param_code, - const ZoneFinderPtr param_zone_finder) : - code(param_code), zone_finder(param_zone_finder) - {} - const result::Result code; - const ZoneFinderPtr zone_finder; - }; - /// /// \name Constructors and Destructor. /// - /// \b Note: - /// The copy constructor and the assignment operator are intentionally - /// defined as private, making this class non copyable. //@{ -private: - MemoryDataSrc(const MemoryDataSrc& source); - MemoryDataSrc& operator=(const MemoryDataSrc& source); -public: /// Default constructor. /// /// This constructor internally involves resource allocation, and if @@ -288,20 +258,22 @@ public: /// form of a \c FindResult object as follows: /// - \c code: The result code of the operation. /// - \c result::SUCCESS: A zone that gives an exact match - // is found + /// is found /// - \c result::PARTIALMATCH: A zone whose origin is a - // super domain of \c name is found (but there is no exact match) + /// super domain of \c name is found (but there is no exact match) /// - \c result::NOTFOUND: For all other cases. /// - \c zone: A "Boost" shared pointer to the found \c Zone object if one - // is found; otherwise \c NULL. + /// is found; otherwise \c NULL. /// /// This method never throws an exception. /// /// \param name A domain name for which the search is performed. /// \return A \c FindResult object enclosing the search result (see above). - FindResult findZone(const isc::dns::Name& name) const; + virtual FindResult findZone(const isc::dns::Name& name) const; private: + // TODO: Do we still need the PImpl if nobody should manipulate this class + // directly any more (it should be handled trough DataSourceClient)? class MemoryDataSrcImpl; MemoryDataSrcImpl* impl_; }; From 34cfc02f00196f9f5124172b10de5cc8fea1081f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 27 Jul 2011 12:36:08 +0200 Subject: [PATCH 274/974] [trac1060] Rename MemoryDataSrc to InMemoryClient And some more similar renaming. Just to match naming conventions. --- src/bin/auth/auth_config.cc | 12 +-- src/bin/auth/auth_srv.cc | 30 ++++---- src/bin/auth/auth_srv.h | 26 +++---- src/bin/auth/command.cc | 4 +- src/bin/auth/query.cc | 4 +- src/bin/auth/query.h | 14 ++-- src/bin/auth/tests/auth_srv_unittest.cc | 10 +-- src/bin/auth/tests/command_unittest.cc | 21 +++-- src/bin/auth/tests/config_unittest.cc | 44 +++++------ src/bin/auth/tests/query_unittest.cc | 72 +++++++++--------- src/lib/datasrc/memory_datasrc.cc | 22 +++--- src/lib/datasrc/memory_datasrc.h | 18 ++--- .../datasrc/tests/memory_datasrc_unittest.cc | 76 +++++++++---------- 13 files changed, 176 insertions(+), 177 deletions(-) diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc index 11a25fa4df..0198963bf2 100644 --- a/src/bin/auth/auth_config.cc +++ b/src/bin/auth/auth_config.cc @@ -107,7 +107,7 @@ DatasourcesConfig::commit() { // server implementation details, and isn't scalable wrt the number of // data source types, and should eventually be improved. // Currently memory data source for class IN is the only possibility. - server_.setMemoryDataSrc(RRClass::IN(), AuthSrv::MemoryDataSrcPtr()); + server_.setInMemoryClient(RRClass::IN(), AuthSrv::InMemoryClientPtr()); BOOST_FOREACH(shared_ptr datasrc_config, datasources_) { datasrc_config->commit(); @@ -125,12 +125,12 @@ public: {} virtual void build(ConstElementPtr config_value); virtual void commit() { - server_.setMemoryDataSrc(rrclass_, memory_datasrc_); + server_.setInMemoryClient(rrclass_, memory_client_); } private: AuthSrv& server_; RRClass rrclass_; - AuthSrv::MemoryDataSrcPtr memory_datasrc_; + AuthSrv::InMemoryClientPtr memory_client_; }; void @@ -143,8 +143,8 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) { // We'd eventually optimize building zones (in case of reloading) by // selectively loading fresh zones. Right now we simply check the // RR class is supported by the server implementation. - server_.getMemoryDataSrc(rrclass_); - memory_datasrc_ = AuthSrv::MemoryDataSrcPtr(new MemoryDataSrc()); + server_.getInMemoryClient(rrclass_); + memory_client_ = AuthSrv::InMemoryClientPtr(new InMemoryClient()); ConstElementPtr zones_config = config_value->get("zones"); if (!zones_config) { @@ -165,7 +165,7 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) { } shared_ptr new_zone(new MemoryZoneFinder(rrclass_, Name(origin->stringValue()))); - const result::Result result = memory_datasrc_->addZone(new_zone); + const result::Result result = memory_client_->addZone(new_zone); if (result == result::EXIST) { isc_throw(AuthConfigError, "zone "<< origin->str() << " already exists"); diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index f96e642bef..5a3144283a 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -108,8 +108,8 @@ public: AbstractSession* xfrin_session_; /// In-memory data source. Currently class IN only for simplicity. - const RRClass memory_datasrc_class_; - AuthSrv::MemoryDataSrcPtr memory_datasrc_; + const RRClass memory_client_class_; + AuthSrv::InMemoryClientPtr memory_client_; /// Hot spot cache isc::datasrc::HotCache cache_; @@ -145,7 +145,7 @@ AuthSrvImpl::AuthSrvImpl(const bool use_cache, AbstractXfroutClient& xfrout_client) : config_session_(NULL), xfrin_session_(NULL), - memory_datasrc_class_(RRClass::IN()), + memory_client_class_(RRClass::IN()), statistics_timer_(io_service_), counters_(), keyring_(NULL), @@ -329,34 +329,34 @@ AuthSrv::getConfigSession() const { return (impl_->config_session_); } -AuthSrv::MemoryDataSrcPtr -AuthSrv::getMemoryDataSrc(const RRClass& rrclass) { +AuthSrv::InMemoryClientPtr +AuthSrv::getInMemoryClient(const RRClass& rrclass) { // XXX: for simplicity, we only support the IN class right now. - if (rrclass != impl_->memory_datasrc_class_) { + if (rrclass != impl_->memory_client_class_) { isc_throw(InvalidParameter, "Memory data source is not supported for RR class " << rrclass); } - return (impl_->memory_datasrc_); + return (impl_->memory_client_); } void -AuthSrv::setMemoryDataSrc(const isc::dns::RRClass& rrclass, - MemoryDataSrcPtr memory_datasrc) +AuthSrv::setInMemoryClient(const isc::dns::RRClass& rrclass, + InMemoryClientPtr memory_client) { // XXX: see above - if (rrclass != impl_->memory_datasrc_class_) { + if (rrclass != impl_->memory_client_class_) { isc_throw(InvalidParameter, "Memory data source is not supported for RR class " << rrclass); - } else if (!impl_->memory_datasrc_ && memory_datasrc) { + } else if (!impl_->memory_client_ && memory_client) { LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_ENABLED) .arg(rrclass); - } else if (impl_->memory_datasrc_ && !memory_datasrc) { + } else if (impl_->memory_client_ && !memory_client) { LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_DISABLED) .arg(rrclass); } - impl_->memory_datasrc_ = memory_datasrc; + impl_->memory_client_ = memory_client; } uint32_t @@ -505,10 +505,10 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message, // If a memory data source is configured call the separate // Query::process() const ConstQuestionPtr question = *message->beginQuestion(); - if (memory_datasrc_ && memory_datasrc_class_ == question->getClass()) { + if (memory_client_ && memory_client_class_ == question->getClass()) { const RRType& qtype = question->getType(); const Name& qname = question->getName(); - auth::Query(*memory_datasrc_, qname, qtype, *message).process(); + auth::Query(*memory_client_, qname, qtype, *message).process(); } else { datasrc::Query query(*message, cache_, dnssec_ok); data_sources_.doQuery(query); diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h index 7eede97cd1..f2259a2994 100644 --- a/src/bin/auth/auth_srv.h +++ b/src/bin/auth/auth_srv.h @@ -17,7 +17,7 @@ #include -// For MemoryDataSrcPtr below. This should be a temporary definition until +// For InMemoryClientPtr below. This should be a temporary definition until // we reorganize the data source framework. #include @@ -39,7 +39,7 @@ namespace isc { namespace datasrc { -class MemoryDataSrc; +class InMemoryClient; } namespace xfr { class AbstractXfroutClient; @@ -133,7 +133,7 @@ public: /// If there is a data source installed, it will be replaced with the /// new one. /// - /// In the current implementation, the SQLite data source and MemoryDataSrc + /// In the current implementation, the SQLite data source and InMemoryClient /// are assumed. /// We can enable memory data source and get the path of SQLite database by /// the \c config parameter. If we disabled memory data source, the SQLite @@ -233,16 +233,16 @@ public: /// void setXfrinSession(isc::cc::AbstractSession* xfrin_session); - /// A shared pointer type for \c MemoryDataSrc. + /// A shared pointer type for \c InMemoryClient. /// /// This is defined inside the \c AuthSrv class as it's supposed to be /// a short term interface until we integrate the in-memory and other /// data source frameworks. - typedef boost::shared_ptr MemoryDataSrcPtr; + typedef boost::shared_ptr InMemoryClientPtr; - /// An immutable shared pointer type for \c MemoryDataSrc. - typedef boost::shared_ptr - ConstMemoryDataSrcPtr; + /// An immutable shared pointer type for \c InMemoryClient. + typedef boost::shared_ptr + ConstInMemoryClientPtr; /// Returns the in-memory data source configured for the \c AuthSrv, /// if any. @@ -260,11 +260,11 @@ public: /// \param rrclass The RR class of the requested in-memory data source. /// \return A pointer to the in-memory data source, if configured; /// otherwise NULL. - MemoryDataSrcPtr getMemoryDataSrc(const isc::dns::RRClass& rrclass); + InMemoryClientPtr getInMemoryClient(const isc::dns::RRClass& rrclass); /// Sets or replaces the in-memory data source of the specified RR class. /// - /// As noted in \c getMemoryDataSrc(), some RR classes may not be + /// As noted in \c getInMemoryClient(), some RR classes may not be /// supported, in which case an exception of class \c InvalidParameter /// will be thrown. /// This method never throws an exception otherwise. @@ -275,9 +275,9 @@ public: /// in-memory data source. /// /// \param rrclass The RR class of the in-memory data source to be set. - /// \param memory_datasrc A (shared) pointer to \c MemoryDataSrc to be set. - void setMemoryDataSrc(const isc::dns::RRClass& rrclass, - MemoryDataSrcPtr memory_datasrc); + /// \param memory_datasrc A (shared) pointer to \c InMemoryClient to be set. + void setInMemoryClient(const isc::dns::RRClass& rrclass, + InMemoryClientPtr memory_client); /// \brief Set the communication session with Statistics. /// diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc index d6579aa8ed..87bae406ae 100644 --- a/src/bin/auth/command.cc +++ b/src/bin/auth/command.cc @@ -177,7 +177,7 @@ private: const RRClass zone_class = class_elem ? RRClass(class_elem->stringValue()) : RRClass::IN(); - AuthSrv::MemoryDataSrcPtr datasrc(server.getMemoryDataSrc(zone_class)); + AuthSrv::InMemoryClientPtr datasrc(server.getInMemoryClient(zone_class)); if (datasrc == NULL) { isc_throw(AuthCommandError, "Memory data source is disabled"); } @@ -189,7 +189,7 @@ private: const Name origin(origin_elem->stringValue()); // Get the current zone - const MemoryDataSrc::FindResult result = datasrc->findZone(origin); + const InMemoryClient::FindResult result = datasrc->findZone(origin); if (result.code != result::SUCCESS) { isc_throw(AuthCommandError, "Zone " << origin << " is not found in data source"); diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc index fd18b88cbc..8be006fb79 100644 --- a/src/bin/auth/query.cc +++ b/src/bin/auth/query.cc @@ -126,8 +126,8 @@ Query::process() const { const bool qtype_is_any = (qtype_ == RRType::ANY()); response_.setHeaderFlag(Message::HEADERFLAG_AA, false); - const MemoryDataSrc::FindResult result = - memory_datasrc_.findZone(qname_); + const InMemoryClient::FindResult result = + memory_client_.findZone(qname_); // If we have no matching authoritative zone for the query name, return // REFUSED. In short, this is to be compatible with BIND 9, but the diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h index 921265fd8c..d5b7e4ebe5 100644 --- a/src/bin/auth/query.h +++ b/src/bin/auth/query.h @@ -26,7 +26,7 @@ class RRset; } namespace datasrc { -class MemoryDataSrc; +class InMemoryClient; } namespace auth { @@ -36,7 +36,7 @@ namespace auth { /// /// Many of the design details for this class are still in flux. /// We'll revisit and update them as we add more functionality, for example: -/// - memory_datasrc parameter of the constructor. It is a data source that +/// - memory_client parameter of the constructor. It is a data source that /// uses in memory dedicated backend. /// - as a related point, we may have to pass the RR class of the query. /// in the initial implementation the RR class is an attribute of memory @@ -51,7 +51,7 @@ namespace auth { /// separate attribute setter. /// - likewise, we'll eventually need to do per zone access control, for which /// we need querier's information such as its IP address. -/// - memory_datasrc and response may better be parameters to process() instead +/// - memory_client and response may better be parameters to process() instead /// of the constructor. /// /// Note: The class name is intentionally the same as the one used in @@ -135,15 +135,15 @@ public: /// /// This constructor never throws an exception. /// - /// \param memory_datasrc The memory datasource wherein the answer to the query is + /// \param memory_client The memory datasource wherein the answer to the query is /// to be found. /// \param qname The query name /// \param qtype The RR type of the query /// \param response The response message to store the answer to the query. - Query(const isc::datasrc::MemoryDataSrc& memory_datasrc, + Query(const isc::datasrc::InMemoryClient& memory_client, const isc::dns::Name& qname, const isc::dns::RRType& qtype, isc::dns::Message& response) : - memory_datasrc_(memory_datasrc), qname_(qname), qtype_(qtype), + memory_client_(memory_client), qname_(qname), qtype_(qtype), response_(response) {} @@ -208,7 +208,7 @@ public: }; private: - const isc::datasrc::MemoryDataSrc& memory_datasrc_; + const isc::datasrc::InMemoryClient& memory_client_; const isc::dns::Name& qname_; const isc::dns::RRType& qtype_; isc::dns::Message& response_; diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index 2b20d65a4d..469858857d 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -651,17 +651,17 @@ TEST_F(AuthSrvTest, updateConfigFail) { QR_FLAG | AA_FLAG, 1, 1, 1, 0); } -TEST_F(AuthSrvTest, updateWithMemoryDataSrc) { +TEST_F(AuthSrvTest, updateWithInMemoryClient) { // Test configuring memory data source. Detailed test cases are covered // in the configuration tests. We only check the AuthSrv interface here. // By default memory data source isn't enabled - EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); + EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); updateConfig(&server, "{\"datasources\": [{\"type\": \"memory\"}]}", true); // after successful configuration, we should have one (with empty zoneset). - ASSERT_NE(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); - EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount()); + ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); + EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount()); // The memory data source is empty, should return REFUSED rcode. createDataFromFile("examplequery_fromWire.wire"); @@ -672,7 +672,7 @@ TEST_F(AuthSrvTest, updateWithMemoryDataSrc) { opcode.getCode(), QR_FLAG, 1, 0, 0, 0); } -TEST_F(AuthSrvTest, chQueryWithMemoryDataSrc) { +TEST_F(AuthSrvTest, chQueryWithInMemoryClient) { // Configure memory data source for class IN updateConfig(&server, "{\"datasources\": " "[{\"class\": \"IN\", \"type\": \"memory\"}]}", true); diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index b7e6752c30..8a82367aea 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -60,7 +60,6 @@ protected: MockSession statistics_session; MockXfroutClient xfrout; AuthSrv server; - AuthSrv::ConstMemoryDataSrcPtr memory_datasrc; ConstElementPtr result; int rcode; public: @@ -110,17 +109,17 @@ TEST_F(AuthCommandTest, shutdown) { // zones, and checks the zones are correctly loaded. void zoneChecks(AuthSrv& server) { - EXPECT_TRUE(server.getMemoryDataSrc(RRClass::IN())); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + EXPECT_TRUE(server.getInMemoryClient(RRClass::IN())); + EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())-> findZone(Name("ns.test1.example")).zone_finder-> find(Name("ns.test1.example"), RRType::A()).code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())-> findZone(Name("ns.test1.example")).zone_finder-> find(Name("ns.test1.example"), RRType::AAAA()).code); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())-> findZone(Name("ns.test2.example")).zone_finder-> find(Name("ns.test2.example"), RRType::A()).code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())-> findZone(Name("ns.test2.example")).zone_finder-> find(Name("ns.test2.example"), RRType::AAAA()).code); } @@ -147,20 +146,20 @@ configureZones(AuthSrv& server) { void newZoneChecks(AuthSrv& server) { - EXPECT_TRUE(server.getMemoryDataSrc(RRClass::IN())); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + EXPECT_TRUE(server.getInMemoryClient(RRClass::IN())); + EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())-> findZone(Name("ns.test1.example")).zone_finder-> find(Name("ns.test1.example"), RRType::A()).code); // now test1.example should have ns/AAAA - EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())-> findZone(Name("ns.test1.example")).zone_finder-> find(Name("ns.test1.example"), RRType::AAAA()).code); // test2.example shouldn't change - EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())-> findZone(Name("ns.test2.example")).zone_finder-> find(Name("ns.test2.example"), RRType::A()).code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getMemoryDataSrc(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())-> findZone(Name("ns.test2.example")).zone_finder-> find(Name("ns.test2.example"), RRType::AAAA()).code); } diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc index 28275085d0..dadb0ee390 100644 --- a/src/bin/auth/tests/config_unittest.cc +++ b/src/bin/auth/tests/config_unittest.cc @@ -57,12 +57,12 @@ protected: TEST_F(AuthConfigTest, datasourceConfig) { // By default, we don't have any in-memory data source. - EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); + EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); configureAuthServer(server, Element::fromJSON( "{\"datasources\": [{\"type\": \"memory\"}]}")); // after successful configuration, we should have one (with empty zoneset). - ASSERT_NE(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); - EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount()); + ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); + EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount()); } TEST_F(AuthConfigTest, databaseConfig) { @@ -82,7 +82,7 @@ TEST_F(AuthConfigTest, versionConfig) { } TEST_F(AuthConfigTest, exceptionGuarantee) { - EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); + EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); // This configuration contains an invalid item, which will trigger // an exception. EXPECT_THROW(configureAuthServer( @@ -92,7 +92,7 @@ TEST_F(AuthConfigTest, exceptionGuarantee) { " \"no_such_config_var\": 1}")), AuthConfigError); // The server state shouldn't change - EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); + EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); } TEST_F(AuthConfigTest, exceptionConversion) { @@ -154,22 +154,22 @@ protected: TEST_F(MemoryDatasrcConfigTest, addZeroDataSrc) { parser->build(Element::fromJSON("[]")); parser->commit(); - EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); + EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); } TEST_F(MemoryDatasrcConfigTest, addEmpty) { // By default, we don't have any in-memory data source. - EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); + EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); parser->build(Element::fromJSON("[{\"type\": \"memory\"}]")); parser->commit(); - EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount()); } TEST_F(MemoryDatasrcConfigTest, addZeroZone) { parser->build(Element::fromJSON("[{\"type\": \"memory\"," " \"zones\": []}]")); parser->commit(); - EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount()); } TEST_F(MemoryDatasrcConfigTest, addOneZone) { @@ -179,9 +179,9 @@ TEST_F(MemoryDatasrcConfigTest, addOneZone) { " \"file\": \"" TEST_DATA_DIR "/example.zone\"}]}]"))); EXPECT_NO_THROW(parser->commit()); - EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount()); // Check it actually loaded something - EXPECT_EQ(ZoneFinder::SUCCESS, server.getMemoryDataSrc(rrclass)->findZone( + EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(rrclass)->findZone( Name("ns.example.com.")).zone_finder->find(Name("ns.example.com."), RRType::A()).code); } @@ -199,7 +199,7 @@ TEST_F(MemoryDatasrcConfigTest, addMultiZones) { " \"file\": \"" TEST_DATA_DIR "/example.net.zone\"}]}]"))); EXPECT_NO_THROW(parser->commit()); - EXPECT_EQ(3, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(3, server.getInMemoryClient(rrclass)->getZoneCount()); } TEST_F(MemoryDatasrcConfigTest, replace) { @@ -209,9 +209,9 @@ TEST_F(MemoryDatasrcConfigTest, replace) { " \"file\": \"" TEST_DATA_DIR "/example.zone\"}]}]"))); EXPECT_NO_THROW(parser->commit()); - EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount()); EXPECT_EQ(isc::datasrc::result::SUCCESS, - server.getMemoryDataSrc(rrclass)->findZone( + server.getInMemoryClient(rrclass)->findZone( Name("example.com")).code); // create a new parser, and install a new set of configuration. It @@ -227,9 +227,9 @@ TEST_F(MemoryDatasrcConfigTest, replace) { " \"file\": \"" TEST_DATA_DIR "/example.net.zone\"}]}]"))); EXPECT_NO_THROW(parser->commit()); - EXPECT_EQ(2, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(2, server.getInMemoryClient(rrclass)->getZoneCount()); EXPECT_EQ(isc::datasrc::result::NOTFOUND, - server.getMemoryDataSrc(rrclass)->findZone( + server.getInMemoryClient(rrclass)->findZone( Name("example.com")).code); } @@ -241,9 +241,9 @@ TEST_F(MemoryDatasrcConfigTest, exception) { " \"file\": \"" TEST_DATA_DIR "/example.zone\"}]}]"))); EXPECT_NO_THROW(parser->commit()); - EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount()); EXPECT_EQ(isc::datasrc::result::SUCCESS, - server.getMemoryDataSrc(rrclass)->findZone( + server.getInMemoryClient(rrclass)->findZone( Name("example.com")).code); // create a new parser, and try to load something. It will throw, @@ -262,9 +262,9 @@ TEST_F(MemoryDatasrcConfigTest, exception) { // commit it // The original should be untouched - EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount()); EXPECT_EQ(isc::datasrc::result::SUCCESS, - server.getMemoryDataSrc(rrclass)->findZone( + server.getInMemoryClient(rrclass)->findZone( Name("example.com")).code); } @@ -275,13 +275,13 @@ TEST_F(MemoryDatasrcConfigTest, remove) { " \"file\": \"" TEST_DATA_DIR "/example.zone\"}]}]"))); EXPECT_NO_THROW(parser->commit()); - EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount()); + EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount()); delete parser; parser = createAuthConfigParser(server, "datasources"); EXPECT_NO_THROW(parser->build(Element::fromJSON("[]"))); EXPECT_NO_THROW(parser->commit()); - EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass)); + EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass)); } TEST_F(MemoryDatasrcConfigTest, adDuplicateZones) { diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index bd167374e3..49ce0d6849 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -234,10 +234,10 @@ protected: response.setOpcode(Opcode::QUERY()); // create and add a matching zone. mock_finder = new MockZoneFinder(); - memory_datasrc.addZone(ZoneFinderPtr(mock_finder)); + memory_client.addZone(ZoneFinderPtr(mock_finder)); } MockZoneFinder* mock_finder; - MemoryDataSrc memory_datasrc; + InMemoryClient memory_client; const Name qname; const RRClass qclass; const RRType qtype; @@ -286,14 +286,14 @@ responseCheck(Message& response, const isc::dns::Rcode& rcode, TEST_F(QueryTest, noZone) { // There's no zone in the memory datasource. So the response should have // REFUSED. - MemoryDataSrc empty_memory_datasrc; - Query nozone_query(empty_memory_datasrc, qname, qtype, response); + InMemoryClient empty_memory_client; + Query nozone_query(empty_memory_client, qname, qtype, response); EXPECT_NO_THROW(nozone_query.process()); EXPECT_EQ(Rcode::REFUSED(), response.getRcode()); } TEST_F(QueryTest, exactMatch) { - Query query(memory_datasrc, qname, qtype, response); + Query query(memory_client, qname, qtype, response); EXPECT_NO_THROW(query.process()); // find match rrset responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, @@ -303,7 +303,7 @@ TEST_F(QueryTest, exactMatch) { TEST_F(QueryTest, exactAddrMatch) { // find match rrset, omit additional data which has already been provided // in the answer section from the additional. - EXPECT_NO_THROW(Query(memory_datasrc, Name("noglue.example.com"), qtype, + EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"), qtype, response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2, @@ -315,7 +315,7 @@ TEST_F(QueryTest, exactAddrMatch) { TEST_F(QueryTest, apexNSMatch) { // find match rrset, omit authority data which has already been provided // in the answer section from the authority section. - EXPECT_NO_THROW(Query(memory_datasrc, Name("example.com"), RRType::NS(), + EXPECT_NO_THROW(Query(memory_client, Name("example.com"), RRType::NS(), response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3, @@ -326,7 +326,7 @@ TEST_F(QueryTest, apexNSMatch) { TEST_F(QueryTest, exactAnyMatch) { // find match rrset, omit additional data which has already been provided // in the answer section from the additional. - EXPECT_NO_THROW(Query(memory_datasrc, Name("noglue.example.com"), + EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"), RRType::ANY(), response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2, @@ -339,7 +339,7 @@ TEST_F(QueryTest, exactAnyMatch) { TEST_F(QueryTest, apexAnyMatch) { // find match rrset, omit additional data which has already been provided // in the answer section from the additional. - EXPECT_NO_THROW(Query(memory_datasrc, Name("example.com"), + EXPECT_NO_THROW(Query(memory_client, Name("example.com"), RRType::ANY(), response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 0, 3, "example.com. 3600 IN SOA . . 0 0 0 0 0\n" @@ -350,7 +350,7 @@ TEST_F(QueryTest, apexAnyMatch) { } TEST_F(QueryTest, mxANYMatch) { - EXPECT_NO_THROW(Query(memory_datasrc, Name("mx.example.com"), + EXPECT_NO_THROW(Query(memory_client, Name("mx.example.com"), RRType::ANY(), response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4, mx_txt, zone_ns_txt, @@ -358,14 +358,14 @@ TEST_F(QueryTest, mxANYMatch) { } TEST_F(QueryTest, glueANYMatch) { - EXPECT_NO_THROW(Query(memory_datasrc, Name("delegation.example.com"), + EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"), RRType::ANY(), response).process()); responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3, NULL, delegation_txt, ns_addrs_txt); } TEST_F(QueryTest, nodomainANY) { - EXPECT_NO_THROW(Query(memory_datasrc, Name("nxdomain.example.com"), + EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"), RRType::ANY(), response).process()); responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0, NULL, soa_txt, NULL, mock_finder->getOrigin()); @@ -378,13 +378,13 @@ TEST_F(QueryTest, noApexNS) { // Disable apex NS record mock_finder->setApexNSFlag(false); - EXPECT_THROW(Query(memory_datasrc, Name("noglue.example.com"), qtype, + EXPECT_THROW(Query(memory_client, Name("noglue.example.com"), qtype, response).process(), Query::NoApexNS); // We don't look into the response, as it threw } TEST_F(QueryTest, delegation) { - EXPECT_NO_THROW(Query(memory_datasrc, Name("delegation.example.com"), + EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"), qtype, response).process()); responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3, @@ -392,14 +392,14 @@ TEST_F(QueryTest, delegation) { } TEST_F(QueryTest, nxdomain) { - EXPECT_NO_THROW(Query(memory_datasrc, Name("nxdomain.example.com"), qtype, + EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype, response).process()); responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0, NULL, soa_txt, NULL, mock_finder->getOrigin()); } TEST_F(QueryTest, nxrrset) { - EXPECT_NO_THROW(Query(memory_datasrc, Name("www.example.com"), + EXPECT_NO_THROW(Query(memory_client, Name("www.example.com"), RRType::TXT(), response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0, @@ -415,19 +415,19 @@ TEST_F(QueryTest, noSOA) { mock_finder->setSOAFlag(false); // The NX Domain - EXPECT_THROW(Query(memory_datasrc, Name("nxdomain.example.com"), + EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype, response).process(), Query::NoSOA); // Of course, we don't look into the response, as it throwed // NXRRSET - EXPECT_THROW(Query(memory_datasrc, Name("nxrrset.example.com"), + EXPECT_THROW(Query(memory_client, Name("nxrrset.example.com"), qtype, response).process(), Query::NoSOA); } TEST_F(QueryTest, noMatchZone) { // there's a zone in the memory datasource but it doesn't match the qname. // should result in REFUSED. - Query(memory_datasrc, Name("example.org"), qtype, response).process(); + Query(memory_client, Name("example.org"), qtype, response).process(); EXPECT_EQ(Rcode::REFUSED(), response.getRcode()); } @@ -438,7 +438,7 @@ TEST_F(QueryTest, noMatchZone) { * A record, other to unknown out of zone one. */ TEST_F(QueryTest, MX) { - Query(memory_datasrc, Name("mx.example.com"), RRType::MX(), + Query(memory_client, Name("mx.example.com"), RRType::MX(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4, @@ -452,7 +452,7 @@ TEST_F(QueryTest, MX) { * This should not trigger the additional processing for the exchange. */ TEST_F(QueryTest, MXAlias) { - Query(memory_datasrc, Name("cnamemx.example.com"), RRType::MX(), + Query(memory_client, Name("cnamemx.example.com"), RRType::MX(), response).process(); // there shouldn't be no additional RRs for the exchanges (we have 3 @@ -472,7 +472,7 @@ TEST_F(QueryTest, MXAlias) { * returned. */ TEST_F(QueryTest, CNAME) { - Query(memory_datasrc, Name("cname.example.com"), RRType::A(), + Query(memory_client, Name("cname.example.com"), RRType::A(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0, @@ -482,7 +482,7 @@ TEST_F(QueryTest, CNAME) { TEST_F(QueryTest, explicitCNAME) { // same owner name as the CNAME test but explicitly query for CNAME RR. // expect the same response as we don't provide a full chain yet. - Query(memory_datasrc, Name("cname.example.com"), RRType::CNAME(), + Query(memory_client, Name("cname.example.com"), RRType::CNAME(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, @@ -494,7 +494,7 @@ TEST_F(QueryTest, CNAME_NX_RRSET) { // note: with chaining, what should be expected is not trivial: // BIND 9 returns the CNAME in answer and SOA in authority, no additional. // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional. - Query(memory_datasrc, Name("cname.example.com"), RRType::TXT(), + Query(memory_client, Name("cname.example.com"), RRType::TXT(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0, @@ -503,7 +503,7 @@ TEST_F(QueryTest, CNAME_NX_RRSET) { TEST_F(QueryTest, explicitCNAME_NX_RRSET) { // same owner name as the NXRRSET test but explicitly query for CNAME RR. - Query(memory_datasrc, Name("cname.example.com"), RRType::CNAME(), + Query(memory_client, Name("cname.example.com"), RRType::CNAME(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, @@ -517,7 +517,7 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) { // RCODE being NXDOMAIN. // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional, // RCODE being NOERROR. - Query(memory_datasrc, Name("cnamenxdom.example.com"), RRType::A(), + Query(memory_client, Name("cnamenxdom.example.com"), RRType::A(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0, @@ -526,7 +526,7 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) { TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) { // same owner name as the NXDOMAIN test but explicitly query for CNAME RR. - Query(memory_datasrc, Name("cnamenxdom.example.com"), RRType::CNAME(), + Query(memory_client, Name("cnamenxdom.example.com"), RRType::CNAME(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, @@ -542,7 +542,7 @@ TEST_F(QueryTest, CNAME_OUT) { * Then the same test should be done with .org included there and * see what it does (depends on what we want to do) */ - Query(memory_datasrc, Name("cnameout.example.com"), RRType::A(), + Query(memory_client, Name("cnameout.example.com"), RRType::A(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0, @@ -551,7 +551,7 @@ TEST_F(QueryTest, CNAME_OUT) { TEST_F(QueryTest, explicitCNAME_OUT) { // same owner name as the OUT test but explicitly query for CNAME RR. - Query(memory_datasrc, Name("cnameout.example.com"), RRType::CNAME(), + Query(memory_client, Name("cnameout.example.com"), RRType::CNAME(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, @@ -567,7 +567,7 @@ TEST_F(QueryTest, explicitCNAME_OUT) { * pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME). */ TEST_F(QueryTest, DNAME) { - Query(memory_datasrc, Name("www.dname.example.com"), RRType::A(), + Query(memory_client, Name("www.dname.example.com"), RRType::A(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0, @@ -583,7 +583,7 @@ TEST_F(QueryTest, DNAME) { * DNAME. */ TEST_F(QueryTest, DNAME_ANY) { - Query(memory_datasrc, Name("www.dname.example.com"), RRType::ANY(), + Query(memory_client, Name("www.dname.example.com"), RRType::ANY(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0, @@ -592,7 +592,7 @@ TEST_F(QueryTest, DNAME_ANY) { // Test when we ask for DNAME explicitly, it does no synthetizing. TEST_F(QueryTest, explicitDNAME) { - Query(memory_datasrc, Name("dname.example.com"), RRType::DNAME(), + Query(memory_client, Name("dname.example.com"), RRType::DNAME(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, @@ -604,7 +604,7 @@ TEST_F(QueryTest, explicitDNAME) { * the CNAME, it should return the RRset. */ TEST_F(QueryTest, DNAME_A) { - Query(memory_datasrc, Name("dname.example.com"), RRType::A(), + Query(memory_client, Name("dname.example.com"), RRType::A(), response).process(); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, @@ -616,7 +616,7 @@ TEST_F(QueryTest, DNAME_A) { * It should not synthetize the CNAME. */ TEST_F(QueryTest, DNAME_NX_RRSET) { - EXPECT_NO_THROW(Query(memory_datasrc, Name("dname.example.com"), + EXPECT_NO_THROW(Query(memory_client, Name("dname.example.com"), RRType::TXT(), response).process()); responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0, @@ -636,7 +636,7 @@ TEST_F(QueryTest, LongDNAME) { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." "dname.example.com."); - EXPECT_NO_THROW(Query(memory_datasrc, longname, RRType::A(), + EXPECT_NO_THROW(Query(memory_client, longname, RRType::A(), response).process()); responseCheck(response, Rcode::YXDOMAIN(), AA_FLAG, 1, 0, 0, @@ -655,7 +655,7 @@ TEST_F(QueryTest, MaxLenDNAME) { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." "dname.example.com."); - EXPECT_NO_THROW(Query(memory_datasrc, longname, RRType::A(), + EXPECT_NO_THROW(Query(memory_client, longname, RRType::A(), response).process()); // Check the answer is OK diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 40bdb545e2..a0edd4d56b 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -655,36 +655,36 @@ MemoryZoneFinder::getFileName() const { return (impl_->file_name_); } -/// Implementation details for \c MemoryDataSrc hidden from the public +/// Implementation details for \c InMemoryClient hidden from the public /// interface. /// -/// For now, \c MemoryDataSrc only contains a \c ZoneTable object, which +/// For now, \c InMemoryClient only contains a \c ZoneTable object, which /// consists of (pointers to) \c MemoryZoneFinder objects, we may add more /// member variables later for new features. -class MemoryDataSrc::MemoryDataSrcImpl { +class InMemoryClient::InMemoryClientImpl { public: - MemoryDataSrcImpl() : zone_count(0) {} + InMemoryClientImpl() : zone_count(0) {} unsigned int zone_count; ZoneTable zone_table; }; -MemoryDataSrc::MemoryDataSrc() : impl_(new MemoryDataSrcImpl) +InMemoryClient::InMemoryClient() : impl_(new InMemoryClientImpl) {} -MemoryDataSrc::~MemoryDataSrc() { +InMemoryClient::~InMemoryClient() { delete impl_; } unsigned int -MemoryDataSrc::getZoneCount() const { +InMemoryClient::getZoneCount() const { return (impl_->zone_count); } result::Result -MemoryDataSrc::addZone(ZoneFinderPtr zone) { +InMemoryClient::addZone(ZoneFinderPtr zone) { if (!zone) { isc_throw(InvalidParameter, - "Null pointer is passed to MemoryDataSrc::addZone()"); + "Null pointer is passed to InMemoryClient::addZone()"); } LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE). @@ -697,8 +697,8 @@ MemoryDataSrc::addZone(ZoneFinderPtr zone) { return (result); } -MemoryDataSrc::FindResult -MemoryDataSrc::findZone(const isc::dns::Name& name) const { +InMemoryClient::FindResult +InMemoryClient::findZone(const isc::dns::Name& name) const { LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_ZONE).arg(name); return (FindResult(impl_->zone_table.findZone(name).code, impl_->zone_table.findZone(name).zone)); diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 374198103d..9476e097ea 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -185,13 +185,13 @@ private: /// \brief A data source that uses in memory dedicated backend. /// -/// The \c MemoryDataSrc class represents a data source and provides a +/// The \c InMemoryClient class represents a data source and provides a /// basic interface to help DNS lookup processing. For a given domain /// name, its \c findZone() method searches the in memory dedicated backend /// for the zone that gives a longest match against that name. /// /// The in memory dedicated backend are assumed to be of the same RR class, -/// but the \c MemoryDataSrc class does not enforce the assumption through +/// but the \c InMemoryClient class does not enforce the assumption through /// its interface. /// For example, the \c addZone() method does not check if the new zone is of /// the same RR class as that of the others already in the dedicated backend. @@ -211,7 +211,7 @@ private: /// The findZone() method takes a domain name and returns the best matching \c /// MemoryZone in the form of (Boost) shared pointer, so that it can provide /// the general interface for all data sources. -class MemoryDataSrc : public DataSourceClient { +class InMemoryClient : public DataSourceClient { public: /// /// \name Constructors and Destructor. @@ -223,10 +223,10 @@ public: /// This constructor internally involves resource allocation, and if /// it fails, a corresponding standard exception will be thrown. /// It never throws an exception otherwise. - MemoryDataSrc(); + InMemoryClient(); /// The destructor. - ~MemoryDataSrc(); + ~InMemoryClient(); //@} /// Return the number of zones stored in the data source. @@ -236,7 +236,7 @@ public: /// \return The number of zones stored in the data source. unsigned int getZoneCount() const; - /// Add a \c Zone to the \c MemoryDataSrc. + /// Add a \c Zone to the \c InMemoryClient. /// /// \c Zone must not be associated with a NULL pointer; otherwise /// an exception of class \c InvalidParameter will be thrown. @@ -251,7 +251,7 @@ public: /// stores a zone that has the same origin. result::Result addZone(ZoneFinderPtr zone); - /// Find a \c Zone that best matches the given name in the \c MemoryDataSrc. + /// Find a \c Zone that best matches the given name in the \c InMemoryClient. /// /// It searches the internal storage for a \c Zone that gives the /// longest match against \c name, and returns the result in the @@ -274,8 +274,8 @@ public: private: // TODO: Do we still need the PImpl if nobody should manipulate this class // directly any more (it should be handled trough DataSourceClient)? - class MemoryDataSrcImpl; - MemoryDataSrcImpl* impl_; + class InMemoryClientImpl; + InMemoryClientImpl* impl_; }; } } diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index ab9c799e32..8d960b0674 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -42,116 +42,116 @@ namespace { using result::SUCCESS; using result::EXIST; -class MemoryDataSrcTest : public ::testing::Test { +class InMemoryClientTest : public ::testing::Test { protected: - MemoryDataSrcTest() : rrclass(RRClass::IN()) + InMemoryClientTest() : rrclass(RRClass::IN()) {} RRClass rrclass; - MemoryDataSrc memory_datasrc; + InMemoryClient memory_client; }; -TEST_F(MemoryDataSrcTest, add_find_Zone) { +TEST_F(InMemoryClientTest, add_find_Zone) { // test add zone // Bogus zone (NULL) - EXPECT_THROW(memory_datasrc.addZone(ZoneFinderPtr()), + EXPECT_THROW(memory_client.addZone(ZoneFinderPtr()), isc::InvalidParameter); // add zones with different names one by one - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("a"))))); - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("b"))))); - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("c"))))); // add zones with the same name suffix - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("x.d.e.f"))))); - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("o.w.y.d.e.f"))))); - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("p.w.y.d.e.f"))))); - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("q.w.y.d.e.f"))))); // add super zone and its subzone - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("g.h"))))); - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("i.g.h"))))); - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("z.d.e.f"))))); - EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( + EXPECT_EQ(result::SUCCESS, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("j.z.d.e.f"))))); // different zone class isn't allowed. - EXPECT_EQ(result::EXIST, memory_datasrc.addZone( + EXPECT_EQ(result::EXIST, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("q.w.y.d.e.f"))))); // names are compared in a case insensitive manner. - EXPECT_EQ(result::EXIST, memory_datasrc.addZone( + EXPECT_EQ(result::EXIST, memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("Q.W.Y.d.E.f"))))); // test find zone - EXPECT_EQ(result::SUCCESS, memory_datasrc.findZone(Name("a")).code); + EXPECT_EQ(result::SUCCESS, memory_client.findZone(Name("a")).code); EXPECT_EQ(Name("a"), - memory_datasrc.findZone(Name("a")).zone_finder->getOrigin()); + memory_client.findZone(Name("a")).zone_finder->getOrigin()); EXPECT_EQ(result::SUCCESS, - memory_datasrc.findZone(Name("j.z.d.e.f")).code); + memory_client.findZone(Name("j.z.d.e.f")).code); EXPECT_EQ(Name("j.z.d.e.f"), - memory_datasrc.findZone(Name("j.z.d.e.f")).zone_finder->getOrigin()); + memory_client.findZone(Name("j.z.d.e.f")).zone_finder->getOrigin()); // NOTFOUND - EXPECT_EQ(result::NOTFOUND, memory_datasrc.findZone(Name("d.e.f")).code); + EXPECT_EQ(result::NOTFOUND, memory_client.findZone(Name("d.e.f")).code); EXPECT_EQ(ConstZoneFinderPtr(), - memory_datasrc.findZone(Name("d.e.f")).zone_finder); + memory_client.findZone(Name("d.e.f")).zone_finder); EXPECT_EQ(result::NOTFOUND, - memory_datasrc.findZone(Name("w.y.d.e.f")).code); + memory_client.findZone(Name("w.y.d.e.f")).code); EXPECT_EQ(ConstZoneFinderPtr(), - memory_datasrc.findZone(Name("w.y.d.e.f")).zone_finder); + memory_client.findZone(Name("w.y.d.e.f")).zone_finder); // there's no exact match. the result should be the longest match, // and the code should be PARTIALMATCH. EXPECT_EQ(result::PARTIALMATCH, - memory_datasrc.findZone(Name("j.g.h")).code); + memory_client.findZone(Name("j.g.h")).code); EXPECT_EQ(Name("g.h"), - memory_datasrc.findZone(Name("g.h")).zone_finder->getOrigin()); + memory_client.findZone(Name("g.h")).zone_finder->getOrigin()); EXPECT_EQ(result::PARTIALMATCH, - memory_datasrc.findZone(Name("z.i.g.h")).code); + memory_client.findZone(Name("z.i.g.h")).code); EXPECT_EQ(Name("i.g.h"), - memory_datasrc.findZone(Name("z.i.g.h")).zone_finder->getOrigin()); + memory_client.findZone(Name("z.i.g.h")).zone_finder->getOrigin()); } -TEST_F(MemoryDataSrcTest, getZoneCount) { - EXPECT_EQ(0, memory_datasrc.getZoneCount()); - memory_datasrc.addZone( +TEST_F(InMemoryClientTest, getZoneCount) { + EXPECT_EQ(0, memory_client.getZoneCount()); + memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(rrclass, Name("example.com")))); - EXPECT_EQ(1, memory_datasrc.getZoneCount()); + EXPECT_EQ(1, memory_client.getZoneCount()); // duplicate add. counter shouldn't change - memory_datasrc.addZone( + memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(rrclass, Name("example.com")))); - EXPECT_EQ(1, memory_datasrc.getZoneCount()); + EXPECT_EQ(1, memory_client.getZoneCount()); // add one more - memory_datasrc.addZone( + memory_client.addZone( ZoneFinderPtr(new MemoryZoneFinder(rrclass, Name("example.org")))); - EXPECT_EQ(2, memory_datasrc.getZoneCount()); + EXPECT_EQ(2, memory_client.getZoneCount()); } // A helper callback of masterLoad() used in MemoryZoneFinderTest. From ced9ddecf6b8f7777125b8d4d2ef1b24ccad34cd Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 27 Jul 2011 12:44:33 +0200 Subject: [PATCH 275/974] [trac926] review comments --- src/bin/bindctl/bindcmd.py | 4 ++-- src/lib/config/module_spec.cc | 3 ++- src/lib/python/isc/config/config_data.py | 7 ++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index b6b7238476..3ddc9adc96 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -398,8 +398,8 @@ class BindCmdInterpreter(Cmd): print("Error: " + str(dte)) except isc.cc.data.DataNotFoundError as dnfe: print("Error: " + str(dnfe)) - except isc.cc.data.DataAlreadyPresentError as dnfe: - print("Error: " + str(dnfe)) + except isc.cc.data.DataAlreadyPresentError as dape: + print("Error: " + str(dape)) except KeyError as ke: print("Error: missing " + str(ke)) else: diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc index 7594458b60..306c7954f4 100644 --- a/src/lib/config/module_spec.cc +++ b/src/lib/config/module_spec.cc @@ -327,7 +327,8 @@ ModuleSpec::validateItem(ConstElementPtr spec, ConstElementPtr data, } } if (data->getType() == Element::map) { - // either a 'normal' map or a 'named' set + // either a normal 'map' or a 'named set' (determined by which + // subspecification it has) if (spec->contains("map_item_spec")) { if (!validateSpecList(spec->get("map_item_spec"), data, full, errors)) { return (false); diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py index f7f338e266..fabd37d54b 100644 --- a/src/lib/python/isc/config/config_data.py +++ b/src/lib/python/isc/config/config_data.py @@ -196,7 +196,6 @@ def spec_name_list(spec, prefix="", recurse=False): elif 'named_set_item_spec' in spec: # we added a '/' above, but in this one case we don't want it result.append(prefix[:-1]) - pass else: for name in spec: result.append(prefix + name + "/") @@ -420,6 +419,12 @@ class MultiConfigData: id_prefix += "/" + id_part part_spec = find_spec_part(self._specifications[module].get_config_spec(), id_prefix) if part_spec['item_type'] == 'named_set': + # For named sets, the identifier is partly defined + # by which values are actually present, and not + # purely by the specification. + # So if there is a part of the identifier left, + # we need to look up the value, then see if that + # contains the next part of the identifier we got if len(id_parts) == 0: if 'item_default' in part_spec: return part_spec['item_default'] From 8f5f77f8e2819a66de774a4b7f5216ebc631434c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 27 Jul 2011 13:02:16 +0200 Subject: [PATCH 276/974] [trac1060] Make Query use abstract DataSourceClient It turns out to be simple search & replace, it didn't use any in-memory specific features. The rest of Auth still uses hardcoded in-memory, as it needs to load it, etc. --- src/bin/auth/query.cc | 6 +++--- src/bin/auth/query.h | 18 ++++++++---------- src/bin/auth/tests/query_unittest.cc | 6 +++++- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc index 8be006fb79..05bcd894c8 100644 --- a/src/bin/auth/query.cc +++ b/src/bin/auth/query.cc @@ -19,7 +19,7 @@ #include #include -#include +#include #include @@ -126,8 +126,8 @@ Query::process() const { const bool qtype_is_any = (qtype_ == RRType::ANY()); response_.setHeaderFlag(Message::HEADERFLAG_AA, false); - const InMemoryClient::FindResult result = - memory_client_.findZone(qname_); + const DataSourceClient::FindResult result = + datasrc_client_.findZone(qname_); // If we have no matching authoritative zone for the query name, return // REFUSED. In short, this is to be compatible with BIND 9, but the diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h index d5b7e4ebe5..61baa7795b 100644 --- a/src/bin/auth/query.h +++ b/src/bin/auth/query.h @@ -26,7 +26,7 @@ class RRset; } namespace datasrc { -class InMemoryClient; +class DataSourceClient; } namespace auth { @@ -36,10 +36,8 @@ namespace auth { /// /// Many of the design details for this class are still in flux. /// We'll revisit and update them as we add more functionality, for example: -/// - memory_client parameter of the constructor. It is a data source that -/// uses in memory dedicated backend. /// - as a related point, we may have to pass the RR class of the query. -/// in the initial implementation the RR class is an attribute of memory +/// in the initial implementation the RR class is an attribute of /// datasource and omitted. It's not clear if this assumption holds with /// generic data sources. On the other hand, it will help keep /// implementation simpler, and we might rather want to modify the design @@ -51,7 +49,7 @@ namespace auth { /// separate attribute setter. /// - likewise, we'll eventually need to do per zone access control, for which /// we need querier's information such as its IP address. -/// - memory_client and response may better be parameters to process() instead +/// - datasrc_client and response may better be parameters to process() instead /// of the constructor. /// /// Note: The class name is intentionally the same as the one used in @@ -135,15 +133,15 @@ public: /// /// This constructor never throws an exception. /// - /// \param memory_client The memory datasource wherein the answer to the query is + /// \param datasrc_client The datasource wherein the answer to the query is /// to be found. /// \param qname The query name /// \param qtype The RR type of the query /// \param response The response message to store the answer to the query. - Query(const isc::datasrc::InMemoryClient& memory_client, + Query(const isc::datasrc::DataSourceClient& datasrc_client, const isc::dns::Name& qname, const isc::dns::RRType& qtype, isc::dns::Message& response) : - memory_client_(memory_client), qname_(qname), qtype_(qtype), + datasrc_client_(datasrc_client), qname_(qname), qtype_(qtype), response_(response) {} @@ -157,7 +155,7 @@ public: /// successful search would result in adding a corresponding RRset to /// the answer section of the response. /// - /// If no matching zone is found in the memory datasource, the RCODE of + /// If no matching zone is found in the datasource, the RCODE of /// SERVFAIL will be set in the response. /// Note: this is different from the error code that BIND 9 returns /// by default when it's configured as an authoritative-only server (and @@ -208,7 +206,7 @@ public: }; private: - const isc::datasrc::InMemoryClient& memory_client_; + const isc::datasrc::DataSourceClient& datasrc_client_; const isc::dns::Name& qname_; const isc::dns::RRType& qtype_; isc::dns::Message& response_; diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index 49ce0d6849..577b3ecd81 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -93,7 +93,7 @@ const char* const other_zone_rrs = "mx.delegation.example.com. 3600 IN A 192.0.2.100\n"; // This is a mock Zone class for testing. -// It is a derived class of Zone for the convenient of tests. +// It is a derived class of ZoneFinder for the convenient of tests. // Its find() method emulates the common behavior of protocol compliant // zone classes, but simplifies some minor cases and also supports broken // behavior. @@ -237,6 +237,10 @@ protected: memory_client.addZone(ZoneFinderPtr(mock_finder)); } MockZoneFinder* mock_finder; + // We use InMemoryClient here. We could have some kind of mock client + // here, but historically, the Query supported only InMemoryClient + // (originally named MemoryDataSrc) and was tested with it, so we keep + // it like this for now. InMemoryClient memory_client; const Name qname; const RRClass qclass; From feae0b934e048b17830f49779b01c48136a5b2bf Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 27 Jul 2011 15:09:54 -0400 Subject: [PATCH 277/974] [trac1060] overall documentation updates, typo/wording fixing. --- src/bin/auth/query.h | 17 +++--- src/bin/auth/tests/query_unittest.cc | 2 +- src/lib/datasrc/client.h | 88 +++++++++++++++++++++------- src/lib/datasrc/memory_datasrc.h | 63 ++++++++------------ 4 files changed, 101 insertions(+), 69 deletions(-) diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h index 61baa7795b..fa023fe6f6 100644 --- a/src/bin/auth/query.h +++ b/src/bin/auth/query.h @@ -81,8 +81,8 @@ private: /// This method may throw a exception because its underlying methods may /// throw exceptions. /// - /// \param zone The ZoneFinder wherein the additional data to the query is bo be - /// found. + /// \param zone The ZoneFinder through which the additional data for the + /// query is to be found. /// \param rrset The RRset (i.e., NS or MX rrset) which require additional /// processing. void getAdditional(const isc::datasrc::ZoneFinder& zone, @@ -100,7 +100,8 @@ private: /// The glue records must exactly match the name in the NS RDATA, without /// CNAME or wildcard processing. /// - /// \param zone The \c ZoneFinder wherein the address records is to be found. + /// \param zone The \c ZoneFinder through which the address records is to + /// be found. /// \param qname The name in rrset RDATA. /// \param options The search options. void findAddrs(const isc::datasrc::ZoneFinder& zone, @@ -108,10 +109,10 @@ private: const isc::datasrc::ZoneFinder::FindOptions options = isc::datasrc::ZoneFinder::FIND_DEFAULT) const; - /// \brief Look up \c ZoneFinder's NS and address records for the NS RDATA - /// (domain name) for authoritative answer. + /// \brief Look up a zone's NS RRset and their address records for an + /// authoritative answer. /// - /// On returning an authoritative answer, insert the \c ZoneFinder's NS into the + /// On returning an authoritative answer, insert a zone's NS into the /// authority section and AAAA/A RRs of each of the NS RDATA into the /// additional section. /// @@ -124,8 +125,8 @@ private: /// include AAAA/A RRs under a zone cut in additional section. (BIND 9 /// excludes under-cut RRs; NSD include them.) /// - /// \param zone The \c ZoneFinder wherein the additional data to the query is to - /// be found. + /// \param zone The \c ZoneFinder through which the NS and additional data + /// for the query are to be found. void getAuthAdditional(const isc::datasrc::ZoneFinder& zone) const; public: diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index 577b3ecd81..6a75856eee 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -95,7 +95,7 @@ const char* const other_zone_rrs = // This is a mock Zone class for testing. // It is a derived class of ZoneFinder for the convenient of tests. // Its find() method emulates the common behavior of protocol compliant -// zone classes, but simplifies some minor cases and also supports broken +// ZoneFinder classes, but simplifies some minor cases and also supports broken // behavior. // For simplicity, most names are assumed to be "in zone"; there's only // one zone cut at the point of name "delegation.example.com". diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index d6adef03a5..a830f00c21 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -20,16 +20,56 @@ namespace isc { namespace datasrc { -/// \brief TBD +/// \brief The base class of data source clients. /// -/// naming note: somehow redundant with the namespace of "datasrc", but -/// namespaces are often omitted with 'using' directives. In that case -/// "Client" would be too generic. So we name it with some redundancy. -/// On the other hand, concrete derived classes are generally not expected -/// to be referenced directly from other modules and applications, so -/// we'll give them more concise names such as InMemoryClient. +/// This is an abstract base class that defines the common interface for +/// various types of data source clients. A data source client is a top level +/// access point to a data source, allowing various operations on the data +/// source such as lookups, traversing or updates. The client class itself +/// has limited focus and delegates the responsibility for these specific +/// operations to other classes; in general methods of this class act as +/// factories of these other classes. /// -/// This class is not copyable. +/// The following derived classes are currently (expected to be) provided: +/// - \c InMemoryClient: A client of a conceptual data source that stores +/// all necessary data in memory for faster lookups +/// - \c DatabaseClient: A client that uses a real database backend (such as +/// an SQL database). It would internally hold a connection to the underlying +/// database system. +/// +/// \note It is intentional that while the term these derived classes don't +/// contain "DataSource" unlike their base class. It's also noteworthy +/// that the naming of the base class is somewhat redundant because the +/// namespace \c datasrc would indicate that it's related to a data source. +/// The redundant naming comes from the observation that namespaces are +/// often omitted with \c using directives, in which case "Client" +/// would be too generic. On the other hand, concrete derived classes are +/// generally not expected to be referenced directly from other modules and +/// applications, so we'll give them more concise names such as InMemoryClient. +/// +/// A single \c DataSourceClient object is expected to handle only a single +/// RR class even if the underlying data source contains records for multiple +/// RR classes. Likewise, (when we support views) a \c DataSourceClient +/// object is expected to handle only a single view. +/// +/// If the application uses multiple threads, each thread will need to +/// create and use a separate DataSourceClient. This is because some +/// database backend doesn't allow multiple threads to share the same +/// connection to the database. +/// +/// \note For a client using an in memory backend, this may result in +/// having a multiple copies of the same data in memory, increasing the +/// memory footprint substantially. Depending on how to support multiple +/// CPU cores for concurrent lookups on the same single data source (which +/// is not fully fixed yet, and for which multiple threads may be used), +/// this design may have to be revisited. +/// +/// This class (and therefore its derived classes) are not copyable. +/// This is because the derived classes would generally contain attributes +/// that are not easy to copy (such as a large size of in memory data or a +/// network connection to a database server). In order to avoid a surprising +/// disruption with a naive copy it's prohibited explicitly. For the expected +/// usage of the client classes the restriction should be acceptable. /// /// \todo This class is not complete. It needs more factory methods, for /// accessing the whole zone, updating it, loading it, etc. @@ -65,10 +105,13 @@ public: protected: /// Default constructor. /// - /// \exception - /// This constructor internally involves resource allocation, and if - /// it fails, a corresponding standard exception will be thrown. - /// It never throws an exception otherwise. + /// This is intentionally defined as protected as this base class + /// should never be instantiated directly. + /// + /// The constructor of a concrete derived class may throw an exception. + /// This interface does not specify which exceptions can happen (at least + /// at this moment), and the caller should expect any type of exception + /// and react accordingly. DataSourceClient() {} public: @@ -76,21 +119,24 @@ public: virtual ~DataSourceClient() {} //@} - /// Find a \c Zone that best matches the given name via this client. + /// Returns a \c ZoneFinder for a zone that best matches the given name. /// - /// It searches the internal storage for a \c Zone that gives the - /// longest match against \c name, and returns the result in the - /// form of a \c FindResult object as follows: + /// A concrete derived version of this method gets access to its backend + /// data source to search for a zone whose origin gives the longest match + /// against \c name. It returns the search result in the form of a + /// \c FindResult object as follows: /// - \c code: The result code of the operation. - /// - \c result::SUCCESS: A zone that gives an exact match - /// is found + /// - \c result::SUCCESS: A zone that gives an exact match is found /// - \c result::PARTIALMATCH: A zone whose origin is a /// super domain of \c name is found (but there is no exact match) /// - \c result::NOTFOUND: For all other cases. - /// - \c zone: Pointer to the found \c ZoneFinder object if one - /// is found; otherwise \c NULL. + /// - \c zone_finder: Pointer to a \c ZoneFinder object for the found zone + /// if one is found; otherwise \c NULL. /// - /// This method never throws an exception. + /// A specific derived version of this method may throw an exception. + /// This interface does not specify which exceptions can happen (at least + /// at this moment), and the caller should expect any type of exception + /// and react accordingly. /// /// \param name A domain name for which the search is performed. /// \return A \c FindResult object enclosing the search result (see above). diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 9476e097ea..288563ede0 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -183,34 +183,33 @@ private: //@} }; -/// \brief A data source that uses in memory dedicated backend. +/// \brief A data source client that holds all necessary data in memory. /// -/// The \c InMemoryClient class represents a data source and provides a -/// basic interface to help DNS lookup processing. For a given domain -/// name, its \c findZone() method searches the in memory dedicated backend -/// for the zone that gives a longest match against that name. +/// The \c InMemoryClient class provides an access to a conceptual data +/// source that maintains all necessary data in a memory image, thereby +/// allowing much faster lookups. The in memory data is a copy of some +/// real physical source - in the current implementation a list of zones +/// are populated as a result of \c addZone() calls; zone data is given +/// in a standard master file (but there's a plan to use database backends +/// as a source of the in memory data). /// -/// The in memory dedicated backend are assumed to be of the same RR class, -/// but the \c InMemoryClient class does not enforce the assumption through +/// Although every data source client is assumed to be of the same RR class, +/// the \c InMemoryClient class does not enforce the assumption through /// its interface. /// For example, the \c addZone() method does not check if the new zone is of -/// the same RR class as that of the others already in the dedicated backend. +/// the same RR class as that of the others already in memory. /// It is caller's responsibility to ensure this assumption. /// /// Notes to developer: /// -/// For now, we don't make it a derived class of AbstractDataSrc because the -/// interface is so different (we'll eventually consider this as part of the -/// generalization work). -/// /// The addZone() method takes a (Boost) shared pointer because it would be /// inconvenient to require the caller to maintain the ownership of zones, /// while it wouldn't be safe to delete unnecessary zones inside the dedicated /// backend. /// -/// The findZone() method takes a domain name and returns the best matching \c -/// MemoryZone in the form of (Boost) shared pointer, so that it can provide -/// the general interface for all data sources. +/// The findZone() method takes a domain name and returns the best matching +/// \c MemoryZoneFinder in the form of (Boost) shared pointer, so that it can +/// provide the general interface for all data sources. class InMemoryClient : public DataSourceClient { public: /// @@ -229,51 +228,37 @@ public: ~InMemoryClient(); //@} - /// Return the number of zones stored in the data source. + /// Return the number of zones stored in the client. /// /// This method never throws an exception. /// - /// \return The number of zones stored in the data source. + /// \return The number of zones stored in the client. unsigned int getZoneCount() const; - /// Add a \c Zone to the \c InMemoryClient. + /// Add a zone (in the form of \c ZoneFinder) to the \c InMemoryClient. /// - /// \c Zone must not be associated with a NULL pointer; otherwise + /// \c zone must not be associated with a NULL pointer; otherwise /// an exception of class \c InvalidParameter will be thrown. /// If internal resource allocation fails, a corresponding standard /// exception will be thrown. /// This method never throws an exception otherwise. /// - /// \param zone A \c Zone object to be added. + /// \param zone A \c ZoneFinder object to be added. /// \return \c result::SUCCESS If the zone is successfully - /// added to the memory data source. + /// added to the client. /// \return \c result::EXIST The memory data source already /// stores a zone that has the same origin. result::Result addZone(ZoneFinderPtr zone); - /// Find a \c Zone that best matches the given name in the \c InMemoryClient. + /// Returns a \c ZoneFinder for a zone that best matches the given name. /// - /// It searches the internal storage for a \c Zone that gives the - /// longest match against \c name, and returns the result in the - /// form of a \c FindResult object as follows: - /// - \c code: The result code of the operation. - /// - \c result::SUCCESS: A zone that gives an exact match - /// is found - /// - \c result::PARTIALMATCH: A zone whose origin is a - /// super domain of \c name is found (but there is no exact match) - /// - \c result::NOTFOUND: For all other cases. - /// - \c zone: A "Boost" shared pointer to the found \c Zone object if one - /// is found; otherwise \c NULL. - /// - /// This method never throws an exception. - /// - /// \param name A domain name for which the search is performed. - /// \return A \c FindResult object enclosing the search result (see above). + /// This derived version of the method never throws an exception. + /// For other details see \c DataSourceClient::findZone(). virtual FindResult findZone(const isc::dns::Name& name) const; private: // TODO: Do we still need the PImpl if nobody should manipulate this class - // directly any more (it should be handled trough DataSourceClient)? + // directly any more (it should be handled through DataSourceClient)? class InMemoryClientImpl; InMemoryClientImpl* impl_; }; From 0373b72ac00aaecb7745cf7fd129424994e2fab8 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Thu, 28 Jul 2011 11:12:49 +0800 Subject: [PATCH 278/974] [trac1128] implement SRV rr type --- src/lib/dns/Makefile.am | 2 + src/lib/dns/rdata/generic/srv_33.cc | 170 ++++++++++++++++++ src/lib/dns/rdata/generic/srv_33.h | 60 +++++++ src/lib/dns/tests/Makefile.am | 1 + src/lib/dns/tests/rdata_srv_unittest.cc | 167 +++++++++++++++++ src/lib/dns/tests/testdata/rdata_srv_fromWire | 27 +++ 6 files changed, 427 insertions(+) create mode 100644 src/lib/dns/rdata/generic/srv_33.cc create mode 100644 src/lib/dns/rdata/generic/srv_33.h create mode 100644 src/lib/dns/tests/rdata_srv_unittest.cc create mode 100644 src/lib/dns/tests/testdata/rdata_srv_fromWire diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 887ac09fee..e28bbaef5e 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -51,6 +51,8 @@ EXTRA_DIST += rdata/generic/soa_6.cc EXTRA_DIST += rdata/generic/soa_6.h EXTRA_DIST += rdata/generic/txt_16.cc EXTRA_DIST += rdata/generic/txt_16.h +EXTRA_DIST += rdata/generic/srv_33.cc +EXTRA_DIST += rdata/generic/srv_33.h EXTRA_DIST += rdata/hs_4/a_1.cc EXTRA_DIST += rdata/hs_4/a_1.h EXTRA_DIST += rdata/in_1/a_1.cc diff --git a/src/lib/dns/rdata/generic/srv_33.cc b/src/lib/dns/rdata/generic/srv_33.cc new file mode 100644 index 0000000000..257f801c77 --- /dev/null +++ b/src/lib/dns/rdata/generic/srv_33.cc @@ -0,0 +1,170 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct SRVImpl { + // straightforward representation of SRV RDATA fields + SRVImpl(uint16_t priority, uint16_t weight, uint16_t port, + const Name& target) : + priority_(priority), weight_(weight), port_(port), + target_(target) + {} + + uint16_t priority_; + uint16_t weight_; + uint16_t port_; + Name target_; +}; + +SRV::SRV(const string& srv_str) : + impl_(NULL) +{ + istringstream iss(srv_str); + string targetname; + unsigned int priority, weight, port; + + iss >> priority >> weight >> port >> targetname; + if (iss.bad() || iss.fail()) { + isc_throw(InvalidRdataText, "Invalid SRV text"); + } + if (priority > 0xffff) { + isc_throw(InvalidRdataText, "SRV priority out of range"); + } + if (weight > 0xffff) { + isc_throw(InvalidRdataText, "SRV weight out of range"); + } + if (port > 0xffff) { + isc_throw(InvalidRdataText, "SRV port out of range"); + } + + impl_ = new SRVImpl(priority, weight, port, Name(targetname)); +} + +SRV::SRV(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 6) { + isc_throw(InvalidRdataLength, "SRV too short"); + } + + uint16_t priority = buffer.readUint16(); + uint16_t weight = buffer.readUint16(); + uint16_t port = buffer.readUint16(); + const Name targetname(buffer); + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +SRV::SRV(const SRV& source) : + Rdata(), impl_(new SRVImpl(*source.impl_)) +{} + +SRV& +SRV::operator=(const SRV& source) { + if (impl_ == source.impl_) { + return (*this); + } + + SRVImpl* newimpl = new SRVImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SRV::~SRV() { + delete impl_; +} + +string +SRV::toText() const { + using namespace boost; + return (lexical_cast(static_cast(impl_->priority_)) + + " " + lexical_cast(static_cast(impl_->weight_)) + + " " + lexical_cast(static_cast(impl_->port_)) + + " " + impl_->target_.toText()); +} + +void +SRV::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->priority_); + buffer.writeUint16(impl_->weight_); + buffer.writeUint16(impl_->port_); + impl_->target_.toWire(buffer); +} + +void +SRV::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->priority_); + renderer.writeUint16(impl_->weight_); + renderer.writeUint16(impl_->port_); + renderer.writeName(impl_->target_, false); +} + +int +SRV::compare(const Rdata& other) const { + const SRV& other_srv = dynamic_cast(other); + + if (impl_->priority_ != other_srv.impl_->priority_) { + return (impl_->priority_ < other_srv.impl_->priority_ ? -1 : 1); + } + if (impl_->weight_ != other_srv.impl_->weight_) { + return (impl_->weight_ < other_srv.impl_->weight_ ? -1 : 1); + } + if (impl_->port_ != other_srv.impl_->port_) { + return (impl_->port_ < other_srv.impl_->port_ ? -1 : 1); + } + + return (compareNames(impl_->target_, other_srv.impl_->target_)); +} + +uint16_t +SRV::getPriority() const { + return (impl_->priority_); +} + +uint16_t +SRV::getWeight() const { + return (impl_->weight_); +} + +uint16_t +SRV::getPort() const { + return (impl_->port_); +} + +const Name& +SRV::getTarget() const { + return (impl_->target_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/srv_33.h b/src/lib/dns/rdata/generic/srv_33.h new file mode 100644 index 0000000000..81771bbd3d --- /dev/null +++ b/src/lib/dns/rdata/generic/srv_33.h @@ -0,0 +1,60 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include + +#include +#include +#include +#include + +// BEGIN_HEADER_GUARD + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct SRVImpl; + +class SRV : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + SRV& operator=(const SRV& source); + ~SRV(); + + /// + /// Specialized methods + /// + uint16_t getPriority() const; + uint16_t getWeight() const; + uint16_t getPort() const; + const Name& getTarget() const; + +private: + SRVImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index 3a249c1768..bd6fbe2a6e 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -41,6 +41,7 @@ run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc run_unittests_SOURCES += rdata_nsec3param_unittest.cc run_unittests_SOURCES += rdata_rrsig_unittest.cc run_unittests_SOURCES += rdata_rp_unittest.cc +run_unittests_SOURCES += rdata_srv_unittest.cc run_unittests_SOURCES += rdata_tsig_unittest.cc run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc run_unittests_SOURCES += question_unittest.cc diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc new file mode 100644 index 0000000000..0bb1fef1ec --- /dev/null +++ b/src/lib/dns/tests/rdata_srv_unittest.cc @@ -0,0 +1,167 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for generic +// 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using isc::UnitTestUtil; +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { +class Rdata_SRV_Test : public RdataTest { + // there's nothing to specialize +}; + +string srv_txt("1 5 1500 a.example.com."); +string srv_txt2("1 5 1400 example.com."); +string too_long_label("012345678901234567890123456789" + "0123456789012345678901234567890123"); + +// 1 5 1500 a.example.com. +const uint8_t wiredata_srv[] = { + 0x00, 0x01, 0x00, 0x05, 0x05, 0xdc, 0x01, 0x61, 0x07, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; +// 1 5 1400 example.com. +const uint8_t wiredata_srv2[] = { + 0x00, 0x01, 0x00, 0x05, 0x05, 0x78, 0x07, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; + +const generic::SRV rdata_srv(srv_txt); +const generic::SRV rdata_srv2(srv_txt2); + +TEST_F(Rdata_SRV_Test, createFromText) { + EXPECT_EQ(1, rdata_srv.getPriority()); + EXPECT_EQ(5, rdata_srv.getWeight()); + EXPECT_EQ(1500, rdata_srv.getPort()); + EXPECT_EQ(Name("a.example.com."), rdata_srv.getTarget()); +} + +TEST_F(Rdata_SRV_Test, badText) { + // priority is too large (2814...6 is 2^48) + EXPECT_THROW(generic::SRV("281474976710656 5 1500 a.example.com."), + InvalidRdataText); + // weight is too large + EXPECT_THROW(generic::SRV("1 281474976710656 1500 a.example.com."), + InvalidRdataText); + // port is too large + EXPECT_THROW(generic::SRV("1 5 281474976710656 a.example.com."), + InvalidRdataText); + // incomplete text + EXPECT_THROW(generic::SRV("1 5 a.example.com."), + InvalidRdataText); + // bad name + EXPECT_THROW(generic::SRV("1 5 1500 a.example.com." + too_long_label), + TooLongLabel); +} + +TEST_F(Rdata_SRV_Test, assignment) { + generic::SRV copy((string(srv_txt2))); + copy = rdata_srv; + EXPECT_EQ(0, copy.compare(rdata_srv)); + + // Check if the copied data is valid even after the original is deleted + generic::SRV* copy2 = new generic::SRV(rdata_srv); + generic::SRV copy3((string(srv_txt2))); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_srv)); + + // Self assignment + copy = copy; + EXPECT_EQ(0, copy.compare(rdata_srv)); +} + +TEST_F(Rdata_SRV_Test, createFromWire) { + EXPECT_EQ(0, rdata_srv.compare( + *rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_srv_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_srv_fromWire", 23), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_srv_fromWire", 46), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_cname_fromWire", 69), + DNSMessageFORMERR); +} + +TEST_F(Rdata_SRV_Test, toWireBuffer) { + rdata_srv.toWire(obuffer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + wiredata_srv, sizeof(wiredata_srv)); + obuffer.clear(); + rdata_srv2.toWire(obuffer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + wiredata_srv2, sizeof(wiredata_srv2)); +} + +TEST_F(Rdata_SRV_Test, toWireRenderer) { + rdata_srv.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + wiredata_srv, sizeof(wiredata_srv)); + renderer.clear(); + rdata_srv2.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + wiredata_srv2, sizeof(wiredata_srv2)); +} + +TEST_F(Rdata_SRV_Test, toText) { + EXPECT_EQ(srv_txt, rdata_srv.toText()); + EXPECT_EQ(srv_txt2, rdata_srv2.toText()); +} + +TEST_F(Rdata_SRV_Test, compare) { + // test RDATAs, sorted in the ascendent order. + vector compare_set; + compare_set.push_back(generic::SRV("1 5 1500 a.example.com.")); + compare_set.push_back(generic::SRV("2 5 1500 a.example.com.")); + compare_set.push_back(generic::SRV("2 6 1500 a.example.com.")); + compare_set.push_back(generic::SRV("2 6 1600 a.example.com.")); + compare_set.push_back(generic::SRV("2 6 1600 example.com.")); + + EXPECT_EQ(0, compare_set[0].compare( + generic::SRV("1 5 1500 a.example.com."))); + + vector::const_iterator it; + vector::const_iterator it_end = compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_srv.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/testdata/rdata_srv_fromWire b/src/lib/dns/tests/testdata/rdata_srv_fromWire new file mode 100644 index 0000000000..659757a379 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_srv_fromWire @@ -0,0 +1,27 @@ +# +# various kinds of SRV RDATA stored in an input buffer +# +# RDLENGHT=21 bytes +# 0 1 + 00 15 +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2(bytes) + 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# short length +# 3 4 + 00 12 +# 5 6 7 8 9 30 1 2 3 4 5 6 7 8 9 40 1 2 3 4 5 + 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# length too long +# 6 7 + 00 19 +# +# 8 9 50 1 2 3 4 5 6 7 8 9 60 1 2 3 4 5 6 7 8 + 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# incomplete target name +# 9 70 + 00 06 +# 1 2 3 4 5 6 7 8 9 70 1 2 3 4 5 6 7 8 + 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 From 34634d2ba1efba222403e8a210379d1573759939 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Thu, 28 Jul 2011 11:17:49 +0800 Subject: [PATCH 279/974] [trac1128] update copyright information --- src/lib/dns/rdata/generic/srv_33.cc | 2 +- src/lib/dns/rdata/generic/srv_33.h | 2 +- src/lib/dns/tests/rdata_srv_unittest.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/dns/rdata/generic/srv_33.cc b/src/lib/dns/rdata/generic/srv_33.cc index 257f801c77..119a2768be 100644 --- a/src/lib/dns/rdata/generic/srv_33.cc +++ b/src/lib/dns/rdata/generic/srv_33.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above diff --git a/src/lib/dns/rdata/generic/srv_33.h b/src/lib/dns/rdata/generic/srv_33.h index 81771bbd3d..8a060d6b10 100644 --- a/src/lib/dns/rdata/generic/srv_33.h +++ b/src/lib/dns/rdata/generic/srv_33.h @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc index 0bb1fef1ec..f4a6e062ad 100644 --- a/src/lib/dns/tests/rdata_srv_unittest.cc +++ b/src/lib/dns/tests/rdata_srv_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for generic // purpose with or without fee is hereby granted, provided that the above From d42d232acb16847ea8ec775854469e3226cdfe17 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Thu, 28 Jul 2011 13:10:35 +0800 Subject: [PATCH 280/974] [trac1128] update comments and minor fix --- src/lib/dns/rdata/generic/srv_33.cc | 2 ++ src/lib/dns/tests/rdata_srv_unittest.cc | 4 ++++ src/lib/dns/tests/testdata/rdata_srv_fromWire | 13 +++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/rdata/generic/srv_33.cc b/src/lib/dns/rdata/generic/srv_33.cc index 119a2768be..fd6b694d6c 100644 --- a/src/lib/dns/rdata/generic/srv_33.cc +++ b/src/lib/dns/rdata/generic/srv_33.cc @@ -126,6 +126,8 @@ SRV::toWire(AbstractMessageRenderer& renderer) const { renderer.writeUint16(impl_->priority_); renderer.writeUint16(impl_->weight_); renderer.writeUint16(impl_->port_); + // According to RFC 2782, name compression is not + // to be used for this field. renderer.writeName(impl_->target_, false); } diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc index f4a6e062ad..0cb7f27ab1 100644 --- a/src/lib/dns/tests/rdata_srv_unittest.cc +++ b/src/lib/dns/tests/rdata_srv_unittest.cc @@ -111,6 +111,10 @@ TEST_F(Rdata_SRV_Test, createFromWire) { EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), "rdata_cname_fromWire", 69), DNSMessageFORMERR); + // parse compressed target name + EXPECT_EQ(0, rdata_srv.compare( + *rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_srv_fromWire", 89))); } TEST_F(Rdata_SRV_Test, toWireBuffer) { diff --git a/src/lib/dns/tests/testdata/rdata_srv_fromWire b/src/lib/dns/tests/testdata/rdata_srv_fromWire index 659757a379..dac87e9144 100644 --- a/src/lib/dns/tests/testdata/rdata_srv_fromWire +++ b/src/lib/dns/tests/testdata/rdata_srv_fromWire @@ -20,8 +20,17 @@ # 8 9 50 1 2 3 4 5 6 7 8 9 60 1 2 3 4 5 6 7 8 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 # +# # incomplete target name # 9 70 - 00 06 -# 1 2 3 4 5 6 7 8 9 70 1 2 3 4 5 6 7 8 + 00 12 +# 1 2 3 4 5 6 7 8 9 80 1 2 3 4 5 6 7 8 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 +# +# +# Valid compressed target name: 'a' + pointer +# 9 90 + 00 0a +# +# 1 2 3 4 5 6 7 8 9 100 + 00 01 00 05 05 dc 01 61 c0 0a From 119442008b97f3b39d0ade075dd219a2f781e2a3 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 28 Jul 2011 11:23:06 +0200 Subject: [PATCH 281/974] [trac1060] Rename MemoryZoneFinder to InMemoryZoneFinder To preserve consistency. --- src/bin/auth/auth_config.cc | 3 +- src/bin/auth/command.cc | 8 +- src/lib/datasrc/memory_datasrc.cc | 36 ++-- src/lib/datasrc/memory_datasrc.h | 16 +- .../datasrc/tests/memory_datasrc_unittest.cc | 173 +++++++++--------- src/lib/datasrc/tests/zonetable_unittest.cc | 30 +-- 6 files changed, 136 insertions(+), 130 deletions(-) diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc index 0198963bf2..e45d499a4b 100644 --- a/src/bin/auth/auth_config.cc +++ b/src/bin/auth/auth_config.cc @@ -163,7 +163,8 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) { isc_throw(AuthConfigError, "Missing zone file for zone: " << origin->str()); } - shared_ptr new_zone(new MemoryZoneFinder(rrclass_, + shared_ptr new_zone(new + InMemoryZoneFinder(rrclass_, Name(origin->stringValue()))); const result::Result result = memory_client_->addZone(new_zone); if (result == result::EXIST) { diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc index 87bae406ae..a240457401 100644 --- a/src/bin/auth/command.cc +++ b/src/bin/auth/command.cc @@ -136,8 +136,8 @@ public: // that doesn't block other server operations. // TODO: we may (should?) want to check the "last load time" and // the timestamp of the file and skip loading if the file isn't newer. - shared_ptr newzone( - new MemoryZoneFinder(oldzone->getClass(), oldzone->getOrigin())); + shared_ptr newzone( + new InMemoryZoneFinder(oldzone->getClass(), oldzone->getOrigin())); newzone->load(oldzone->getFileName()); oldzone->swap(*newzone); LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE) @@ -146,7 +146,7 @@ public: private: // zone finder to be updated with the new file. - shared_ptr oldzone; + shared_ptr oldzone; // A helper private method to parse and validate command parameters. // On success, it sets 'oldzone' to the zone to be updated. @@ -195,7 +195,7 @@ private: " is not found in data source"); } - oldzone = boost::dynamic_pointer_cast( + oldzone = boost::dynamic_pointer_cast( result.zone_finder); return (true); diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index a0edd4d56b..23a714e36e 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -32,10 +32,10 @@ using namespace isc::dns; namespace isc { namespace datasrc { -// Private data and hidden methods of MemoryZoneFinder -struct MemoryZoneFinder::MemoryZoneFinderImpl { +// Private data and hidden methods of InMemoryZoneFinder +struct InMemoryZoneFinder::InMemoryZoneFinderImpl { // Constructor - MemoryZoneFinderImpl(const RRClass& zone_class, const Name& origin) : + InMemoryZoneFinderImpl(const RRClass& zone_class, const Name& origin) : zone_class_(zone_class), origin_(origin), origin_data_(NULL), domains_(true) { @@ -223,7 +223,7 @@ struct MemoryZoneFinder::MemoryZoneFinderImpl { * Implementation of longer methods. We put them here, because the * access is without the impl_-> and it will get inlined anyway. */ - // Implementation of MemoryZoneFinder::add + // Implementation of InMemoryZoneFinder::add result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) { // Sanitize input. This will cause an exception to be thrown // if the input RRset is empty. @@ -409,7 +409,7 @@ struct MemoryZoneFinder::MemoryZoneFinderImpl { } } - // Implementation of MemoryZoneFinder::find + // Implementation of InMemoryZoneFinder::find FindResult find(const Name& name, RRType type, RRsetList* target, const FindOptions options) const { @@ -593,50 +593,50 @@ struct MemoryZoneFinder::MemoryZoneFinderImpl { } }; -MemoryZoneFinder::MemoryZoneFinder(const RRClass& zone_class, const Name& origin) : - impl_(new MemoryZoneFinderImpl(zone_class, origin)) +InMemoryZoneFinder::InMemoryZoneFinder(const RRClass& zone_class, const Name& origin) : + impl_(new InMemoryZoneFinderImpl(zone_class, origin)) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin). arg(zone_class); } -MemoryZoneFinder::~MemoryZoneFinder() { +InMemoryZoneFinder::~InMemoryZoneFinder() { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_DESTROY).arg(getOrigin()). arg(getClass()); delete impl_; } const Name& -MemoryZoneFinder::getOrigin() const { +InMemoryZoneFinder::getOrigin() const { return (impl_->origin_); } const RRClass& -MemoryZoneFinder::getClass() const { +InMemoryZoneFinder::getClass() const { return (impl_->zone_class_); } ZoneFinder::FindResult -MemoryZoneFinder::find(const Name& name, const RRType& type, +InMemoryZoneFinder::find(const Name& name, const RRType& type, RRsetList* target, const FindOptions options) const { return (impl_->find(name, type, target, options)); } result::Result -MemoryZoneFinder::add(const ConstRRsetPtr& rrset) { +InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) { return (impl_->add(rrset, &impl_->domains_)); } void -MemoryZoneFinder::load(const string& filename) { +InMemoryZoneFinder::load(const string& filename) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()). arg(filename); // Load it into a temporary tree - MemoryZoneFinderImpl::DomainTree tmp; + InMemoryZoneFinderImpl::DomainTree tmp; masterLoad(filename.c_str(), getOrigin(), getClass(), - boost::bind(&MemoryZoneFinderImpl::addFromLoad, impl_, _1, &tmp)); + boost::bind(&InMemoryZoneFinderImpl::addFromLoad, impl_, _1, &tmp)); // If it went well, put it inside impl_->file_name_ = filename; tmp.swap(impl_->domains_); @@ -644,14 +644,14 @@ MemoryZoneFinder::load(const string& filename) { } void -MemoryZoneFinder::swap(MemoryZoneFinder& zone) { +InMemoryZoneFinder::swap(InMemoryZoneFinder& zone) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()). arg(zone.getOrigin()); std::swap(impl_, zone.impl_); } const string -MemoryZoneFinder::getFileName() const { +InMemoryZoneFinder::getFileName() const { return (impl_->file_name_); } @@ -659,7 +659,7 @@ MemoryZoneFinder::getFileName() const { /// interface. /// /// For now, \c InMemoryClient only contains a \c ZoneTable object, which -/// consists of (pointers to) \c MemoryZoneFinder objects, we may add more +/// consists of (pointers to) \c InMemoryZoneFinder objects, we may add more /// member variables later for new features. class InMemoryClient::InMemoryClientImpl { public: diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 288563ede0..d57b0dc05c 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -38,7 +38,7 @@ namespace datasrc { /// backend). This is why the class has methods like \c load() or \c add(). /// /// This class is non copyable. -class MemoryZoneFinder : boost::noncopyable, public ZoneFinder { +class InMemoryZoneFinder : boost::noncopyable, public ZoneFinder { /// /// \name Constructors and Destructor. public: @@ -50,11 +50,11 @@ public: /// /// \param rrclass The RR class of the zone. /// \param origin The origin name of the zone. - MemoryZoneFinder(const isc::dns::RRClass& rrclass, + InMemoryZoneFinder(const isc::dns::RRClass& rrclass, const isc::dns::Name& origin); /// The destructor. - virtual ~MemoryZoneFinder(); + virtual ~InMemoryZoneFinder(); //@} /// \brief Returns the origin of the zone. @@ -171,15 +171,15 @@ public: /// /// This method never throws an exception. /// - /// \param zone Another \c MemoryZone object which is to be swapped with + /// \param zone Another \c InMemoryZone object which is to be swapped with /// \c this zone. - void swap(MemoryZoneFinder& zone); + void swap(InMemoryZoneFinder& zone); private: /// \name Hidden private data //@{ - struct MemoryZoneFinderImpl; - MemoryZoneFinderImpl* impl_; + struct InMemoryZoneFinderImpl; + InMemoryZoneFinderImpl* impl_; //@} }; @@ -208,7 +208,7 @@ private: /// backend. /// /// The findZone() method takes a domain name and returns the best matching -/// \c MemoryZoneFinder in the form of (Boost) shared pointer, so that it can +/// \c InMemoryZoneFinder in the form of (Boost) shared pointer, so that it can /// provide the general interface for all data sources. class InMemoryClient : public DataSourceClient { public: diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 8d960b0674..67cbb2eeca 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -58,48 +58,50 @@ TEST_F(InMemoryClientTest, add_find_Zone) { // add zones with different names one by one EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), - Name("a"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), + Name("a"))))); EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), Name("b"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(), + Name("b"))))); EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), Name("c"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), + Name("c"))))); // add zones with the same name suffix EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), - Name("x.d.e.f"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(), + Name("x.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), - Name("o.w.y.d.e.f"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(), + Name("o.w.y.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), - Name("p.w.y.d.e.f"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(), + Name("p.w.y.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), - Name("q.w.y.d.e.f"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), + Name("q.w.y.d.e.f"))))); // add super zone and its subzone EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), - Name("g.h"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(), + Name("g.h"))))); EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), Name("i.g.h"))))); EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), - Name("z.d.e.f"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), + Name("z.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), - Name("j.z.d.e.f"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), + Name("j.z.d.e.f"))))); // different zone class isn't allowed. EXPECT_EQ(result::EXIST, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), - Name("q.w.y.d.e.f"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(), + Name("q.w.y.d.e.f"))))); // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), - Name("Q.W.Y.d.E.f"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), + Name("Q.W.Y.d.E.f"))))); // test find zone EXPECT_EQ(result::SUCCESS, memory_client.findZone(Name("a")).code); @@ -109,7 +111,8 @@ TEST_F(InMemoryClientTest, add_find_Zone) { EXPECT_EQ(result::SUCCESS, memory_client.findZone(Name("j.z.d.e.f")).code); EXPECT_EQ(Name("j.z.d.e.f"), - memory_client.findZone(Name("j.z.d.e.f")).zone_finder->getOrigin()); + memory_client.findZone(Name("j.z.d.e.f")).zone_finder-> + getOrigin()); // NOTFOUND EXPECT_EQ(result::NOTFOUND, memory_client.findZone(Name("d.e.f")).code); @@ -131,38 +134,39 @@ TEST_F(InMemoryClientTest, add_find_Zone) { EXPECT_EQ(result::PARTIALMATCH, memory_client.findZone(Name("z.i.g.h")).code); EXPECT_EQ(Name("i.g.h"), - memory_client.findZone(Name("z.i.g.h")).zone_finder->getOrigin()); + memory_client.findZone(Name("z.i.g.h")).zone_finder-> + getOrigin()); } TEST_F(InMemoryClientTest, getZoneCount) { EXPECT_EQ(0, memory_client.getZoneCount()); memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(rrclass, - Name("example.com")))); + ZoneFinderPtr(new InMemoryZoneFinder(rrclass, + Name("example.com")))); EXPECT_EQ(1, memory_client.getZoneCount()); // duplicate add. counter shouldn't change memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(rrclass, - Name("example.com")))); + ZoneFinderPtr(new InMemoryZoneFinder(rrclass, + Name("example.com")))); EXPECT_EQ(1, memory_client.getZoneCount()); // add one more memory_client.addZone( - ZoneFinderPtr(new MemoryZoneFinder(rrclass, - Name("example.org")))); + ZoneFinderPtr(new InMemoryZoneFinder(rrclass, + Name("example.org")))); EXPECT_EQ(2, memory_client.getZoneCount()); } -// A helper callback of masterLoad() used in MemoryZoneFinderTest. +// A helper callback of masterLoad() used in InMemoryZoneFinderTest. void setRRset(RRsetPtr rrset, vector::iterator& it) { *(*it) = rrset; ++it; } -/// \brief Test fixture for the MemoryZoneFinder class -class MemoryZoneFinderTest : public ::testing::Test { +/// \brief Test fixture for the InMemoryZoneFinder class +class InMemoryZoneFinderTest : public ::testing::Test { // A straightforward pair of textual RR(set) and a RRsetPtr variable // to store the RRset. Used to build test data below. struct RRsetData { @@ -170,7 +174,7 @@ class MemoryZoneFinderTest : public ::testing::Test { RRsetPtr* rrset; }; public: - MemoryZoneFinderTest() : + InMemoryZoneFinderTest() : class_(RRClass::IN()), origin_("example.org"), zone_finder_(class_, origin_) @@ -233,7 +237,7 @@ public: const RRClass class_; const Name origin_; // The zone to torture by tests - MemoryZoneFinder zone_finder_; + InMemoryZoneFinder zone_finder_; /* * Some RRsets to put inside the zone. @@ -282,7 +286,7 @@ public: * \param check_answer Should a check against equality of the answer be * done? * \param answer The expected rrset, if any should be returned. - * \param zone Check different MemoryZoneFinder object than zone_ (if NULL, + * \param zone Check different InMemoryZoneFinder object than zone_ (if NULL, * uses zone_) * \param check_wild_answer Checks that the answer has the same RRs, type * class and TTL as the eqxpected answer and that the name corresponds @@ -294,7 +298,7 @@ public: bool check_answer = true, const ConstRRsetPtr& answer = ConstRRsetPtr(), RRsetList* target = NULL, - MemoryZoneFinder* zone_finder = NULL, + InMemoryZoneFinder* zone_finder = NULL, ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT, bool check_wild_answer = false) { @@ -347,12 +351,12 @@ public: }; /** - * \brief Test MemoryZoneFinder::MemoryZoneFinder constructor. + * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor. * * Takes the created zone and checks its properties they are the same * as passed parameters. */ -TEST_F(MemoryZoneFinderTest, constructor) { +TEST_F(InMemoryZoneFinderTest, constructor) { ASSERT_EQ(class_, zone_finder_.getClass()); ASSERT_EQ(origin_, zone_finder_.getOrigin()); } @@ -362,12 +366,12 @@ TEST_F(MemoryZoneFinderTest, constructor) { * We test that it throws at the correct moments and the correct exceptions. * And we test the return value. */ -TEST_F(MemoryZoneFinderTest, add) { +TEST_F(InMemoryZoneFinderTest, add) { // This one does not belong to this zone - EXPECT_THROW(zone_finder_.add(rr_out_), MemoryZoneFinder::OutOfZone); + EXPECT_THROW(zone_finder_.add(rr_out_), InMemoryZoneFinder::OutOfZone); // Test null pointer EXPECT_THROW(zone_finder_.add(ConstRRsetPtr()), - MemoryZoneFinder::NullRRset); + InMemoryZoneFinder::NullRRset); // Now put all the data we have there. It should throw nothing EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); @@ -380,22 +384,22 @@ TEST_F(MemoryZoneFinderTest, add) { EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_finder_.add(rr_ns_a_))); } -TEST_F(MemoryZoneFinderTest, addMultipleCNAMEs) { +TEST_F(InMemoryZoneFinderTest, addMultipleCNAMEs) { rr_cname_->addRdata(generic::CNAME("canonical2.example.org.")); - EXPECT_THROW(zone_finder_.add(rr_cname_), MemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_cname_), InMemoryZoneFinder::AddError); } -TEST_F(MemoryZoneFinderTest, addCNAMEThenOther) { +TEST_F(InMemoryZoneFinderTest, addCNAMEThenOther) { EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_)); - EXPECT_THROW(zone_finder_.add(rr_cname_a_), MemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_cname_a_), InMemoryZoneFinder::AddError); } -TEST_F(MemoryZoneFinderTest, addOtherThenCNAME) { +TEST_F(InMemoryZoneFinderTest, addOtherThenCNAME) { EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_a_)); - EXPECT_THROW(zone_finder_.add(rr_cname_), MemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_cname_), InMemoryZoneFinder::AddError); } -TEST_F(MemoryZoneFinderTest, findCNAME) { +TEST_F(InMemoryZoneFinderTest, findCNAME) { // install CNAME RR EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_)); @@ -408,7 +412,7 @@ TEST_F(MemoryZoneFinderTest, findCNAME) { rr_cname_); } -TEST_F(MemoryZoneFinderTest, findCNAMEUnderZoneCut) { +TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) { // There's nothing special when we find a CNAME under a zone cut // (with FIND_GLUE_OK). The behavior is different from BIND 9, // so we test this case explicitly. @@ -425,27 +429,27 @@ TEST_F(MemoryZoneFinderTest, findCNAMEUnderZoneCut) { // Two DNAMEs at single domain are disallowed by RFC 2672, section 3) // Having a CNAME there is disallowed too, but it is tested by // addOtherThenCNAME and addCNAMEThenOther. -TEST_F(MemoryZoneFinderTest, addMultipleDNAMEs) { +TEST_F(InMemoryZoneFinderTest, addMultipleDNAMEs) { rr_dname_->addRdata(generic::DNAME("target2.example.org.")); - EXPECT_THROW(zone_finder_.add(rr_dname_), MemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_dname_), InMemoryZoneFinder::AddError); } /* * These two tests ensure that we can't have DNAME and NS at the same * node with the exception of the apex of zone (forbidden by RFC 2672) */ -TEST_F(MemoryZoneFinderTest, addDNAMEThenNS) { +TEST_F(InMemoryZoneFinderTest, addDNAMEThenNS) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_))); - EXPECT_THROW(zone_finder_.add(rr_dname_ns_), MemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_dname_ns_), InMemoryZoneFinder::AddError); } -TEST_F(MemoryZoneFinderTest, addNSThenDNAME) { +TEST_F(InMemoryZoneFinderTest, addNSThenDNAME) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_ns_))); - EXPECT_THROW(zone_finder_.add(rr_dname_), MemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_dname_), InMemoryZoneFinder::AddError); } // It is allowed to have NS and DNAME at apex -TEST_F(MemoryZoneFinderTest, DNAMEAndNSAtApex) { +TEST_F(InMemoryZoneFinderTest, DNAMEAndNSAtApex) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_apex_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); @@ -456,7 +460,7 @@ TEST_F(MemoryZoneFinderTest, DNAMEAndNSAtApex) { rr_dname_apex_); } -TEST_F(MemoryZoneFinderTest, NSAndDNAMEAtApex) { +TEST_F(InMemoryZoneFinderTest, NSAndDNAMEAtApex) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_apex_))); } @@ -465,7 +469,7 @@ TEST_F(MemoryZoneFinderTest, NSAndDNAMEAtApex) { // 2672 as well. // Search under a DNAME record. It should return the DNAME -TEST_F(MemoryZoneFinderTest, findBelowDNAME) { +TEST_F(InMemoryZoneFinderTest, findBelowDNAME) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_))); findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME, true, rr_dname_); @@ -473,7 +477,7 @@ TEST_F(MemoryZoneFinderTest, findBelowDNAME) { // Search at the domain with DNAME. It should act as DNAME isn't there, DNAME // influences only the data below (see RFC 2672, section 3) -TEST_F(MemoryZoneFinderTest, findAtDNAME) { +TEST_F(InMemoryZoneFinderTest, findAtDNAME) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_a_))); @@ -486,7 +490,7 @@ TEST_F(MemoryZoneFinderTest, findAtDNAME) { // Try searching something that is both under NS and DNAME, without and with // GLUE_OK mode (it should stop at the NS and DNAME respectively). -TEST_F(MemoryZoneFinderTest, DNAMEUnderNS) { +TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) { zone_finder_.add(rr_child_ns_); zone_finder_.add(rr_child_dname_); @@ -498,7 +502,7 @@ TEST_F(MemoryZoneFinderTest, DNAMEUnderNS) { } // Test adding child zones and zone cut handling -TEST_F(MemoryZoneFinderTest, delegationNS) { +TEST_F(InMemoryZoneFinderTest, delegationNS) { // add in-zone data EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); @@ -526,7 +530,7 @@ TEST_F(MemoryZoneFinderTest, delegationNS) { ZoneFinder::DELEGATION, true, rr_child_ns_); } -TEST_F(MemoryZoneFinderTest, findAny) { +TEST_F(InMemoryZoneFinderTest, findAny) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_))); @@ -571,7 +575,7 @@ TEST_F(MemoryZoneFinderTest, findAny) { EXPECT_EQ(0, new_glue_child_rrsets.size()); } -TEST_F(MemoryZoneFinderTest, glue) { +TEST_F(InMemoryZoneFinderTest, glue) { // install zone data: // a zone cut EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_))); @@ -619,7 +623,7 @@ TEST_F(MemoryZoneFinderTest, glue) { * \todo This doesn't do any kind of CNAME and so on. If it isn't * directly there, it just tells it doesn't exist. */ -TEST_F(MemoryZoneFinderTest, find) { +TEST_F(InMemoryZoneFinderTest, find) { // Fill some data inside // Now put all the data we have there. It should throw nothing EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_))); @@ -641,7 +645,7 @@ TEST_F(MemoryZoneFinderTest, find) { findTest(Name("example.net"), RRType::A(), ZoneFinder::NXDOMAIN); } -TEST_F(MemoryZoneFinderTest, emptyNode) { +TEST_F(InMemoryZoneFinderTest, emptyNode) { /* * The backend RBTree for this test should look like as follows: * example.org @@ -680,7 +684,7 @@ TEST_F(MemoryZoneFinderTest, emptyNode) { findTest(Name("org"), RRType::A(), ZoneFinder::NXDOMAIN); } -TEST_F(MemoryZoneFinderTest, load) { +TEST_F(InMemoryZoneFinderTest, load) { // Put some data inside the zone EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_finder_.add(rr_ns_))); // Loading with different origin should fail @@ -689,7 +693,7 @@ TEST_F(MemoryZoneFinderTest, load) { // See the original data is still there, survived the exception findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_); // Create correct zone - MemoryZoneFinder rootzone(class_, Name(".")); + InMemoryZoneFinder rootzone(class_, Name(".")); // Try putting something inside EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, rootzone.add(rr_ns_aaaa_))); // Load the zone. It should overwrite/remove the above RRset @@ -715,7 +719,7 @@ TEST_F(MemoryZoneFinderTest, load) { * Test that puts a (simple) wildcard into the zone and checks we can * correctly find the data. */ -TEST_F(MemoryZoneFinderTest, wildcard) { +TEST_F(InMemoryZoneFinderTest, wildcard) { /* * example.org. * | @@ -768,7 +772,7 @@ TEST_F(MemoryZoneFinderTest, wildcard) { * - When the query is in another zone. That is, delegation cancels * the wildcard defaults." */ -TEST_F(MemoryZoneFinderTest, delegatedWildcard) { +TEST_F(InMemoryZoneFinderTest, delegatedWildcard) { EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_wild_)); EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)); @@ -787,7 +791,7 @@ TEST_F(MemoryZoneFinderTest, delegatedWildcard) { } // Tests combination of wildcard and ANY. -TEST_F(MemoryZoneFinderTest, anyWildcard) { +TEST_F(InMemoryZoneFinderTest, anyWildcard) { EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_)); // First try directly the name (normal match) @@ -815,7 +819,7 @@ TEST_F(MemoryZoneFinderTest, anyWildcard) { // Test there's nothing in the wildcard in the middle if we load // wild.*.foo.example.org. -TEST_F(MemoryZoneFinderTest, emptyWildcard) { +TEST_F(InMemoryZoneFinderTest, emptyWildcard) { /* * example.org. * foo @@ -858,7 +862,7 @@ TEST_F(MemoryZoneFinderTest, emptyWildcard) { } // Same as emptyWildcard, but with multiple * in the path. -TEST_F(MemoryZoneFinderTest, nestedEmptyWildcard) { +TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) { EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nested_emptywild_)); { @@ -918,7 +922,7 @@ TEST_F(MemoryZoneFinderTest, nestedEmptyWildcard) { // We run this part twice from the below test, in two slightly different // situations void -MemoryZoneFinderTest::doCancelWildcardTest() { +InMemoryZoneFinderTest::doCancelWildcardTest() { // These should be canceled { SCOPED_TRACE("Canceled under foo.wild.example.org"); @@ -972,7 +976,7 @@ MemoryZoneFinderTest::doCancelWildcardTest() { * Tests few cases "around" the canceled wildcard match, to see something that * shouldn't be canceled isn't. */ -TEST_F(MemoryZoneFinderTest, cancelWildcard) { +TEST_F(InMemoryZoneFinderTest, cancelWildcard) { EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_)); EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_)); @@ -991,23 +995,24 @@ TEST_F(MemoryZoneFinderTest, cancelWildcard) { } } -TEST_F(MemoryZoneFinderTest, loadBadWildcard) { +TEST_F(InMemoryZoneFinderTest, loadBadWildcard) { // We reject loading the zone if it contains a wildcard name for // NS or DNAME. - EXPECT_THROW(zone_finder_.add(rr_nswild_), MemoryZoneFinder::AddError); - EXPECT_THROW(zone_finder_.add(rr_dnamewild_), MemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_nswild_), InMemoryZoneFinder::AddError); + EXPECT_THROW(zone_finder_.add(rr_dnamewild_), + InMemoryZoneFinder::AddError); } -TEST_F(MemoryZoneFinderTest, swap) { +TEST_F(InMemoryZoneFinderTest, swap) { // build one zone with some data - MemoryZoneFinder zone1(class_, origin_); + InMemoryZoneFinder zone1(class_, origin_); EXPECT_EQ(result::SUCCESS, zone1.add(rr_ns_)); EXPECT_EQ(result::SUCCESS, zone1.add(rr_ns_aaaa_)); // build another zone of a different RR class with some other data const Name other_origin("version.bind"); ASSERT_NE(origin_, other_origin); // make sure these two are different - MemoryZoneFinder zone2(RRClass::CH(), other_origin); + InMemoryZoneFinder zone2(RRClass::CH(), other_origin); EXPECT_EQ(result::SUCCESS, zone2.add(RRsetPtr(new RRset(Name("version.bind"), RRClass::CH(), RRType::TXT(), @@ -1029,7 +1034,7 @@ TEST_F(MemoryZoneFinderTest, swap) { ConstRRsetPtr(), NULL, &zone2); } -TEST_F(MemoryZoneFinderTest, getFileName) { +TEST_F(InMemoryZoneFinderTest, getFileName) { // for an empty zone the file name should also be empty. EXPECT_TRUE(zone_finder_.getFileName().empty()); @@ -1039,7 +1044,7 @@ TEST_F(MemoryZoneFinderTest, getFileName) { EXPECT_TRUE(zone_finder_.getFileName().empty()); // after a successful load, the specified file name should be set - MemoryZoneFinder rootzone(class_, Name(".")); + InMemoryZoneFinder rootzone(class_, Name(".")); EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone")); EXPECT_EQ(TEST_DATA_DIR "/root.zone", rootzone.getFileName()); // overriding load, which will fail diff --git a/src/lib/datasrc/tests/zonetable_unittest.cc b/src/lib/datasrc/tests/zonetable_unittest.cc index ec13382d61..fa74c0eb8c 100644 --- a/src/lib/datasrc/tests/zonetable_unittest.cc +++ b/src/lib/datasrc/tests/zonetable_unittest.cc @@ -18,7 +18,7 @@ #include #include -// We use MemoryZone to put something into the table +// We use InMemoryZone to put something into the table #include #include @@ -28,29 +28,29 @@ using namespace isc::datasrc; namespace { TEST(ZoneTest, init) { - MemoryZoneFinder zone(RRClass::IN(), Name("example.com")); + InMemoryZoneFinder zone(RRClass::IN(), Name("example.com")); EXPECT_EQ(Name("example.com"), zone.getOrigin()); EXPECT_EQ(RRClass::IN(), zone.getClass()); - MemoryZoneFinder ch_zone(RRClass::CH(), Name("example")); + InMemoryZoneFinder ch_zone(RRClass::CH(), Name("example")); EXPECT_EQ(Name("example"), ch_zone.getOrigin()); EXPECT_EQ(RRClass::CH(), ch_zone.getClass()); } TEST(ZoneTest, find) { - MemoryZoneFinder zone(RRClass::IN(), Name("example.com")); + InMemoryZoneFinder zone(RRClass::IN(), Name("example.com")); EXPECT_EQ(ZoneFinder::NXDOMAIN, zone.find(Name("www.example.com"), RRType::A()).code); } class ZoneTableTest : public ::testing::Test { protected: - ZoneTableTest() : zone1(new MemoryZoneFinder(RRClass::IN(), - Name("example.com"))), - zone2(new MemoryZoneFinder(RRClass::IN(), - Name("example.net"))), - zone3(new MemoryZoneFinder(RRClass::IN(), - Name("example"))) + ZoneTableTest() : zone1(new InMemoryZoneFinder(RRClass::IN(), + Name("example.com"))), + zone2(new InMemoryZoneFinder(RRClass::IN(), + Name("example.net"))), + zone3(new InMemoryZoneFinder(RRClass::IN(), + Name("example"))) {} ZoneTable zone_table; ZoneFinderPtr zone1, zone2, zone3; @@ -61,8 +61,8 @@ TEST_F(ZoneTableTest, addZone) { EXPECT_EQ(result::EXIST, zone_table.addZone(zone1)); // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, zone_table.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::IN(), - Name("EXAMPLE.COM"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), + Name("EXAMPLE.COM"))))); EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone2)); EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone3)); @@ -70,8 +70,8 @@ TEST_F(ZoneTableTest, addZone) { // Zone table is indexed only by name. Duplicate origin name with // different zone class isn't allowed. EXPECT_EQ(result::EXIST, zone_table.addZone( - ZoneFinderPtr(new MemoryZoneFinder(RRClass::CH(), - Name("example.com"))))); + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(), + Name("example.com"))))); /// Bogus zone (NULL) EXPECT_THROW(zone_table.addZone(ZoneFinderPtr()), isc::InvalidParameter); @@ -109,7 +109,7 @@ TEST_F(ZoneTableTest, findZone) { // make sure the partial match is indeed the longest match by adding // a zone with a shorter origin and query again. - ZoneFinderPtr zone_com(new MemoryZoneFinder(RRClass::IN(), Name("com"))); + ZoneFinderPtr zone_com(new InMemoryZoneFinder(RRClass::IN(), Name("com"))); EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone_com)); EXPECT_EQ(Name("example.com"), zone_table.findZone(Name("www.example.com")).zone->getOrigin()); From 0d2c284222839ff21401cecb7cb567cb0cc04127 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 28 Jul 2011 12:14:17 +0200 Subject: [PATCH 282/974] [trac1060] Some more renaming zone->zone_finder in many cases --- src/bin/auth/auth_config.cc | 8 ++-- src/bin/auth/command.cc | 15 ++++--- src/lib/datasrc/memory_datasrc.cc | 14 +++--- src/lib/datasrc/memory_datasrc.h | 30 +++++++------ .../datasrc/tests/memory_datasrc_unittest.cc | 44 +++++++++---------- 5 files changed, 57 insertions(+), 54 deletions(-) diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc index e45d499a4b..d684c68611 100644 --- a/src/bin/auth/auth_config.cc +++ b/src/bin/auth/auth_config.cc @@ -163,10 +163,10 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) { isc_throw(AuthConfigError, "Missing zone file for zone: " << origin->str()); } - shared_ptr new_zone(new - InMemoryZoneFinder(rrclass_, + shared_ptr zone_finder(new + InMemoryZoneFinder(rrclass_, Name(origin->stringValue()))); - const result::Result result = memory_client_->addZone(new_zone); + const result::Result result = memory_client_->addZone(zone_finder); if (result == result::EXIST) { isc_throw(AuthConfigError, "zone "<< origin->str() << " already exists"); @@ -178,7 +178,7 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) { * need the load method to be split into some kind of build and * commit/abort parts. */ - new_zone->load(file->stringValue()); + zone_finder->load(file->stringValue()); } } diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc index a240457401..0944005b03 100644 --- a/src/bin/auth/command.cc +++ b/src/bin/auth/command.cc @@ -136,20 +136,21 @@ public: // that doesn't block other server operations. // TODO: we may (should?) want to check the "last load time" and // the timestamp of the file and skip loading if the file isn't newer. - shared_ptr newzone( - new InMemoryZoneFinder(oldzone->getClass(), oldzone->getOrigin())); - newzone->load(oldzone->getFileName()); - oldzone->swap(*newzone); + shared_ptr zone_finder( + new InMemoryZoneFinder(old_zone_finder->getClass(), + old_zone_finder->getOrigin())); + newzone->load(old_zone_finder->getFileName()); + old_zone_finder->swap(*newzone); LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE) .arg(newzone->getOrigin()).arg(newzone->getClass()); } private: // zone finder to be updated with the new file. - shared_ptr oldzone; + shared_ptr old_zone_finder; // A helper private method to parse and validate command parameters. - // On success, it sets 'oldzone' to the zone to be updated. + // On success, it sets 'old_zone_finder' to the zone to be updated. // It returns true if everything is okay; and false if the command is // valid but there's no need for further process. bool validate(AuthSrv& server, isc::data::ConstElementPtr args) { @@ -195,7 +196,7 @@ private: " is not found in data source"); } - oldzone = boost::dynamic_pointer_cast( + old_zone_finder = boost::dynamic_pointer_cast( result.zone_finder); return (true); diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 23a714e36e..3d24ce0200 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -644,10 +644,10 @@ InMemoryZoneFinder::load(const string& filename) { } void -InMemoryZoneFinder::swap(InMemoryZoneFinder& zone) { +InMemoryZoneFinder::swap(InMemoryZoneFinder& zone_finder) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()). - arg(zone.getOrigin()); - std::swap(impl_, zone.impl_); + arg(zone_finder.getOrigin()); + std::swap(impl_, zone_finder.impl_); } const string @@ -681,16 +681,16 @@ InMemoryClient::getZoneCount() const { } result::Result -InMemoryClient::addZone(ZoneFinderPtr zone) { - if (!zone) { +InMemoryClient::addZone(ZoneFinderPtr zone_finder) { + if (!zone_finder) { isc_throw(InvalidParameter, "Null pointer is passed to InMemoryClient::addZone()"); } LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE). - arg(zone->getOrigin()).arg(zone->getClass().toText()); + arg(zone_finder->getOrigin()).arg(zone_finder->getClass().toText()); - const result::Result result = impl_->zone_table.addZone(zone); + const result::Result result = impl_->zone_table.addZone(zone_finder); if (result == result::SUCCESS) { ++impl_->zone_count; } diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index d57b0dc05c..9bed9603c1 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -30,7 +30,7 @@ class RRsetList; namespace datasrc { -/// A derived zone class intended to be used with the memory data source. +/// A derived zone finder class intended to be used with the memory data source. /// /// Conceptually this "finder" maintains a local in-memory copy of all RRs /// of a single zone from some kind of source (right now it's a textual @@ -51,7 +51,7 @@ public: /// \param rrclass The RR class of the zone. /// \param origin The origin name of the zone. InMemoryZoneFinder(const isc::dns::RRClass& rrclass, - const isc::dns::Name& origin); + const isc::dns::Name& origin); /// The destructor. virtual ~InMemoryZoneFinder(); @@ -131,14 +131,14 @@ public: /// Return the master file name of the zone /// /// This method returns the name of the zone's master file to be loaded. - /// The returned string will be an empty unless the zone has successfully - /// loaded a zone. + /// The returned string will be an empty unless the zone finder has + /// successfully loaded a zone. /// /// This method should normally not throw an exception. But the creation /// of the return string may involve a resource allocation, and if it /// fails, the corresponding standard exception will be thrown. /// - /// \return The name of the zone file loaded in the zone, or an empty + /// \return The name of the zone file loaded in the zone finder, or an empty /// string if the zone hasn't loaded any file. const std::string getFileName() const; @@ -167,13 +167,14 @@ public: /// configuration reloading is written. void load(const std::string& filename); - /// Exchanges the content of \c this zone with that of the given \c zone. + /// Exchanges the content of \c this zone finder with that of the given + /// \c zone_finder. /// /// This method never throws an exception. /// - /// \param zone Another \c InMemoryZone object which is to be swapped with - /// \c this zone. - void swap(InMemoryZoneFinder& zone); + /// \param zone_finder Another \c InMemoryZone object which is to + /// be swapped with \c this zone finder. + void swap(InMemoryZoneFinder& zone_finder); private: /// \name Hidden private data @@ -237,20 +238,21 @@ public: /// Add a zone (in the form of \c ZoneFinder) to the \c InMemoryClient. /// - /// \c zone must not be associated with a NULL pointer; otherwise + /// \c zone_finder must not be associated with a NULL pointer; otherwise /// an exception of class \c InvalidParameter will be thrown. /// If internal resource allocation fails, a corresponding standard /// exception will be thrown. /// This method never throws an exception otherwise. /// - /// \param zone A \c ZoneFinder object to be added. - /// \return \c result::SUCCESS If the zone is successfully + /// \param zone_finder A \c ZoneFinder object to be added. + /// \return \c result::SUCCESS If the zone_finder is successfully /// added to the client. /// \return \c result::EXIST The memory data source already /// stores a zone that has the same origin. - result::Result addZone(ZoneFinderPtr zone); + result::Result addZone(ZoneFinderPtr zone_finder); - /// Returns a \c ZoneFinder for a zone that best matches the given name. + /// Returns a \c ZoneFinder for a zone_finder that best matches the given + /// name. /// /// This derived version of the method never throws an exception. /// For other details see \c DataSourceClient::findZone(). diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 67cbb2eeca..22723fc76d 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -236,7 +236,7 @@ public: // Some data to test with const RRClass class_; const Name origin_; - // The zone to torture by tests + // The zone finder to torture by tests InMemoryZoneFinder zone_finder_; /* @@ -274,9 +274,9 @@ public: RRsetPtr rr_not_wild_another_; /** - * \brief Test one find query to the zone. + * \brief Test one find query to the zone finder. * - * Asks a query to the zone and checks it does not throw and returns + * Asks a query to the zone finder and checks it does not throw and returns * expected results. It returns nothing, it just signals failures * to GTEST. * @@ -286,8 +286,8 @@ public: * \param check_answer Should a check against equality of the answer be * done? * \param answer The expected rrset, if any should be returned. - * \param zone Check different InMemoryZoneFinder object than zone_ (if NULL, - * uses zone_) + * \param zone_finder Check different InMemoryZoneFinder object than + * zone_finder_ (if NULL, uses zone_finder_) * \param check_wild_answer Checks that the answer has the same RRs, type * class and TTL as the eqxpected answer and that the name corresponds * to the one searched. It is meant for checking answers for wildcard @@ -353,7 +353,7 @@ public: /** * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor. * - * Takes the created zone and checks its properties they are the same + * Takes the created zone finder and checks its properties they are the same * as passed parameters. */ TEST_F(InMemoryZoneFinderTest, constructor) { @@ -1004,34 +1004,34 @@ TEST_F(InMemoryZoneFinderTest, loadBadWildcard) { } TEST_F(InMemoryZoneFinderTest, swap) { - // build one zone with some data - InMemoryZoneFinder zone1(class_, origin_); - EXPECT_EQ(result::SUCCESS, zone1.add(rr_ns_)); - EXPECT_EQ(result::SUCCESS, zone1.add(rr_ns_aaaa_)); + // build one zone finder with some data + InMemoryZoneFinder finder1(class_, origin_); + EXPECT_EQ(result::SUCCESS, finder1.add(rr_ns_)); + EXPECT_EQ(result::SUCCESS, finder1.add(rr_ns_aaaa_)); - // build another zone of a different RR class with some other data + // build another zone finder of a different RR class with some other data const Name other_origin("version.bind"); ASSERT_NE(origin_, other_origin); // make sure these two are different - InMemoryZoneFinder zone2(RRClass::CH(), other_origin); + InMemoryZoneFinder finder2(RRClass::CH(), other_origin); EXPECT_EQ(result::SUCCESS, - zone2.add(RRsetPtr(new RRset(Name("version.bind"), + finder2.add(RRsetPtr(new RRset(Name("version.bind"), RRClass::CH(), RRType::TXT(), RRTTL(0))))); - zone1.swap(zone2); - EXPECT_EQ(other_origin, zone1.getOrigin()); - EXPECT_EQ(origin_, zone2.getOrigin()); - EXPECT_EQ(RRClass::CH(), zone1.getClass()); - EXPECT_EQ(RRClass::IN(), zone2.getClass()); + finder1.swap(finder2); + EXPECT_EQ(other_origin, finder1.getOrigin()); + EXPECT_EQ(origin_, finder2.getOrigin()); + EXPECT_EQ(RRClass::CH(), finder1.getClass()); + EXPECT_EQ(RRClass::IN(), finder2.getClass()); // make sure the zone data is swapped, too findTest(origin_, RRType::NS(), ZoneFinder::NXDOMAIN, false, - ConstRRsetPtr(), NULL, &zone1); + ConstRRsetPtr(), NULL, &finder1); findTest(other_origin, RRType::TXT(), ZoneFinder::SUCCESS, false, - ConstRRsetPtr(), NULL, &zone1); + ConstRRsetPtr(), NULL, &finder1); findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, false, - ConstRRsetPtr(), NULL, &zone2); + ConstRRsetPtr(), NULL, &finder2); findTest(other_origin, RRType::TXT(), ZoneFinder::NXDOMAIN, false, - ConstRRsetPtr(), NULL, &zone2); + ConstRRsetPtr(), NULL, &finder2); } TEST_F(InMemoryZoneFinderTest, getFileName) { From 8907c6a5c71816483099683e0ddcaf11cf3a7912 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 28 Jul 2011 07:59:29 -0400 Subject: [PATCH 283/974] [trac1060] corrected a variable name to make it compile --- src/bin/auth/command.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc index 0944005b03..940d57bbb4 100644 --- a/src/bin/auth/command.cc +++ b/src/bin/auth/command.cc @@ -139,10 +139,10 @@ public: shared_ptr zone_finder( new InMemoryZoneFinder(old_zone_finder->getClass(), old_zone_finder->getOrigin())); - newzone->load(old_zone_finder->getFileName()); - old_zone_finder->swap(*newzone); + zone_finder->load(old_zone_finder->getFileName()); + old_zone_finder->swap(*zone_finder); LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE) - .arg(newzone->getOrigin()).arg(newzone->getClass()); + .arg(zone_finder->getOrigin()).arg(zone_finder->getClass()); } private: From dd7e5d47df1e9af687cdc87c2d2595893eefec12 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 28 Jul 2011 09:14:58 -0400 Subject: [PATCH 284/974] [trac1060] comment wording fix: s/Memory/InMemory/ --- src/lib/datasrc/rbtree.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h index 03a696749c..ccdfa4856b 100644 --- a/src/lib/datasrc/rbtree.h +++ b/src/lib/datasrc/rbtree.h @@ -704,9 +704,9 @@ public: /// \brief Find with callback and node chain. /// /// This version of \c find() is specifically designed for the backend - /// of the \c MemoryZone class, and implements all necessary features - /// for that purpose. Other applications shouldn't need these additional - /// features, and should normally use the simpler versions. + /// of the \c InMemoryZoneFinder class, and implements all necessary + /// features for that purpose. Other applications shouldn't need these + /// additional features, and should normally use the simpler versions. /// /// This version of \c find() calls the callback whenever traversing (on /// the way from root down the tree) a marked node on the way down through From 21850ab947dbdf98b1d89afc36d8bcfc6001592e Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Thu, 28 Jul 2011 16:35:55 -0400 Subject: [PATCH 285/974] [trac1140] txt and spf rr types derived from common template base --- src/lib/dns/rdata/generic/detail/txt_like.h | 168 ++++++++++++++++++++ src/lib/dns/rdata/generic/spf_99.cc | 19 +++ src/lib/dns/rdata/generic/spf_99.h | 85 ++++++++++ src/lib/dns/rdata/generic/txt_16.cc | 141 ---------------- src/lib/dns/rdata/generic/txt_16.h | 54 ++++++- 5 files changed, 318 insertions(+), 149 deletions(-) create mode 100644 src/lib/dns/rdata/generic/detail/txt_like.h create mode 100644 src/lib/dns/rdata/generic/spf_99.cc create mode 100644 src/lib/dns/rdata/generic/spf_99.h diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h new file mode 100644 index 0000000000..3e117f0371 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/txt_like.h @@ -0,0 +1,168 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __TXT_LIKE_H +#define __TXT_LIKE_H 1 + +#include + +#include +#include + +using namespace std; +using namespace isc::util; + +templateclass TXT_LIKE : public Rdata { +public: + TXT_LIKE(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); + } + + if (rdata_len == 0) { // note that this couldn't happen in the loop. + isc_throw(DNSMessageFORMERR, + "Error in parsing " + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " RDATA: 0-length character string"); + } + + do { + const uint8_t len = buffer.readUint8(); + if (rdata_len < len + 1) { + isc_throw(DNSMessageFORMERR, + "Error in parsing " + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " RDATA: character string length is too large: " << static_cast(len)); + } + vector data(len + 1); + data[0] = len; + buffer.readData(&data[0] + 1, len); + string_list_.push_back(data); + + rdata_len -= (len + 1); + } while (rdata_len > 0); + } + + explicit TXT_LIKE(const std::string& txtstr) { + // TBD: this is a simple, incomplete implementation that only supports + // a single character-string. + + size_t length = txtstr.size(); + size_t pos_begin = 0; + + if (length > 1 && txtstr[0] == '"' && txtstr[length - 1] == '"') { + pos_begin = 1; + length -= 2; + } + + if (length > MAX_CHARSTRING_LEN) { + isc_throw(CharStringTooLong, RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " RDATA construction from text: string length is too long: " << length); + } + + // TBD: right now, we don't support escaped characters + if (txtstr.find('\\') != string::npos) { + isc_throw(InvalidRdataText, RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " RDATA from text: escaped character is currently not supported: " << txtstr); + } + + vector data; + data.reserve(length + 1); + data.push_back(length); + data.insert(data.end(), txtstr.begin() + pos_begin, + txtstr.begin() + pos_begin + length); + string_list_.push_back(data); + } + + TXT_LIKE(const RTYPE& other) : + Rdata(), string_list_(other.string_list_) + {} + + void + toWire(OutputBuffer& buffer) const { + for (vector >::const_iterator it = string_list_.begin(); + it != string_list_.end(); + ++it) + { + buffer.writeData(&(*it)[0], (*it).size()); + } + } + + void + toWire(AbstractMessageRenderer& renderer) const { + for (vector >::const_iterator it = string_list_.begin(); + it != string_list_.end(); + ++it) + { + renderer.writeData(&(*it)[0], (*it).size()); + } + } + + string + toText() const { + string s; + + // XXX: this implementation is not entirely correct. for example, it + // should escape double-quotes if they appear in the character string. + for (vector >::const_iterator it = string_list_.begin(); + it != string_list_.end(); + ++it) + { + if (!s.empty()) { + s.push_back(' '); + } + s.push_back('"'); + s.insert(s.end(), (*it).begin() + 1, (*it).end()); + s.push_back('"'); + } + + return (s); + } + + int + compare(const Rdata& other) const { + const RTYPE& other_txt = dynamic_cast(other); + + // This implementation is not efficient. Revisit this (TBD). + OutputBuffer this_buffer(0); + toWire(this_buffer); + size_t this_len = this_buffer.getLength(); + + OutputBuffer other_buffer(0); + other_txt.toWire(other_buffer); + const size_t other_len = other_buffer.getLength(); + + const size_t cmplen = min(this_len, other_len); + const int cmp = memcmp(this_buffer.getData(), other_buffer.getData(), + cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : + (this_len < other_len) ? -1 : 1); + } + } + +private: + /// Note: this is a prototype version; we may reconsider + /// this representation later. + std::vector > string_list_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE + +#endif // __TXT_LIKE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/spf_99.cc b/src/lib/dns/rdata/generic/spf_99.cc new file mode 100644 index 0000000000..cf72d76882 --- /dev/null +++ b/src/lib/dns/rdata/generic/spf_99.cc @@ -0,0 +1,19 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/spf_99.h b/src/lib/dns/rdata/generic/spf_99.h new file mode 100644 index 0000000000..7556b4bd72 --- /dev/null +++ b/src/lib/dns/rdata/generic/spf_99.h @@ -0,0 +1,85 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_HEADER_GUARD + +#include + +#include +#include + +#include +#include +#include +#include + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +#include + +class SPF : public TXT_LIKE { + friend class TXT_LIKE; + static string const id; + +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + +}; + +/// explicit SPF(const std::string& type_str); +inline SPF::SPF(const std::string& type_str) : TXT_LIKE(type_str) {} + +/// SPF(isc::util::InputBuffer& buffer, size_t rdata_len); +inline SPF::SPF(isc::util::InputBuffer& buffer, size_t rdata_len) : TXT_LIKE(buffer, rdata_len) {} + +/// SPF(const SPF& other); +inline SPF::SPF(const SPF& other) : TXT_LIKE(other) {} + +/// virtual std::string toText() const; +inline std::string SPF::toText() const +{ + return TXT_LIKE::toText(); +} + +/// virtual void toWire(isc::util::OutputBuffer& buffer) const; +inline void SPF::toWire(isc::util::OutputBuffer& buffer) const +{ + TXT_LIKE::toWire(buffer); +} + +/// virtual void toWire(AbstractMessageRenderer& renderer) const; +inline void SPF::toWire(AbstractMessageRenderer& renderer) const +{ + TXT_LIKE::toWire(renderer); +} + +/// virtual int compare(const Rdata& other) const; +inline int SPF::compare(const Rdata& other) const +{ + return TXT_LIKE::compare(other); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc index ac2ba8a9f0..cf72d76882 100644 --- a/src/lib/dns/rdata/generic/txt_16.cc +++ b/src/lib/dns/rdata/generic/txt_16.cc @@ -12,149 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -using namespace std; -using namespace isc::util; - // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE -TXT::TXT(InputBuffer& buffer, size_t rdata_len) { - if (rdata_len > MAX_RDLENGTH) { - isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); - } - - if (rdata_len == 0) { // note that this couldn't happen in the loop. - isc_throw(DNSMessageFORMERR, - "Error in parsing TXT RDATA: 0-length character string"); - } - - do { - const uint8_t len = buffer.readUint8(); - if (rdata_len < len + 1) { - isc_throw(DNSMessageFORMERR, - "Error in parsing TXT RDATA: character string length " - "is too large: " << static_cast(len)); - } - vector data(len + 1); - data[0] = len; - buffer.readData(&data[0] + 1, len); - string_list_.push_back(data); - - rdata_len -= (len + 1); - } while (rdata_len > 0); -} - -TXT::TXT(const std::string& txtstr) { - // TBD: this is a simple, incomplete implementation that only supports - // a single character-string. - - size_t length = txtstr.size(); - size_t pos_begin = 0; - - if (length > 1 && txtstr[0] == '"' && txtstr[length - 1] == '"') { - pos_begin = 1; - length -= 2; - } - - if (length > MAX_CHARSTRING_LEN) { - isc_throw(CharStringTooLong, "TXT RDATA construction from text: " - "string length is too long: " << length); - } - - // TBD: right now, we don't support escaped characters - if (txtstr.find('\\') != string::npos) { - isc_throw(InvalidRdataText, "TXT RDATA from text: " - "escaped character is currently not supported: " << txtstr); - } - - vector data; - data.reserve(length + 1); - data.push_back(length); - data.insert(data.end(), txtstr.begin() + pos_begin, - txtstr.begin() + pos_begin + length); - string_list_.push_back(data); -} - -TXT::TXT(const TXT& other) : - Rdata(), string_list_(other.string_list_) -{} - -void -TXT::toWire(OutputBuffer& buffer) const { - for (vector >::const_iterator it = string_list_.begin(); - it != string_list_.end(); - ++it) - { - buffer.writeData(&(*it)[0], (*it).size()); - } -} - -void -TXT::toWire(AbstractMessageRenderer& renderer) const { - for (vector >::const_iterator it = string_list_.begin(); - it != string_list_.end(); - ++it) - { - renderer.writeData(&(*it)[0], (*it).size()); - } -} - -string -TXT::toText() const { - string s; - - // XXX: this implementation is not entirely correct. for example, it - // should escape double-quotes if they appear in the character string. - for (vector >::const_iterator it = string_list_.begin(); - it != string_list_.end(); - ++it) - { - if (!s.empty()) { - s.push_back(' '); - } - s.push_back('"'); - s.insert(s.end(), (*it).begin() + 1, (*it).end()); - s.push_back('"'); - } - - return (s); -} - -int -TXT::compare(const Rdata& other) const { - const TXT& other_txt = dynamic_cast(other); - - // This implementation is not efficient. Revisit this (TBD). - OutputBuffer this_buffer(0); - toWire(this_buffer); - size_t this_len = this_buffer.getLength(); - - OutputBuffer other_buffer(0); - other_txt.toWire(other_buffer); - const size_t other_len = other_buffer.getLength(); - - const size_t cmplen = min(this_len, other_len); - const int cmp = memcmp(this_buffer.getData(), other_buffer.getData(), - cmplen); - if (cmp != 0) { - return (cmp); - } else { - return ((this_len == other_len) ? 0 : - (this_len < other_len) ? -1 : 1); - } -} - // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/txt_16.h b/src/lib/dns/rdata/generic/txt_16.h index b4c791f6ae..84ac143b16 100644 --- a/src/lib/dns/rdata/generic/txt_16.h +++ b/src/lib/dns/rdata/generic/txt_16.h @@ -14,13 +14,16 @@ // BEGIN_HEADER_GUARD -#include - #include -#include +#include #include +#include +#include +#include +#include + // BEGIN_ISC_NAMESPACE // BEGIN_COMMON_DECLARATIONS @@ -28,16 +31,51 @@ // BEGIN_RDATA_NAMESPACE -class TXT : public Rdata { +#include + +class TXT : public TXT_LIKE { + friend class TXT_LIKE; + static string const id; + public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS -private: - /// Note: this is a prototype version; we may reconsider - /// this representation later. - std::vector > string_list_; + }; +/// explicit TXT(const std::string& type_str); +inline TXT::TXT(const std::string& type_str) : TXT_LIKE(type_str) {} + +/// TXT(isc::util::InputBuffer& buffer, size_t rdata_len); +inline TXT::TXT(isc::util::InputBuffer& buffer, size_t rdata_len) : TXT_LIKE(buffer, rdata_len) {} + +/// TXT(const TXT& other); +inline TXT::TXT(const TXT& other) : TXT_LIKE(other) {} + +/// virtual std::string toText() const; +inline std::string TXT::toText() const +{ + return TXT_LIKE::toText(); +} + +/// virtual void toWire(isc::util::OutputBuffer& buffer) const; +inline void TXT::toWire(isc::util::OutputBuffer& buffer) const +{ + TXT_LIKE::toWire(buffer); +} + +/// virtual void toWire(AbstractMessageRenderer& renderer) const; +inline void TXT::toWire(AbstractMessageRenderer& renderer) const +{ + TXT_LIKE::toWire(renderer); +} + +/// virtual int compare(const Rdata& other) const; +inline int TXT::compare(const Rdata& other) const +{ + return TXT_LIKE::compare(other); +} + // END_RDATA_NAMESPACE // END_ISC_NAMESPACE // END_HEADER_GUARD From c8710633f9cad97adc038852319f1a7a22cebc44 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Fri, 29 Jul 2011 14:20:59 +0800 Subject: [PATCH 286/974] [trac1128] Add more comments, avoid including useless header, and some minor fixes. --- src/lib/dns/rdata/generic/srv_33.cc | 172 ------------ src/lib/dns/rdata/in_1/srv_33.cc | 271 +++++++++++++++++++ src/lib/dns/rdata/{generic => in_1}/srv_33.h | 42 ++- src/lib/dns/tests/rdata_srv_unittest.cc | 40 +-- 4 files changed, 330 insertions(+), 195 deletions(-) delete mode 100644 src/lib/dns/rdata/generic/srv_33.cc create mode 100644 src/lib/dns/rdata/in_1/srv_33.cc rename src/lib/dns/rdata/{generic => in_1}/srv_33.h (54%) diff --git a/src/lib/dns/rdata/generic/srv_33.cc b/src/lib/dns/rdata/generic/srv_33.cc deleted file mode 100644 index fd6b694d6c..0000000000 --- a/src/lib/dns/rdata/generic/srv_33.cc +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC 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. - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include - -using namespace std; -using namespace isc::util; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -struct SRVImpl { - // straightforward representation of SRV RDATA fields - SRVImpl(uint16_t priority, uint16_t weight, uint16_t port, - const Name& target) : - priority_(priority), weight_(weight), port_(port), - target_(target) - {} - - uint16_t priority_; - uint16_t weight_; - uint16_t port_; - Name target_; -}; - -SRV::SRV(const string& srv_str) : - impl_(NULL) -{ - istringstream iss(srv_str); - string targetname; - unsigned int priority, weight, port; - - iss >> priority >> weight >> port >> targetname; - if (iss.bad() || iss.fail()) { - isc_throw(InvalidRdataText, "Invalid SRV text"); - } - if (priority > 0xffff) { - isc_throw(InvalidRdataText, "SRV priority out of range"); - } - if (weight > 0xffff) { - isc_throw(InvalidRdataText, "SRV weight out of range"); - } - if (port > 0xffff) { - isc_throw(InvalidRdataText, "SRV port out of range"); - } - - impl_ = new SRVImpl(priority, weight, port, Name(targetname)); -} - -SRV::SRV(InputBuffer& buffer, size_t rdata_len) { - if (rdata_len < 6) { - isc_throw(InvalidRdataLength, "SRV too short"); - } - - uint16_t priority = buffer.readUint16(); - uint16_t weight = buffer.readUint16(); - uint16_t port = buffer.readUint16(); - const Name targetname(buffer); - - impl_ = new SRVImpl(priority, weight, port, targetname); -} - -SRV::SRV(const SRV& source) : - Rdata(), impl_(new SRVImpl(*source.impl_)) -{} - -SRV& -SRV::operator=(const SRV& source) { - if (impl_ == source.impl_) { - return (*this); - } - - SRVImpl* newimpl = new SRVImpl(*source.impl_); - delete impl_; - impl_ = newimpl; - - return (*this); -} - -SRV::~SRV() { - delete impl_; -} - -string -SRV::toText() const { - using namespace boost; - return (lexical_cast(static_cast(impl_->priority_)) + - " " + lexical_cast(static_cast(impl_->weight_)) + - " " + lexical_cast(static_cast(impl_->port_)) + - " " + impl_->target_.toText()); -} - -void -SRV::toWire(OutputBuffer& buffer) const { - buffer.writeUint16(impl_->priority_); - buffer.writeUint16(impl_->weight_); - buffer.writeUint16(impl_->port_); - impl_->target_.toWire(buffer); -} - -void -SRV::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeUint16(impl_->priority_); - renderer.writeUint16(impl_->weight_); - renderer.writeUint16(impl_->port_); - // According to RFC 2782, name compression is not - // to be used for this field. - renderer.writeName(impl_->target_, false); -} - -int -SRV::compare(const Rdata& other) const { - const SRV& other_srv = dynamic_cast(other); - - if (impl_->priority_ != other_srv.impl_->priority_) { - return (impl_->priority_ < other_srv.impl_->priority_ ? -1 : 1); - } - if (impl_->weight_ != other_srv.impl_->weight_) { - return (impl_->weight_ < other_srv.impl_->weight_ ? -1 : 1); - } - if (impl_->port_ != other_srv.impl_->port_) { - return (impl_->port_ < other_srv.impl_->port_ ? -1 : 1); - } - - return (compareNames(impl_->target_, other_srv.impl_->target_)); -} - -uint16_t -SRV::getPriority() const { - return (impl_->priority_); -} - -uint16_t -SRV::getWeight() const { - return (impl_->weight_); -} - -uint16_t -SRV::getPort() const { - return (impl_->port_); -} - -const Name& -SRV::getTarget() const { - return (impl_->target_); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/srv_33.cc b/src/lib/dns/rdata/in_1/srv_33.cc new file mode 100644 index 0000000000..df3ac1b61b --- /dev/null +++ b/src/lib/dns/rdata/in_1/srv_33.cc @@ -0,0 +1,271 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct SRVImpl { + // straightforward representation of SRV RDATA fields + SRVImpl(uint16_t priority, uint16_t weight, uint16_t port, + const Name& target) : + priority_(priority), weight_(weight), port_(port), + target_(target) + {} + + uint16_t priority_; + uint16_t weight_; + uint16_t port_; + Name target_; +}; + +namespace { +string +getToken(istringstream& iss, const string& full_input) { + string token; + iss >> token; + if (iss.bad() || iss.fail()) { + isc_throw(InvalidRdataText, "Invalid SRV text: parse error " << + full_input); + } + return (token); +} + +// This helper function converts a string token to an *unsigned* integer. +// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently +// wide to store resulting integers. +template +NumType +tokenToNum(const string& num_token) { + NumType num; + try { + num = lexical_cast(num_token); + } catch (const boost::bad_lexical_cast& ex) { + isc_throw(InvalidRdataText, "Invalid SRV numeric parameter: " << + num_token); + } + if (num < 0 || num >= (static_cast(1) << BitSize)) { + isc_throw(InvalidRdataText, "Numeric SRV parameter out of range: " << + num); + } + return (num); +} +} + +/// \brief Constructor from string. +/// +/// \c srv_str must be formatted as follows: +/// \code +/// \endcode +/// where +/// - , , and are an unsigned 16-bit decimal +/// integer. +/// - is a valid textual representation of domain name. +/// +/// An example of valid string is: +/// \code "1 5 1500 example.com." \endcode +/// +/// Exceptions +/// +/// If is not a valid domain name, a corresponding exception from +/// the \c Name class will be thrown; +/// if %any of the other bullet points above is not met, an exception of +/// class \c InvalidRdataText will be thrown. +/// This constructor internally involves resource allocation, and if it fails +/// a corresponding standard exception will be thrown. +SRV::SRV(const string& srv_str) : + impl_(NULL) +{ + istringstream iss(srv_str); + + const int32_t priority = tokenToNum(getToken(iss, srv_str)); + const int32_t weight = tokenToNum(getToken(iss, srv_str)); + const int32_t port = tokenToNum(getToken(iss, srv_str)); + const Name targetname(getToken(iss, srv_str)); + + if (!iss.eof()) { + isc_throw(InvalidRdataText, "Unexpected input for SRV RDATA: " << + srv_str); + } + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not begin with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC2782, the Target field must be a non compressed form +/// of domain name. But this implementation accepts a %SRV RR even if that +/// field is compressed. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes, normally expected +/// to be the value of the RDLENGTH field of the corresponding RR. +SRV::SRV(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 6) { + isc_throw(InvalidRdataLength, "SRV too short"); + } + + uint16_t priority = buffer.readUint16(); + uint16_t weight = buffer.readUint16(); + uint16_t port = buffer.readUint16(); + const Name targetname(buffer); + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +SRV::SRV(const SRV& source) : + Rdata(), impl_(new SRVImpl(*source.impl_)) +{} + +SRV& +SRV::operator=(const SRV& source) { + if (impl_ == source.impl_) { + return (*this); + } + + SRVImpl* newimpl = new SRVImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SRV::~SRV() { + delete impl_; +} + +/// \brief Convert the \c SRV to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c SRV(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c SRV object. +string +SRV::toText() const { + using namespace boost; + return (lexical_cast(impl_->priority_) + + " " + lexical_cast(impl_->weight_) + + " " + lexical_cast(impl_->port_) + + " " + impl_->target_.toText()); +} + +/// \brief Render the \c SRV in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +SRV::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->priority_); + buffer.writeUint16(impl_->weight_); + buffer.writeUint16(impl_->port_); + impl_->target_.toWire(buffer); +} + +/// \brief Render the \c SRV in the wire format with taking into account +/// compression. +/// +/// As specified in RFC2782, the Target field (a domain name) will not be +/// compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +SRV::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->priority_); + renderer.writeUint16(impl_->weight_); + renderer.writeUint16(impl_->port_); + renderer.writeName(impl_->target_, false); +} + +/// \brief Compare two instances of \c SRV RDATA. +/// +/// See documentation in \c Rdata. +int +SRV::compare(const Rdata& other) const { + const SRV& other_srv = dynamic_cast(other); + + if (impl_->priority_ != other_srv.impl_->priority_) { + return (impl_->priority_ < other_srv.impl_->priority_ ? -1 : 1); + } + if (impl_->weight_ != other_srv.impl_->weight_) { + return (impl_->weight_ < other_srv.impl_->weight_ ? -1 : 1); + } + if (impl_->port_ != other_srv.impl_->port_) { + return (impl_->port_ < other_srv.impl_->port_ ? -1 : 1); + } + + return (compareNames(impl_->target_, other_srv.impl_->target_)); +} + +uint16_t +SRV::getPriority() const { + return (impl_->priority_); +} + +uint16_t +SRV::getWeight() const { + return (impl_->weight_); +} + +uint16_t +SRV::getPort() const { + return (impl_->port_); +} + +const Name& +SRV::getTarget() const { + return (impl_->target_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/srv_33.h b/src/lib/dns/rdata/in_1/srv_33.h similarity index 54% rename from src/lib/dns/rdata/generic/srv_33.h rename to src/lib/dns/rdata/in_1/srv_33.h index 8a060d6b10..aa223186fb 100644 --- a/src/lib/dns/rdata/generic/srv_33.h +++ b/src/lib/dns/rdata/in_1/srv_33.h @@ -14,11 +14,7 @@ #include -#include - #include -#include -#include #include // BEGIN_HEADER_GUARD @@ -30,21 +26,59 @@ // BEGIN_RDATA_NAMESPACE +/// \brief \c rdata::SRV class represents the SRV RDATA as defined %in +/// RFC2782. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// SRV RDATA. + struct SRVImpl; class SRV : public Rdata { public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. SRV& operator=(const SRV& source); + + /// \brief The destructor. ~SRV(); /// /// Specialized methods /// + + /// \brief Return the value of the priority field. + /// + /// This method never throws an exception. uint16_t getPriority() const; + + /// \brief Return the value of the weight field. + /// + /// This method never throws an exception. uint16_t getWeight() const; + + /// \brief Return the value of the port field. + /// + /// This method never throws an exception. uint16_t getPort() const; + + /// \brief Return the value of the target field. + /// + /// \return A reference to a \c Name class object corresponding to the + /// internal target name. + /// + /// This method never throws an exception. const Name& getTarget() const; private: diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc index 0cb7f27ab1..3394f43aef 100644 --- a/src/lib/dns/tests/rdata_srv_unittest.cc +++ b/src/lib/dns/tests/rdata_srv_unittest.cc @@ -50,8 +50,8 @@ const uint8_t wiredata_srv2[] = { 0x00, 0x01, 0x00, 0x05, 0x05, 0x78, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; -const generic::SRV rdata_srv(srv_txt); -const generic::SRV rdata_srv2(srv_txt2); +const in::SRV rdata_srv(srv_txt); +const in::SRV rdata_srv2(srv_txt2); TEST_F(Rdata_SRV_Test, createFromText) { EXPECT_EQ(1, rdata_srv.getPriority()); @@ -62,30 +62,32 @@ TEST_F(Rdata_SRV_Test, createFromText) { TEST_F(Rdata_SRV_Test, badText) { // priority is too large (2814...6 is 2^48) - EXPECT_THROW(generic::SRV("281474976710656 5 1500 a.example.com."), + EXPECT_THROW(in::SRV("281474976710656 5 1500 a.example.com."), InvalidRdataText); // weight is too large - EXPECT_THROW(generic::SRV("1 281474976710656 1500 a.example.com."), + EXPECT_THROW(in::SRV("1 281474976710656 1500 a.example.com."), InvalidRdataText); // port is too large - EXPECT_THROW(generic::SRV("1 5 281474976710656 a.example.com."), + EXPECT_THROW(in::SRV("1 5 281474976710656 a.example.com."), InvalidRdataText); // incomplete text - EXPECT_THROW(generic::SRV("1 5 a.example.com."), + EXPECT_THROW(in::SRV("1 5 a.example.com."), + InvalidRdataText); + EXPECT_THROW(in::SRV("1 5 1500a.example.com."), InvalidRdataText); // bad name - EXPECT_THROW(generic::SRV("1 5 1500 a.example.com." + too_long_label), + EXPECT_THROW(in::SRV("1 5 1500 a.example.com." + too_long_label), TooLongLabel); } TEST_F(Rdata_SRV_Test, assignment) { - generic::SRV copy((string(srv_txt2))); + in::SRV copy((string(srv_txt2))); copy = rdata_srv; EXPECT_EQ(0, copy.compare(rdata_srv)); // Check if the copied data is valid even after the original is deleted - generic::SRV* copy2 = new generic::SRV(rdata_srv); - generic::SRV copy3((string(srv_txt2))); + in::SRV* copy2 = new in::SRV(rdata_srv); + in::SRV copy3((string(srv_txt2))); copy3 = *copy2; delete copy2; EXPECT_EQ(0, copy3.compare(rdata_srv)); @@ -148,18 +150,18 @@ TEST_F(Rdata_SRV_Test, toText) { TEST_F(Rdata_SRV_Test, compare) { // test RDATAs, sorted in the ascendent order. - vector compare_set; - compare_set.push_back(generic::SRV("1 5 1500 a.example.com.")); - compare_set.push_back(generic::SRV("2 5 1500 a.example.com.")); - compare_set.push_back(generic::SRV("2 6 1500 a.example.com.")); - compare_set.push_back(generic::SRV("2 6 1600 a.example.com.")); - compare_set.push_back(generic::SRV("2 6 1600 example.com.")); + vector compare_set; + compare_set.push_back(in::SRV("1 5 1500 a.example.com.")); + compare_set.push_back(in::SRV("2 5 1500 a.example.com.")); + compare_set.push_back(in::SRV("2 6 1500 a.example.com.")); + compare_set.push_back(in::SRV("2 6 1600 a.example.com.")); + compare_set.push_back(in::SRV("2 6 1600 example.com.")); EXPECT_EQ(0, compare_set[0].compare( - generic::SRV("1 5 1500 a.example.com."))); + in::SRV("1 5 1500 a.example.com."))); - vector::const_iterator it; - vector::const_iterator it_end = compare_set.end(); + vector::const_iterator it; + vector::const_iterator it_end = compare_set.end(); for (it = compare_set.begin(); it != it_end - 1; ++it) { EXPECT_GT(0, (*it).compare(*(it + 1))); EXPECT_LT(0, (*(it + 1)).compare(*it)); From d9d0d1f6cb6c6210f293dcf5c181024d2df787f6 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Fri, 29 Jul 2011 14:39:28 +0800 Subject: [PATCH 287/974] [trac1128] fix comments --- src/lib/dns/rdata/in_1/srv_33.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/rdata/in_1/srv_33.cc b/src/lib/dns/rdata/in_1/srv_33.cc index df3ac1b61b..09808b685b 100644 --- a/src/lib/dns/rdata/in_1/srv_33.cc +++ b/src/lib/dns/rdata/in_1/srv_33.cc @@ -121,7 +121,7 @@ SRV::SRV(const string& srv_str) : /// When a read operation on \c buffer fails (e.g., due to a corrupted /// message) a corresponding exception from the \c InputBuffer class will /// be thrown. -/// If the wire-format data does not begin with a valid domain name, +/// If the wire-format data does not end with a valid domain name, /// a corresponding exception from the \c Name class will be thrown. /// In addition, this constructor internally involves resource allocation, /// and if it fails a corresponding standard exception will be thrown. From e89a3a1302cd3e95403c5c64edb126153852ff35 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Fri, 29 Jul 2011 17:32:26 +0800 Subject: [PATCH 288/974] [trac1130] Add naptr rrtype files --- src/lib/dns/rdata/in_1/naptr_35.cc | 63 ++++++++++++++++++++ src/lib/dns/rdata/in_1/naptr_35.h | 43 +++++++++++++ src/lib/dns/tests/Makefile.am | 1 + src/lib/dns/tests/rdata_in_naptr_unittest.cc | 47 +++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 src/lib/dns/rdata/in_1/naptr_35.cc create mode 100644 src/lib/dns/rdata/in_1/naptr_35.h create mode 100644 src/lib/dns/tests/rdata_in_naptr_unittest.cc diff --git a/src/lib/dns/rdata/in_1/naptr_35.cc b/src/lib/dns/rdata/in_1/naptr_35.cc new file mode 100644 index 0000000000..cd58e8da8a --- /dev/null +++ b/src/lib/dns/rdata/in_1/naptr_35.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +NAPTR::NAPTR(InputBuffer& buffer, size_t len) { +} + +NAPTR::NAPTR(const std::string& naptr_str) { +} + +NAPTR::NAPTR(const NAPTR& naptr) { +} + +void +NAPTR::toWire(OutputBuffer& buffer) const { +} + +void +NAPTR::toWire(AbstractMessageRenderer& renderer) const { +} + +string +NAPTR::toText() const { +} + +int +NAPTR::compare(const Rdata& other) const { + return 0; +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/naptr_35.h b/src/lib/dns/rdata/in_1/naptr_35.h new file mode 100644 index 0000000000..be5e22df2e --- /dev/null +++ b/src/lib/dns/rdata/in_1/naptr_35.h @@ -0,0 +1,43 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_HEADER_GUARD + +#include + +#include + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class NAPTR : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + // NAPTR specific methods +private: +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index 3a249c1768..dbb0a9e337 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -42,6 +42,7 @@ run_unittests_SOURCES += rdata_nsec3param_unittest.cc run_unittests_SOURCES += rdata_rrsig_unittest.cc run_unittests_SOURCES += rdata_rp_unittest.cc run_unittests_SOURCES += rdata_tsig_unittest.cc +run_unittests_SOURCES += rdata_in_naptr_unittest.cc run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc run_unittests_SOURCES += question_unittest.cc run_unittests_SOURCES += rrparamregistry_unittest.cc diff --git a/src/lib/dns/tests/rdata_in_naptr_unittest.cc b/src/lib/dns/tests/rdata_in_naptr_unittest.cc new file mode 100644 index 0000000000..0f5577b375 --- /dev/null +++ b/src/lib/dns/tests/rdata_in_naptr_unittest.cc @@ -0,0 +1,47 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using isc::UnitTestUtil; +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { +class Rdata_IN_NAPTR_Test : public RdataTest { +}; + +// 10 100 "S" "SIP+D2U" "" _sip._udp.example.com. +static uint8_t naptr_rdata[] = {0x00,0x0a,0x00,0x64,0x01,0x53,0x07,0x53,0x49,0x50,0x2b,0x44,0x32,0x55,0x00,0x04,0x5f,0x73,0x69,0x70, + 0x04,0x5f,0x75,0x64,0x70,0x07,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x03,0x63,0x6f,0x6d,0x00}; + +static char *naptr_str = "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; + +TEST_F(Rdata_IN_NAPTR_Test, createFromText) { +} + +} From 25b02eeaa9acda461629d19c4c6c2b20b5850795 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 1 Aug 2011 09:31:12 +0200 Subject: [PATCH 289/974] [trac1128] fix EXTRA_DIST --- src/lib/dns/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index e28bbaef5e..4a0173cb17 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -51,14 +51,14 @@ EXTRA_DIST += rdata/generic/soa_6.cc EXTRA_DIST += rdata/generic/soa_6.h EXTRA_DIST += rdata/generic/txt_16.cc EXTRA_DIST += rdata/generic/txt_16.h -EXTRA_DIST += rdata/generic/srv_33.cc -EXTRA_DIST += rdata/generic/srv_33.h EXTRA_DIST += rdata/hs_4/a_1.cc EXTRA_DIST += rdata/hs_4/a_1.h EXTRA_DIST += rdata/in_1/a_1.cc EXTRA_DIST += rdata/in_1/a_1.h EXTRA_DIST += rdata/in_1/aaaa_28.cc EXTRA_DIST += rdata/in_1/aaaa_28.h +EXTRA_DIST += rdata/in_1/srv_33.cc +EXTRA_DIST += rdata/in_1/srv_33.h #EXTRA_DIST += rdata/template.cc #EXTRA_DIST += rdata/template.h From 579fd2bf848e994ed6dcd8d1c3633f2fa62cbd28 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 28 Jul 2011 13:47:17 +0200 Subject: [PATCH 290/974] [trac1061] Interface of the database connection and client It will look something like this, hopefully. Let's see if it works. --- src/lib/datasrc/Makefile.am | 1 + src/lib/datasrc/client.h | 2 ++ src/lib/datasrc/database.cc | 21 +++++++++++++++++++ src/lib/datasrc/database.h | 40 +++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 src/lib/datasrc/database.cc create mode 100644 src/lib/datasrc/database.h diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index 261baaeb0b..eecd26a9b6 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -22,6 +22,7 @@ libdatasrc_la_SOURCES += zone.h libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc libdatasrc_la_SOURCES += client.h +libdatasrc_la_SOURCES += database.h database.cc nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index a830f00c21..9fe6519532 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -15,6 +15,8 @@ #ifndef __DATA_SOURCE_CLIENT_H #define __DATA_SOURCE_CLIENT_H 1 +#include + #include namespace isc { diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc new file mode 100644 index 0000000000..71014d2cea --- /dev/null +++ b/src/lib/datasrc/database.cc @@ -0,0 +1,21 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +namespace isc { +namespace datasrc { + +} +} diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h new file mode 100644 index 0000000000..b2fe081c26 --- /dev/null +++ b/src/lib/datasrc/database.h @@ -0,0 +1,40 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __DATABASE_DATASRC_H +#define __DATABASE_DATASRC_H + +#include + +namespace isc { +namespace datasrc { + +class DatabaseConnection : boost::noncopyable { +public: + ~ DatabaseConnection() { } + virtual std::pair getZone() const; +}; + +class DatabaseClient : public DataSourceClient { +public: + DatabaseClient(const std::auto_ptr& connection); + virtual FindResult findZone(const isc::dns::Name& name) const; +private: + const std::auto_ptr connection_; +}; + +} +} + +#endif From 63f4617b5ab99d75e98e40760ff68bb1615a84e6 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 28 Jul 2011 15:42:45 +0200 Subject: [PATCH 291/974] [trac1061] Doxygen comments for database classes --- src/lib/datasrc/database.h | 138 ++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index b2fe081c26..e949ec3cb7 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -20,17 +20,151 @@ namespace isc { namespace datasrc { +/** + * \brief Abstract connection to database with DNS data + * + * This class is defines interface to databases. Each supported database + * will provide methods for accessing the data stored there in a generic + * manner. The methods are meant to be low-level, without much or any knowledge + * about DNS and should be possible to translate directly to queries. + * + * On the other hand, how the communication with database is done and in what + * schema (in case of relational/SQL database) is up to the concrete classes. + * + * This class is non-copyable, as copying connections to database makes little + * sense and will not be needed. + * + * \todo Is it true this does not need to be copied? For example the zone + * iterator might need it's own copy. But a virtual clone() method might + * be better for that than copy constructor. + * + * \note The same application may create multiple connections to the same + * database. If the database allows having multiple open queries at one + * connection, the connection class may share it. + */ class DatabaseConnection : boost::noncopyable { public: - ~ DatabaseConnection() { } - virtual std::pair getZone() const; + /** + * \brief Destructor + * + * It is empty, but needs a virtual one, since we will use the derived + * classes in polymorphic way. + */ + virtual ~ DatabaseConnection() { } + /** + * \brief Retrieve a zone identifier + * + * This method looks up a zone for the given name in the database. It + * should match only exact zone name (eg. name is equal to the zone's + * apex), as the DatabaseClient will loop trough the labels itself and + * find the most suitable zone. + * + * It is not specified if and what implementation of this method may throw, + * so code should expect anything. + * + * \param name The name of the zone's apex to be looked up. + * \return The first part of the result indicates if a matching zone + * was found. In case it was, the second part is internal zone ID. + * This one will be passed to methods finding data in the zone. + * It is not required to keep them, in which case whatever might + * be returned - the ID is only passed back to the connection as + * an opaque handle. + */ + virtual std::pair getZone(const isc::dns::Name& name) const; }; +/** + * \brief Concrete data source client oriented at database backends. + * + * This class (together with corresponding versions of ZoneFinder, + * ZoneIterator, etc.) translates high-level data source queries to + * low-level calls on DatabaseConnection. It calls multiple queries + * if necessary and validates data from the database, allowing the + * DatabaseConnection to be just simple translation to SQL/other + * queries to database. + * + * While it is possible to subclass it for specific database in case + * of special needs, it is not expected to be needed. This should just + * work as it is with whatever DatabaseConnection. + */ class DatabaseClient : public DataSourceClient { public: + /** + * \brief Constructor + * + * It initializes the client with a connection. + * + * It throws isc::InvalidParameter if connection is NULL. It might throw + * standard allocation exception as well, but doesn't throw anything else. + * + * \note Some objects returned from methods of this class (like ZoneFinder) + * hold references to the connection. As the lifetime of the connection + * is bound to this object, the returned objects must not be used after + * descruction of the DatabaseClient. + * + * \todo Should we use shared_ptr instead? On one side, we would get rid of + * the restriction and maybe could easy up some shutdown scenarios with + * multi-threaded applications, on the other hand it is more expensive + * and looks generally unneeded. + * + * \param connection The connection to use to get data. As the parameter + * suggests, the client takes ownership of the connection and will + * delete it when itself deleted. + */ DatabaseClient(const std::auto_ptr& connection); + /** + * \brief Corresponding ZoneFinder implementation + * + * The zone finder implementation for database data sources. Similarly + * to the DatabaseClient, it translates the queries to methods of the + * connection. + * + * Application should not come directly in contact with this class + * (it should handle it trough generic ZoneFinder pointer), therefore + * it could be completely hidden in the .cc file. But it is provided + * to allow testing and for rare cases when a database needs slightly + * different handling, so it can be subclassed. + * + * Methods directly corresponds to the ones in ZoneFinder. + */ + class Finder : public ZoneFinder { + public: + /** + * \brief Constructor + * + * \param connection The connection (shared with DatabaseClient) to + * be used for queries (the one asked for ID before). + * \param zone_id The zone ID which was returned from + * DatabaseConnection::getZone and which will be passed to further + * calls to the connection. + */ + Finder(DatabaseConnection& connection, int zone_id); + virtual const isc::dns::Name& getOrigin() const; + virtual const isc::dns::RRClass& getClass() const; + virtual FindResult find(const isc::dns::Name& name, + const isc::dns::RRType& type, + isc::dns::RRsetList* target = NULL, + const FindOptions options = FIND_DEFAULT) + const = 0; + private: + DatabaseConnection& connection_; + const int zone_id_; + }; + /** + * \brief Find a zone in the database + * + * This queries connection's getZone to find the best matching zone. + * It will propagate whatever exceptions are thrown from that method + * (which is not restricted in any way). + * + * \param name Name of the zone or data contained there. + * \return Result containing the code and instance of Finder, if anything + * is found. Applications should not rely on the specific class being + * returned, though. + */ virtual FindResult findZone(const isc::dns::Name& name) const; private: + /// \brief Our connection. const std::auto_ptr connection_; }; From f773f9ac21221663bd093806374cab83abd2288d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 1 Aug 2011 10:50:12 +0200 Subject: [PATCH 292/974] [trac1160] move getToken and numToToken to util --- src/lib/dns/rdata/any_255/tsig_250.cc | 124 +++++++++---------------- src/lib/dns/rdata/in_1/srv_33.cc | 57 +++--------- src/lib/util/strutil.cc | 11 +++ src/lib/util/strutil.h | 62 +++++++++++++ src/lib/util/tests/strutil_unittest.cc | 62 +++++++++++++ 5 files changed, 196 insertions(+), 120 deletions(-) diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc index 2557965c47..aeb1b3b847 100644 --- a/src/lib/dns/rdata/any_255/tsig_250.cc +++ b/src/lib/dns/rdata/any_255/tsig_250.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -30,6 +31,7 @@ using namespace std; using namespace boost; using namespace isc::util; using namespace isc::util::encode; +using namespace isc::util::str; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE @@ -65,45 +67,6 @@ struct TSIG::TSIGImpl { const vector other_data_; }; -namespace { -string -getToken(istringstream& iss, const string& full_input) { - string token; - iss >> token; - if (iss.bad() || iss.fail()) { - isc_throw(InvalidRdataText, "Invalid TSIG text: parse error " << - full_input); - } - return (token); -} - -// This helper function converts a string token to an *unsigned* integer. -// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently -// wide to store resulting integers. -// BitSize is the maximum number of bits that the resulting integer can take. -// This function first checks whether the given token can be converted to -// an integer of NumType type. It then confirms the conversion result is -// within the valid range, i.e., [0, 2^NumType - 1]. The second check is -// necessary because lexical_cast where T is an unsigned integer type -// doesn't correctly reject negative numbers when compiled with SunStudio. -template -NumType -tokenToNum(const string& num_token) { - NumType num; - try { - num = lexical_cast(num_token); - } catch (const boost::bad_lexical_cast& ex) { - isc_throw(InvalidRdataText, "Invalid TSIG numeric parameter: " << - num_token); - } - if (num < 0 || num >= (static_cast(1) << BitSize)) { - isc_throw(InvalidRdataText, "Numeric TSIG parameter out of range: " << - num); - } - return (num); -} -} - /// \brief Constructor from string. /// /// \c tsig_str must be formatted as follows: @@ -148,47 +111,52 @@ tokenToNum(const string& num_token) { TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) { istringstream iss(tsig_str); - const Name algorithm(getToken(iss, tsig_str)); - const int64_t time_signed = tokenToNum(getToken(iss, - tsig_str)); - const int32_t fudge = tokenToNum(getToken(iss, tsig_str)); - const int32_t macsize = tokenToNum(getToken(iss, tsig_str)); + try { + const Name algorithm(getToken(iss)); + const int64_t time_signed = tokenToNum(getToken(iss)); + const int32_t fudge = tokenToNum(getToken(iss)); + const int32_t macsize = tokenToNum(getToken(iss)); - const string mac_txt = (macsize > 0) ? getToken(iss, tsig_str) : ""; - vector mac; - decodeBase64(mac_txt, mac); - if (mac.size() != macsize) { - isc_throw(InvalidRdataText, "TSIG MAC size and data are inconsistent"); + const string mac_txt = (macsize > 0) ? getToken(iss) : ""; + vector mac; + decodeBase64(mac_txt, mac); + if (mac.size() != macsize) { + isc_throw(InvalidRdataText, "TSIG MAC size and data are inconsistent"); + } + + const int32_t orig_id = tokenToNum(getToken(iss)); + + const string error_txt = getToken(iss); + int32_t error = 0; + // XXX: In the initial implementation we hardcode the mnemonics. + // We'll soon generalize this. + if (error_txt == "BADSIG") { + error = 16; + } else if (error_txt == "BADKEY") { + error = 17; + } else if (error_txt == "BADTIME") { + error = 18; + } else { + error = tokenToNum(error_txt); + } + + const int32_t otherlen = tokenToNum(getToken(iss)); + const string otherdata_txt = (otherlen > 0) ? getToken(iss) : ""; + vector other_data; + decodeBase64(otherdata_txt, other_data); + + if (!iss.eof()) { + isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " << + tsig_str); + } + + impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id, + error, other_data); + + } catch (StringTokenError ste) { + isc_throw(InvalidRdataText, "Invalid TSIG text: " << ste.what() << + ": " << tsig_str); } - - const int32_t orig_id = tokenToNum(getToken(iss, tsig_str)); - - const string error_txt = getToken(iss, tsig_str); - int32_t error = 0; - // XXX: In the initial implementation we hardcode the mnemonics. - // We'll soon generalize this. - if (error_txt == "BADSIG") { - error = 16; - } else if (error_txt == "BADKEY") { - error = 17; - } else if (error_txt == "BADTIME") { - error = 18; - } else { - error = tokenToNum(error_txt); - } - - const int32_t otherlen = tokenToNum(getToken(iss, tsig_str)); - const string otherdata_txt = (otherlen > 0) ? getToken(iss, tsig_str) : ""; - vector other_data; - decodeBase64(otherdata_txt, other_data); - - if (!iss.eof()) { - isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " << - tsig_str); - } - - impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id, - error, other_data); } /// \brief Constructor from wire-format data. diff --git a/src/lib/dns/rdata/in_1/srv_33.cc b/src/lib/dns/rdata/in_1/srv_33.cc index 09808b685b..84f86617ee 100644 --- a/src/lib/dns/rdata/in_1/srv_33.cc +++ b/src/lib/dns/rdata/in_1/srv_33.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -44,39 +45,6 @@ struct SRVImpl { Name target_; }; -namespace { -string -getToken(istringstream& iss, const string& full_input) { - string token; - iss >> token; - if (iss.bad() || iss.fail()) { - isc_throw(InvalidRdataText, "Invalid SRV text: parse error " << - full_input); - } - return (token); -} - -// This helper function converts a string token to an *unsigned* integer. -// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently -// wide to store resulting integers. -template -NumType -tokenToNum(const string& num_token) { - NumType num; - try { - num = lexical_cast(num_token); - } catch (const boost::bad_lexical_cast& ex) { - isc_throw(InvalidRdataText, "Invalid SRV numeric parameter: " << - num_token); - } - if (num < 0 || num >= (static_cast(1) << BitSize)) { - isc_throw(InvalidRdataText, "Numeric SRV parameter out of range: " << - num); - } - return (num); -} -} - /// \brief Constructor from string. /// /// \c srv_str must be formatted as follows: @@ -103,17 +71,22 @@ SRV::SRV(const string& srv_str) : { istringstream iss(srv_str); - const int32_t priority = tokenToNum(getToken(iss, srv_str)); - const int32_t weight = tokenToNum(getToken(iss, srv_str)); - const int32_t port = tokenToNum(getToken(iss, srv_str)); - const Name targetname(getToken(iss, srv_str)); + try { + const int32_t priority = str::tokenToNum(str::getToken(iss)); + const int32_t weight = str::tokenToNum(str::getToken(iss)); + const int32_t port = str::tokenToNum(str::getToken(iss)); + const Name targetname(str::getToken(iss)); - if (!iss.eof()) { - isc_throw(InvalidRdataText, "Unexpected input for SRV RDATA: " << - srv_str); + if (!iss.eof()) { + isc_throw(InvalidRdataText, "Unexpected input for SRV RDATA: " << + srv_str); + } + + impl_ = new SRVImpl(priority, weight, port, targetname); + } catch (str::StringTokenError ste) { + isc_throw(InvalidRdataText, "Invalid SRV text: " << + ste.what() << ": " << srv_str); } - - impl_ = new SRVImpl(priority, weight, port, targetname); } /// \brief Constructor from wire-format data. diff --git a/src/lib/util/strutil.cc b/src/lib/util/strutil.cc index 161f9acd65..ed7fc9b171 100644 --- a/src/lib/util/strutil.cc +++ b/src/lib/util/strutil.cc @@ -132,6 +132,17 @@ format(const std::string& format, const std::vector& args) { return (result); } +std::string +getToken(std::istringstream& iss) { + string token; + iss >> token; + if (iss.bad() || iss.fail()) { + isc_throw(StringTokenError, "could not read token from string"); + } + return (token); +} + + } // namespace str } // namespace util } // namespace isc diff --git a/src/lib/util/strutil.h b/src/lib/util/strutil.h index e044c15ff5..1cb7bdd064 100644 --- a/src/lib/util/strutil.h +++ b/src/lib/util/strutil.h @@ -18,7 +18,10 @@ #include #include #include +#include #include +#include +#include namespace isc { namespace util { @@ -26,6 +29,16 @@ namespace str { /// \brief A Set of C++ Utilities for Manipulating Strings +/// +/// \brief A standard string util exception that is thrown if getToken or +/// numToToken are called with bad input data +/// +class StringTokenError : public Exception { +public: + StringTokenError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + /// \brief Normalize Backslash /// /// Only relevant to Windows, this replaces all "\" in a string with "/" and @@ -140,6 +153,55 @@ std::string format(const std::string& format, const std::vector& args); +/// \brief Returns one token from the given stringstream +/// +/// Using the >> operator, with basic error checking +/// +/// \exception StringTokenError if the token cannot be read from the stream +/// +/// \param iss stringstream to read one token from +/// +/// \return the first token read from the stringstream +std::string getToken(std::istringstream& iss); + +/// \brief Converts a string token to an *unsigned* integer. +/// +/// The value is converted using a lexical cast, with error and bounds +/// checking. +/// +/// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently +/// wide to store resulting integers. +/// +/// BitSize is the maximum number of bits that the resulting integer can take. +/// This function first checks whether the given token can be converted to +/// an integer of NumType type. It then confirms the conversion result is +/// within the valid range, i.e., [0, 2^NumType - 1]. The second check is +/// necessary because lexical_cast where T is an unsigned integer type +/// doesn't correctly reject negative numbers when compiled with SunStudio. +/// +/// \exception StringTokenError if the value is out of range, or if it +/// could not be converted +/// +/// \param num_token the string token to convert +/// +/// \return the converted value, of type NumType +template +NumType +tokenToNum(const std::string& num_token) { + NumType num; + try { + num = boost::lexical_cast(num_token); + } catch (const boost::bad_lexical_cast& ex) { + isc_throw(StringTokenError, "Invalid SRV numeric parameter: " << + num_token); + } + if (num < 0 || num >= (static_cast(1) << BitSize)) { + isc_throw(StringTokenError, "Numeric SRV parameter out of range: " << + num); + } + return (num); +} + } // namespace str } // namespace util } // namespace isc diff --git a/src/lib/util/tests/strutil_unittest.cc b/src/lib/util/tests/strutil_unittest.cc index cd3a9ca811..6309800b43 100644 --- a/src/lib/util/tests/strutil_unittest.cc +++ b/src/lib/util/tests/strutil_unittest.cc @@ -12,6 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + #include #include @@ -213,3 +215,63 @@ TEST_F(StringUtilTest, Formatting) { string format9 = "%s %s"; EXPECT_EQ(format9, isc::util::str::format(format9, args)); } + +TEST_F(StringUtilTest, getToken) { + string s("a b c"); + istringstream ss(s); + EXPECT_EQ("a", isc::util::str::getToken(ss)); + EXPECT_EQ("b", isc::util::str::getToken(ss)); + EXPECT_EQ("c", isc::util::str::getToken(ss)); + EXPECT_THROW(isc::util::str::getToken(ss), isc::util::str::StringTokenError); +} + +int32_t tokenToNumCall_32_16(const string& token) { + return isc::util::str::tokenToNum(token); +} + +int16_t tokenToNumCall_16_8(const string& token) { + return isc::util::str::tokenToNum(token); +} + +TEST_F(StringUtilTest, tokenToNum) { + uint32_t num32 = tokenToNumCall_32_16("0"); + EXPECT_EQ(0, num32); + num32 = tokenToNumCall_32_16("123"); + EXPECT_EQ(123, num32); + num32 = tokenToNumCall_32_16("65535"); + EXPECT_EQ(65535, num32); + + EXPECT_THROW(tokenToNumCall_32_16(""), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_32_16("a"), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_32_16("-1"), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_32_16("65536"), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_32_16("1234567890"), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_32_16("-1234567890"), + isc::util::str::StringTokenError); + + uint16_t num16 = tokenToNumCall_16_8("123"); + EXPECT_EQ(123, num16); + num16 = tokenToNumCall_16_8("0"); + EXPECT_EQ(0, num16); + num16 = tokenToNumCall_16_8("255"); + EXPECT_EQ(255, num16); + + EXPECT_THROW(tokenToNumCall_16_8(""), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_16_8("a"), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_16_8("-1"), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_16_8("256"), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_16_8("1234567890"), + isc::util::str::StringTokenError); + EXPECT_THROW(tokenToNumCall_16_8("-1234567890"), + isc::util::str::StringTokenError); + +} From e5d9f259dce621201a2c52b56b260f8de776ecc0 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 1 Aug 2011 10:53:09 +0200 Subject: [PATCH 293/974] [trac1160] fixture wasn't used in any of the tests --- src/lib/util/tests/strutil_unittest.cc | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/lib/util/tests/strutil_unittest.cc b/src/lib/util/tests/strutil_unittest.cc index 6309800b43..74bc17d314 100644 --- a/src/lib/util/tests/strutil_unittest.cc +++ b/src/lib/util/tests/strutil_unittest.cc @@ -24,17 +24,9 @@ using namespace isc; using namespace isc::util; using namespace std; -class StringUtilTest : public ::testing::Test { -protected: - StringUtilTest() - { - } -}; - - // Check for slash replacement -TEST_F(StringUtilTest, Slash) { +TEST(StringUtilTest, Slash) { string instring = ""; isc::util::str::normalizeSlash(instring); @@ -51,7 +43,7 @@ TEST_F(StringUtilTest, Slash) { // Check that leading and trailing space trimming works -TEST_F(StringUtilTest, Trim) { +TEST(StringUtilTest, Trim) { // Empty and full string. EXPECT_EQ("", isc::util::str::trim("")); @@ -73,7 +65,7 @@ TEST_F(StringUtilTest, Trim) { // returned vector; if not as expected, the following references may be invalid // so should not be used. -TEST_F(StringUtilTest, Tokens) { +TEST(StringUtilTest, Tokens) { vector result; // Default delimiters @@ -159,7 +151,7 @@ TEST_F(StringUtilTest, Tokens) { // Changing case -TEST_F(StringUtilTest, ChangeCase) { +TEST(StringUtilTest, ChangeCase) { string mixed("abcDEFghiJKLmno123[]{=+--+]}"); string upper("ABCDEFGHIJKLMNO123[]{=+--+]}"); string lower("abcdefghijklmno123[]{=+--+]}"); @@ -175,7 +167,7 @@ TEST_F(StringUtilTest, ChangeCase) { // Formatting -TEST_F(StringUtilTest, Formatting) { +TEST(StringUtilTest, Formatting) { vector args; args.push_back("arg1"); @@ -216,7 +208,7 @@ TEST_F(StringUtilTest, Formatting) { EXPECT_EQ(format9, isc::util::str::format(format9, args)); } -TEST_F(StringUtilTest, getToken) { +TEST(StringUtilTest, getToken) { string s("a b c"); istringstream ss(s); EXPECT_EQ("a", isc::util::str::getToken(ss)); @@ -233,7 +225,7 @@ int16_t tokenToNumCall_16_8(const string& token) { return isc::util::str::tokenToNum(token); } -TEST_F(StringUtilTest, tokenToNum) { +TEST(StringUtilTest, tokenToNum) { uint32_t num32 = tokenToNumCall_32_16("0"); EXPECT_EQ(0, num32); num32 = tokenToNumCall_32_16("123"); From d299036c6ac281d1d6c119c5fdbe603bed404851 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 1 Aug 2011 11:17:33 +0200 Subject: [PATCH 294/974] [trac1160] small namespace use change --- src/lib/dns/rdata/in_1/srv_33.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib/dns/rdata/in_1/srv_33.cc b/src/lib/dns/rdata/in_1/srv_33.cc index 84f86617ee..5b8c538cf3 100644 --- a/src/lib/dns/rdata/in_1/srv_33.cc +++ b/src/lib/dns/rdata/in_1/srv_33.cc @@ -27,6 +27,7 @@ using namespace std; using namespace isc::util; +using namespace isc::util::str; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE @@ -72,10 +73,10 @@ SRV::SRV(const string& srv_str) : istringstream iss(srv_str); try { - const int32_t priority = str::tokenToNum(str::getToken(iss)); - const int32_t weight = str::tokenToNum(str::getToken(iss)); - const int32_t port = str::tokenToNum(str::getToken(iss)); - const Name targetname(str::getToken(iss)); + const int32_t priority = tokenToNum(getToken(iss)); + const int32_t weight = tokenToNum(getToken(iss)); + const int32_t port = tokenToNum(getToken(iss)); + const Name targetname(getToken(iss)); if (!iss.eof()) { isc_throw(InvalidRdataText, "Unexpected input for SRV RDATA: " << @@ -83,7 +84,7 @@ SRV::SRV(const string& srv_str) : } impl_ = new SRVImpl(priority, weight, port, targetname); - } catch (str::StringTokenError ste) { + } catch (StringTokenError ste) { isc_throw(InvalidRdataText, "Invalid SRV text: " << ste.what() << ": " << srv_str); } From b5fbd9c942b1080aa60a48ee23da60574d1fc22f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 1 Aug 2011 11:47:37 +0200 Subject: [PATCH 295/974] [trac1061] Tests for finding of the zone. --- src/lib/datasrc/database.h | 20 ++++- src/lib/datasrc/tests/Makefile.am | 1 + src/lib/datasrc/tests/database_unittest.cc | 93 ++++++++++++++++++++++ 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/lib/datasrc/tests/database_unittest.cc diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index e949ec3cb7..e2ff407984 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -70,7 +70,7 @@ public: * be returned - the ID is only passed back to the connection as * an opaque handle. */ - virtual std::pair getZone(const isc::dns::Name& name) const; + virtual std::pair getZone(const isc::dns::Name& name) const = 0; }; /** @@ -146,6 +146,24 @@ public: isc::dns::RRsetList* target = NULL, const FindOptions options = FIND_DEFAULT) const = 0; + /** + * \brief The zone ID + * + * This function provides the stored zone ID as passed to the + * constructor. This is meant for testing purposes and normal + * applications shouldn't need it. + */ + int zone_id() const { return (zone_id_); } + /** + * \brief The database connection. + * + * This function provides the database connection stored inside as + * passed to the constructor. This is meant for testing purposes and + * normal applications shouldn't need it. + */ + const DatabaseConnection& connection() const { + return (connection_); + } private: DatabaseConnection& connection_; const int zone_id_; diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index fbcf9c95c0..9cfd0d8c29 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -28,6 +28,7 @@ run_unittests_SOURCES += rbtree_unittest.cc run_unittests_SOURCES += zonetable_unittest.cc run_unittests_SOURCES += memory_datasrc_unittest.cc run_unittests_SOURCES += logger_unittest.cc +run_unittests_SOURCES += database_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc new file mode 100644 index 0000000000..45e445905f --- /dev/null +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -0,0 +1,93 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include + +#include + +using namespace isc::datasrc; +using namespace std; +using namespace boost; +using isc::dns::Name; + +namespace { + +/* + * A virtual database connection that pretends it contains single zone -- + * example.org. + */ +class MockConnection : public DatabaseConnection { +public: + virtual std::pair getZone(const Name& name) const { + if (name == Name("zone.example.org")) { + return (std::pair(true, 42)); + } else { + return (std::pair(false, 0)); + } + } +}; + +class DatabaseClientTest : public ::testing::Test { +public: + DatabaseClientTest() { + createClient(); + } + /* + * We initialize the client from a function, so we can call it multiple + * times per test. + */ + void createClient() { + current_connection_ = new MockConnection(); + client_.reset(new DatabaseClient(auto_ptr( + current_connection_))); + } + // Will be deleted by client_, just keep the current value for comparison. + MockConnection* current_connection_; + auto_ptr client_; + /** + * Check the zone finder is a valid one and references the zone ID and + * connection available here. + */ + void checkZoneFinder(const DataSourceClient::FindResult& zone) { + ASSERT_NE(ZoneFinderPtr(), zone.zone_finder) << "No zone finder"; + shared_ptr finder( + dynamic_pointer_cast(zone.zone_finder)); + ASSERT_NE(shared_ptr(), finder) << + "Wrong type of finder"; + EXPECT_EQ(42, finder->zone_id()); + EXPECT_EQ(current_connection_, &finder->connection()); + } +}; + +TEST_F(DatabaseClientTest, zoneNotFound) { + DataSourceClient::FindResult zone(client_->findZone(Name("example.com"))); + EXPECT_EQ(result::NOTFOUND, zone.code); +} + +TEST_F(DatabaseClientTest, exactZone) { + DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); + EXPECT_EQ(result::SUCCESS, zone.code); + checkZoneFinder(zone); +} + +TEST_F(DatabaseClientTest, superZone) { + DataSourceClient::FindResult zone(client_->findZone(Name( + "sub.example.org"))); + EXPECT_EQ(result::PARTIALMATCH, zone.code); + checkZoneFinder(zone); +} + +} From 14a0766224d50d1c4c409e883cf29515dafc25f0 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 1 Aug 2011 12:03:35 +0200 Subject: [PATCH 296/974] [trac1061] Test for constructor exception --- src/lib/datasrc/tests/database_unittest.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 45e445905f..f18dc3e69a 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -15,6 +15,7 @@ #include #include +#include #include @@ -90,4 +91,9 @@ TEST_F(DatabaseClientTest, superZone) { checkZoneFinder(zone); } +TEST_F(DatabaseClientTest, noConnException) { + EXPECT_THROW(DatabaseClient(auto_ptr()), + isc::InvalidParameter); +} + } From b63b9aac20259f3612e23c7a3e977dcb48693ef1 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 1 Aug 2011 13:04:40 +0200 Subject: [PATCH 297/974] [trac1061] Don't return reference While it works for in-memory zone and similar, it can't be done with databases, as the database returns some primitive data and it must be created on the spot. --- src/bin/auth/tests/query_unittest.cc | 4 ++-- src/lib/datasrc/database.h | 4 ++-- src/lib/datasrc/memory_datasrc.cc | 4 ++-- src/lib/datasrc/memory_datasrc.h | 4 ++-- src/lib/datasrc/zone.h | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index 6a75856eee..9ef8c13682 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -122,8 +122,8 @@ public: masterLoad(zone_stream, origin_, rrclass_, boost::bind(&MockZoneFinder::loadRRset, this, _1)); } - virtual const isc::dns::Name& getOrigin() const { return (origin_); } - virtual const isc::dns::RRClass& getClass() const { return (rrclass_); } + virtual isc::dns::Name getOrigin() const { return (origin_); } + virtual isc::dns::RRClass getClass() const { return (rrclass_); } virtual FindResult find(const isc::dns::Name& name, const isc::dns::RRType& type, RRsetList* target = NULL, diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index e2ff407984..f3aa9f5740 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -139,8 +139,8 @@ public: * calls to the connection. */ Finder(DatabaseConnection& connection, int zone_id); - virtual const isc::dns::Name& getOrigin() const; - virtual const isc::dns::RRClass& getClass() const; + virtual isc::dns::Name getOrigin() const; + virtual isc::dns::RRClass getClass() const; virtual FindResult find(const isc::dns::Name& name, const isc::dns::RRType& type, isc::dns::RRsetList* target = NULL, diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 3d24ce0200..26223dad90 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -606,12 +606,12 @@ InMemoryZoneFinder::~InMemoryZoneFinder() { delete impl_; } -const Name& +Name InMemoryZoneFinder::getOrigin() const { return (impl_->origin_); } -const RRClass& +RRClass InMemoryZoneFinder::getClass() const { return (impl_->zone_class_); } diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 9bed9603c1..9707797299 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -58,10 +58,10 @@ public: //@} /// \brief Returns the origin of the zone. - virtual const isc::dns::Name& getOrigin() const; + virtual isc::dns::Name getOrigin() const; /// \brief Returns the class of the zone. - virtual const isc::dns::RRClass& getClass() const; + virtual isc::dns::RRClass getClass() const; /// \brief Looks up an RRset in the zone. /// diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 69785f0227..f67ed4be24 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -131,10 +131,10 @@ public: /// These methods should never throw an exception. //@{ /// Return the origin name of the zone. - virtual const isc::dns::Name& getOrigin() const = 0; + virtual isc::dns::Name getOrigin() const = 0; /// Return the RR class of the zone. - virtual const isc::dns::RRClass& getClass() const = 0; + virtual isc::dns::RRClass getClass() const = 0; //@} /// From 11b8b873e7fd6722053aa224d20f29350bf2b298 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 1 Aug 2011 13:06:13 +0200 Subject: [PATCH 298/974] [trac1061] Implement finding a zone in DB And provide some basics of the ZoneFinder, which does not work, but compiles at last. --- src/lib/datasrc/database.cc | 64 ++++++++++++++++++++++ src/lib/datasrc/database.h | 4 +- src/lib/datasrc/tests/database_unittest.cc | 2 +- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 71014d2cea..5fe9f7ba8e 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -14,8 +14,72 @@ #include +#include +#include + +using isc::dns::Name; + namespace isc { namespace datasrc { +DatabaseClient::DatabaseClient(std::auto_ptr connection) : + connection_(connection) +{ + if (connection_.get() == NULL) { + isc_throw(isc::InvalidParameter, + "No connection provided to DatabaseClient"); + } +} + +DataSourceClient::FindResult +DatabaseClient::findZone(const Name& name) const { + std::pair zone(connection_->getZone(name)); + // Try exact first + if (zone.first) { + return (FindResult(result::SUCCESS, + ZoneFinderPtr(new Finder(*connection_, + zone.second)))); + } + // Than super domains + // Start from 1, as 0 is covered above + for (size_t i(1); i < name.getLabelCount(); ++i) { + zone = connection_->getZone(name.split(i)); + if (zone.first) { + return (FindResult(result::PARTIALMATCH, + ZoneFinderPtr(new Finder(*connection_, + zone.second)))); + } + } + // No, really nothing + return (FindResult(result::NOTFOUND, ZoneFinderPtr())); +} + +DatabaseClient::Finder::Finder(DatabaseConnection& connection, int zone_id) : + connection_(connection), + zone_id_(zone_id) +{ } + +ZoneFinder::FindResult +DatabaseClient::Finder::find(const isc::dns::Name&, + const isc::dns::RRType&, + isc::dns::RRsetList*, + const FindOptions) const +{ + // TODO Implement + return (FindResult(SUCCESS, isc::dns::ConstRRsetPtr())); +} + +Name +DatabaseClient::Finder::getOrigin() const { + // TODO Implement + return (Name(".")); +} + +isc::dns::RRClass +DatabaseClient::Finder::getClass() const { + // TODO Implement + return isc::dns::RRClass::IN(); +} + } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index f3aa9f5740..5693479a77 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -111,7 +111,7 @@ public: * suggests, the client takes ownership of the connection and will * delete it when itself deleted. */ - DatabaseClient(const std::auto_ptr& connection); + DatabaseClient(std::auto_ptr connection); /** * \brief Corresponding ZoneFinder implementation * @@ -145,7 +145,7 @@ public: const isc::dns::RRType& type, isc::dns::RRsetList* target = NULL, const FindOptions options = FIND_DEFAULT) - const = 0; + const; /** * \brief The zone ID * diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f18dc3e69a..b60d5c0ced 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -33,7 +33,7 @@ namespace { class MockConnection : public DatabaseConnection { public: virtual std::pair getZone(const Name& name) const { - if (name == Name("zone.example.org")) { + if (name == Name("example.org")) { return (std::pair(true, 42)); } else { return (std::pair(false, 0)); From be9d5fe994e6a086a951e432d56e7de2af3cfd09 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 1 Aug 2011 13:29:36 +0200 Subject: [PATCH 299/974] [trac1061] First attempt at SQLite3Connection interface It will probably change during the implementation though. --- src/lib/datasrc/Makefile.am | 1 + src/lib/datasrc/sqlite3_connection.cc | 14 +++++++++ src/lib/datasrc/sqlite3_connection.h | 43 +++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/lib/datasrc/sqlite3_connection.cc create mode 100644 src/lib/datasrc/sqlite3_connection.h diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index eecd26a9b6..e6bff58fea 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -23,6 +23,7 @@ libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc libdatasrc_la_SOURCES += client.h libdatasrc_la_SOURCES += database.h database.cc +libdatasrc_la_SOURCES += sqlite3_connection.h sqlite3_connection.cc nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc new file mode 100644 index 0000000000..e8c25092dc --- /dev/null +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -0,0 +1,14 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h new file mode 100644 index 0000000000..e18386cd7d --- /dev/null +++ b/src/lib/datasrc/sqlite3_connection.h @@ -0,0 +1,43 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + + +#ifndef __DATASRC_SQLITE3_CONNECTION_H +#define __DATASRC_SQLITE3_CONNECTION_H + +#include + +// TODO Once the whole SQLite3 thing is ported here, move the Sqlite3Error +// here and remove the header file. +#include + +namespace isc { +namespace datasrc { + +class SQLite3Connection : public DatabaseConnection { +public: + // TODO Should we simplify this as well and just pass config to the + // constructor and be done? (whenever the config would change, we would + // recreate new connections) + Result init() { return (init(isc::data::ElementPtr())); } + Result init(const isc::data::ConstElementPtr& config); + Result close(); + + virtual std::pair getZone(const isc::dns::Name& name) const; +}; + +} +} + +#endif From e62e50f3143aa67bd60c2351ad61d7544f28d4ca Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 1 Aug 2011 15:26:35 +0200 Subject: [PATCH 300/974] [trac1160] addition for systems where libtool ignores dependency_libs still need to find a real solution for this problem... --- src/bin/auth/Makefile.am | 1 + src/bin/auth/benchmarks/Makefile.am | 1 + src/bin/auth/tests/Makefile.am | 1 + src/bin/dhcp6/Makefile.am | 1 + src/bin/host/Makefile.am | 1 + src/lib/bench/tests/Makefile.am | 1 + src/lib/cache/tests/Makefile.am | 1 + src/lib/datasrc/tests/Makefile.am | 1 + src/lib/dns/benchmarks/Makefile.am | 1 + src/lib/resolve/tests/Makefile.am | 1 + 10 files changed, 10 insertions(+) diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am index 64136c1f2b..e3128b5113 100644 --- a/src/bin/auth/Makefile.am +++ b/src/bin/auth/Makefile.am @@ -56,6 +56,7 @@ EXTRA_DIST += auth_messages.mes b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la +b10_auth_LDADD += $(top_builddir)/src/lib/util/libutil.la b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am index cf3fe4aed8..d51495bcb4 100644 --- a/src/bin/auth/benchmarks/Makefile.am +++ b/src/bin/auth/benchmarks/Makefile.am @@ -17,6 +17,7 @@ query_bench_SOURCES += ../auth_log.h ../auth_log.cc nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la +query_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la query_bench_LDADD += $(top_builddir)/src/lib/bench/libbench.la query_bench_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am index 71520c287f..5cd2f5a6cd 100644 --- a/src/bin/auth/tests/Makefile.am +++ b/src/bin/auth/tests/Makefile.am @@ -47,6 +47,7 @@ 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/util/libutil.la run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 8d341cb947..824e8a81bb 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -35,6 +35,7 @@ b10_dhcp6_SOURCES = main.cc b10_dhcp6_SOURCES += dhcp6.h b10_dhcp6_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/dns/libdns++.la +b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libutil.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libcc.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/bin/host/Makefile.am b/src/bin/host/Makefile.am index ec34ce751f..a8f96c21fe 100644 --- a/src/bin/host/Makefile.am +++ b/src/bin/host/Makefile.am @@ -13,6 +13,7 @@ CLEANFILES = *.gcno *.gcda bin_PROGRAMS = b10-host b10_host_SOURCES = host.cc b10_host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la +b10_host_LDADD += $(top_builddir)/src/lib/util/libutil.la b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la man_MANS = b10-host.1 diff --git a/src/lib/bench/tests/Makefile.am b/src/lib/bench/tests/Makefile.am index 3ebdf29a3e..3f8a67863b 100644 --- a/src/lib/bench/tests/Makefile.am +++ b/src/lib/bench/tests/Makefile.am @@ -16,6 +16,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDADD = $(top_builddir)/src/lib/bench/libbench.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la run_unittests_LDADD += $(GTEST_LDADD) diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am index f9237af2a5..a215c568ae 100644 --- a/src/lib/cache/tests/Makefile.am +++ b/src/lib/cache/tests/Makefile.am @@ -56,6 +56,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index fbcf9c95c0..ffedb75f02 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -36,6 +36,7 @@ run_unittests_LDADD = $(GTEST_LDADD) run_unittests_LDADD += $(SQLITE_LIBS) 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/util/libutil.la run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la diff --git a/src/lib/dns/benchmarks/Makefile.am b/src/lib/dns/benchmarks/Makefile.am index 864538518e..0d7856ff72 100644 --- a/src/lib/dns/benchmarks/Makefile.am +++ b/src/lib/dns/benchmarks/Makefile.am @@ -13,5 +13,6 @@ noinst_PROGRAMS = rdatarender_bench rdatarender_bench_SOURCES = rdatarender_bench.cc rdatarender_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la +rdatarender_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la rdatarender_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la rdatarender_bench_LDADD += $(SQLITE_LIBS) diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am index ee311a6bac..cf05d9b08e 100644 --- a/src/lib/resolve/tests/Makefile.am +++ b/src/lib/resolve/tests/Makefile.am @@ -31,6 +31,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la From f4620596bd798f3c0e1d4b7738a5c4ca1730cf89 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Mon, 1 Aug 2011 12:29:05 -0400 Subject: [PATCH 301/974] [trac1144] DS reworked and DLV added --- src/lib/dns/rdata/generic/detail/ds_like.h | 169 +++++++++++++++++++++ src/lib/dns/rdata/generic/dlv_32769.cc | 19 +++ src/lib/dns/rdata/generic/dlv_32769.h | 97 ++++++++++++ src/lib/dns/rdata/generic/ds_43.cc | 155 ------------------- src/lib/dns/rdata/generic/ds_43.h | 79 +++++++--- 5 files changed, 345 insertions(+), 174 deletions(-) create mode 100644 src/lib/dns/rdata/generic/detail/ds_like.h create mode 100644 src/lib/dns/rdata/generic/dlv_32769.cc create mode 100644 src/lib/dns/rdata/generic/dlv_32769.h diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h new file mode 100644 index 0000000000..20cf09469d --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -0,0 +1,169 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __DS_LIKE_H +#define __DS_LIKE_H 1 + +#include + +#include +#include + +using namespace std; +using namespace isc::util; + +struct DSImpl { + // straightforward representation of DS RDATA fields + DSImpl(uint16_t tag, uint8_t algorithm, uint8_t digest_type, + const vector& digest) : + tag_(tag), algorithm_(algorithm), digest_type_(digest_type), + digest_(digest) + {} + + uint16_t tag_; + uint8_t algorithm_; + uint8_t digest_type_; + const vector digest_; +}; + +templateclass DS_LIKE : public Rdata { +public: + DS_LIKE(const string& ds_str) : + impl_(NULL) + { + istringstream iss(ds_str); + unsigned int tag, algorithm, digest_type; + stringbuf digestbuf; + + iss >> tag >> algorithm >> digest_type >> &digestbuf; + if (iss.bad() || iss.fail()) { + isc_throw(InvalidRdataText, "Invalid DS text"); + } + if (tag > 0xffff) { + isc_throw(InvalidRdataText, "DS tag out of range"); + } + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, "DS algorithm out of range"); + } + if (digest_type > 0xff) { + isc_throw(InvalidRdataText, "DS digest type out of range"); + } + + vector digest; + decodeHex(digestbuf.str(), digest); + + impl_ = new DSImpl(tag, algorithm, digest_type, digest); + } + + DS_LIKE(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, "DS too short"); + } + + uint16_t tag = buffer.readUint16(); + uint16_t algorithm = buffer.readUint8(); + uint16_t digest_type = buffer.readUint8(); + + rdata_len -= 4; + vector digest(rdata_len); + buffer.readData(&digest[0], rdata_len); + + impl_ = new DSImpl(tag, algorithm, digest_type, digest); + } + + DS_LIKE(const DS_LIKE& source) : + Rdata(), impl_(new DSImpl(*source.impl_)) + {} + + DS_LIKE& + operator=(const DS_LIKE& source) { + if (impl_ == source.impl_) { + return (*this); + } + + DSImpl* newimpl = new DSImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); + } + + ~DS_LIKE() { + delete impl_; + } + + string + toText() const { + using namespace boost; + return (lexical_cast(static_cast(impl_->tag_)) + + " " + lexical_cast(static_cast(impl_->algorithm_)) + + " " + lexical_cast(static_cast(impl_->digest_type_)) + + " " + encodeHex(impl_->digest_)); + } + + void + toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->tag_); + buffer.writeUint8(impl_->algorithm_); + buffer.writeUint8(impl_->digest_type_); + buffer.writeData(&impl_->digest_[0], impl_->digest_.size()); + } + + void + toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->tag_); + renderer.writeUint8(impl_->algorithm_); + renderer.writeUint8(impl_->digest_type_); + renderer.writeData(&impl_->digest_[0], impl_->digest_.size()); + } + + int + compare(const Rdata& other) const { + const RTYPE& other_ds = dynamic_cast(other); + + if (impl_->tag_ != other_ds.impl_->tag_) { + return (impl_->tag_ < other_ds.impl_->tag_ ? -1 : 1); + } + if (impl_->algorithm_ != other_ds.impl_->algorithm_) { + return (impl_->algorithm_ < other_ds.impl_->algorithm_ ? -1 : 1); + } + if (impl_->digest_type_ != other_ds.impl_->digest_type_) { + return (impl_->digest_type_ < other_ds.impl_->digest_type_ ? -1 : 1); + } + + size_t this_len = impl_->digest_.size(); + size_t other_len = other_ds.impl_->digest_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&impl_->digest_[0], &other_ds.impl_->digest_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } + } + + uint16_t + getTag() const { + return (impl_->tag_); + } + +private: + DSImpl* impl_; +}; + +#endif // __DS_LIKE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/dlv_32769.cc b/src/lib/dns/rdata/generic/dlv_32769.cc new file mode 100644 index 0000000000..cf72d76882 --- /dev/null +++ b/src/lib/dns/rdata/generic/dlv_32769.cc @@ -0,0 +1,19 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/dlv_32769.h b/src/lib/dns/rdata/generic/dlv_32769.h new file mode 100644 index 0000000000..1a8e7a3766 --- /dev/null +++ b/src/lib/dns/rdata/generic/dlv_32769.h @@ -0,0 +1,97 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_HEADER_GUARD + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +#include + +class DLV : public DS_LIKE { + friend class DS_LIKE; + static string const id; + +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + +}; + +/// explicit DLV(const std::string& type_str); +inline DLV::DLV(const std::string& type_str) : DS_LIKE(type_str) {} + +/// DLV(isc::util::InputBuffer& buffer, size_t rdata_len); +inline DLV::DLV(isc::util::InputBuffer& buffer, size_t rdata_len) : DS_LIKE(buffer, rdata_len) {} + +/// DLV(const DLV& other); +inline DLV::DLV(const DLV& other) : DS_LIKE(other) {} + +/// virtual std::string toText() const; +inline std::string DLV::toText() const +{ + return DS_LIKE::toText(); +} + +/// virtual void toWire(isc::util::OutputBuffer& buffer) const; +inline void DLV::toWire(isc::util::OutputBuffer& buffer) const +{ + DS_LIKE::toWire(buffer); +} + +/// virtual void toWire(AbstractMessageRenderer& renderer) const; +inline void DLV::toWire(AbstractMessageRenderer& renderer) const +{ + DS_LIKE::toWire(renderer); +} + +/// virtual int compare(const Rdata& other) const; +inline int DLV::compare(const Rdata& other) const +{ + return DS_LIKE::compare(other); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc index 1b48456d8b..cf72d76882 100644 --- a/src/lib/dns/rdata/generic/ds_43.cc +++ b/src/lib/dns/rdata/generic/ds_43.cc @@ -12,163 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -using namespace std; -using namespace isc::util; -using namespace isc::util::encode; - // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE -struct DSImpl { - // straightforward representation of DS RDATA fields - DSImpl(uint16_t tag, uint8_t algorithm, uint8_t digest_type, - const vector& digest) : - tag_(tag), algorithm_(algorithm), digest_type_(digest_type), - digest_(digest) - {} - - uint16_t tag_; - uint8_t algorithm_; - uint8_t digest_type_; - const vector digest_; -}; - -DS::DS(const string& ds_str) : - impl_(NULL) -{ - istringstream iss(ds_str); - unsigned int tag, algorithm, digest_type; - stringbuf digestbuf; - - iss >> tag >> algorithm >> digest_type >> &digestbuf; - if (iss.bad() || iss.fail()) { - isc_throw(InvalidRdataText, "Invalid DS text"); - } - if (tag > 0xffff) { - isc_throw(InvalidRdataText, "DS tag out of range"); - } - if (algorithm > 0xff) { - isc_throw(InvalidRdataText, "DS algorithm out of range"); - } - if (digest_type > 0xff) { - isc_throw(InvalidRdataText, "DS digest type out of range"); - } - - vector digest; - decodeHex(digestbuf.str(), digest); - - impl_ = new DSImpl(tag, algorithm, digest_type, digest); -} - -DS::DS(InputBuffer& buffer, size_t rdata_len) { - if (rdata_len < 4) { - isc_throw(InvalidRdataLength, "DS too short"); - } - - uint16_t tag = buffer.readUint16(); - uint16_t algorithm = buffer.readUint8(); - uint16_t digest_type = buffer.readUint8(); - - rdata_len -= 4; - vector digest(rdata_len); - buffer.readData(&digest[0], rdata_len); - - impl_ = new DSImpl(tag, algorithm, digest_type, digest); -} - -DS::DS(const DS& source) : - Rdata(), impl_(new DSImpl(*source.impl_)) -{} - -DS& -DS::operator=(const DS& source) { - if (impl_ == source.impl_) { - return (*this); - } - - DSImpl* newimpl = new DSImpl(*source.impl_); - delete impl_; - impl_ = newimpl; - - return (*this); -} - -DS::~DS() { - delete impl_; -} - -string -DS::toText() const { - using namespace boost; - return (lexical_cast(static_cast(impl_->tag_)) + - " " + lexical_cast(static_cast(impl_->algorithm_)) + - " " + lexical_cast(static_cast(impl_->digest_type_)) + - " " + encodeHex(impl_->digest_)); -} - -void -DS::toWire(OutputBuffer& buffer) const { - buffer.writeUint16(impl_->tag_); - buffer.writeUint8(impl_->algorithm_); - buffer.writeUint8(impl_->digest_type_); - buffer.writeData(&impl_->digest_[0], impl_->digest_.size()); -} - -void -DS::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeUint16(impl_->tag_); - renderer.writeUint8(impl_->algorithm_); - renderer.writeUint8(impl_->digest_type_); - renderer.writeData(&impl_->digest_[0], impl_->digest_.size()); -} - -int -DS::compare(const Rdata& other) const { - const DS& other_ds = dynamic_cast(other); - - if (impl_->tag_ != other_ds.impl_->tag_) { - return (impl_->tag_ < other_ds.impl_->tag_ ? -1 : 1); - } - if (impl_->algorithm_ != other_ds.impl_->algorithm_) { - return (impl_->algorithm_ < other_ds.impl_->algorithm_ ? -1 : 1); - } - if (impl_->digest_type_ != other_ds.impl_->digest_type_) { - return (impl_->digest_type_ < other_ds.impl_->digest_type_ ? -1 : 1); - } - - size_t this_len = impl_->digest_.size(); - size_t other_len = other_ds.impl_->digest_.size(); - size_t cmplen = min(this_len, other_len); - int cmp = memcmp(&impl_->digest_[0], &other_ds.impl_->digest_[0], cmplen); - if (cmp != 0) { - return (cmp); - } else { - return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); - } -} - -uint16_t -DS::getTag() const { - return (impl_->tag_); -} - // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/ds_43.h b/src/lib/dns/rdata/generic/ds_43.h index 03b19a0903..0bb813587e 100644 --- a/src/lib/dns/rdata/generic/ds_43.h +++ b/src/lib/dns/rdata/generic/ds_43.h @@ -12,17 +12,30 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include - -#include - -#include -#include -#include -#include - // BEGIN_HEADER_GUARD +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + // BEGIN_ISC_NAMESPACE // BEGIN_COMMON_DECLARATIONS @@ -30,23 +43,51 @@ // BEGIN_RDATA_NAMESPACE -struct DSImpl; +#include + +class DS : public DS_LIKE { + friend class DS_LIKE; + static string const id; -class DS : public Rdata { public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS - DS& operator=(const DS& source); - ~DS(); - /// - /// Specialized methods - /// - uint16_t getTag() const; -private: - DSImpl* impl_; }; +/// explicit DS(const std::string& type_str); +inline DS::DS(const std::string& type_str) : DS_LIKE(type_str) {} + +/// DS(isc::util::InputBuffer& buffer, size_t rdata_len); +inline DS::DS(isc::util::InputBuffer& buffer, size_t rdata_len) : DS_LIKE(buffer, rdata_len) {} + +/// DS(const DS& other); +inline DS::DS(const DS& other) : DS_LIKE(other) {} + +/// virtual std::string toText() const; +inline std::string DS::toText() const +{ + return DS_LIKE::toText(); +} + +/// virtual void toWire(isc::util::OutputBuffer& buffer) const; +inline void DS::toWire(isc::util::OutputBuffer& buffer) const +{ + DS_LIKE::toWire(buffer); +} + +/// virtual void toWire(AbstractMessageRenderer& renderer) const; +inline void DS::toWire(AbstractMessageRenderer& renderer) const +{ + DS_LIKE::toWire(renderer); +} + +/// virtual int compare(const Rdata& other) const; +inline int DS::compare(const Rdata& other) const +{ + return DS_LIKE::compare(other); +} + // END_RDATA_NAMESPACE // END_ISC_NAMESPACE // END_HEADER_GUARD From d2f96b7e1e3e4a5917ea73a56429fa645d8ede7c Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Mon, 1 Aug 2011 17:44:14 +0100 Subject: [PATCH 302/974] [master] ChangeLog for trac 1003 --- ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index 41c9faaaad..d0410df2b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +276. [func] stephen + Although the top-level loggers are named after the program (e.g. + b10-auth, b10-resolver), allow the logger configuration to omit the + "b10-" prefix and use just the module name. + (Trac #1003, git a01cd4ac5a68a1749593600c0f338620511cae2d) + 275. [func] jinmei Added support for TSIG key matching in ACLs. The xfrout ACL can now refer to TSIG key names using the "key" attribute. For From dc3b856b460ff380feb68cdff551f334e6db5a27 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Mon, 1 Aug 2011 13:41:04 -0500 Subject: [PATCH 303/974] [trac1011] add section title to logging message format section also removed some tabs and spaces at end of lines --- doc/guide/bind10-guide.xml | 131 +++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 309c35d5fe..b21e49bea6 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -146,7 +146,7 @@ The processes started by the bind10 command have names starting with "b10-", including: - + @@ -1472,60 +1472,63 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - - Each message written by BIND 10 to the configured logging destinations - comprises a number of components that identify the origin of the - message and, if the message indicates a problem, information about the - problem that may be useful in fixing it. - +
+ Logging Message Formatn - - Consider the message below logged to a file: - 2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink] + + Each message written by BIND 10 to the configured logging destinations + comprises a number of components that identify the origin of the + message and, if the message indicates a problem, information about the + problem that may be useful in fixing it. + + + + Consider the message below logged to a file: + 2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink] ASIODNS_OPENSOCK error 111 opening TCP socket to 127.0.0.1(53) - + - - Note: the layout of messages written to the system logging - file (syslog) may be slightly different. This message has - been split across two lines here for display reasons; in the - logging file, it will appear on one line.) - + + Note: the layout of messages written to the system logging + file (syslog) may be slightly different. This message has + been split across two lines here for display reasons; in the + logging file, it will appear on one line.) + - - The log message comprises a number of components: + + The log message comprises a number of components: - - - 2011-06-15 13:48:22.034 + + + 2011-06-15 13:48:22.034 - - The date and time at which the message was generated. - - + + The date and time at which the message was generated. + + - - ERROR - - The severity of the message. - - + + ERROR + + The severity of the message. + + - - [b10-resolver.asiolink] - - The source of the message. This comprises two components: - the BIND 10 process generating the message (in this - case, b10-resolver) and the module - within the program from which the message originated - (which in the example is the asynchronous I/O link - module, asiolink). - - + + [b10-resolver.asiolink] + + The source of the message. This comprises two components: + the BIND 10 process generating the message (in this + case, b10-resolver) and the module + within the program from which the message originated + (which in the example is the asynchronous I/O link + module, asiolink). + + - - ASIODNS_OPENSOCK - + + ASIODNS_OPENSOCK + The message identification. Every message in BIND 10 has a unique identification, which can be used as an index into the () from which more information can be obtained. - - + + - - error 111 opening TCP socket to 127.0.0.1(53) - - A brief description of the cause of the problem. Within this text, - information relating to the condition that caused the message to - be logged will be included. In this example, error number 111 - (an operating system-specific error number) was encountered when - trying to open a TCP connection to port 53 on the local system - (address 127.0.0.1). The next step would be to find out the reason - for the failure by consulting your system's documentation to - identify what error number 111 means. - - - - + + error 111 opening TCP socket to 127.0.0.1(53) + + A brief description of the cause of the problem. Within this text, + information relating to the condition that caused the message to + be logged will be included. In this example, error number 111 + (an operating system-specific error number) was encountered when + trying to open a TCP connection to port 53 on the local system + (address 127.0.0.1). The next step would be to find out the reason + for the failure by consulting your system's documentation to + identify what error number 111 means. + + + + + +
Logging configuration From 9819295a58b8b40ca6d95c84f1f1de08fb0eb707 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Mon, 1 Aug 2011 13:42:26 -0500 Subject: [PATCH 304/974] [trac1011] reformat some docbook (no change to final result) --- doc/guide/bind10-guide.xml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index b21e49bea6..c7c5fd57b3 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1476,10 +1476,11 @@ then change those defaults with config set Resolver/forward_addresses[0]/address Logging Message Formatn - Each message written by BIND 10 to the configured logging destinations - comprises a number of components that identify the origin of the - message and, if the message indicates a problem, information about the - problem that may be useful in fixing it. + Each message written by BIND 10 to the configured logging + destinations comprises a number of components that identify + the origin of the message and, if the message indicates + a problem, information about the problem that may be + useful in fixing it. @@ -1542,14 +1543,16 @@ then change those defaults with config set Resolver/forward_addresses[0]/address error 111 opening TCP socket to 127.0.0.1(53) - A brief description of the cause of the problem. Within this text, - information relating to the condition that caused the message to - be logged will be included. In this example, error number 111 - (an operating system-specific error number) was encountered when - trying to open a TCP connection to port 53 on the local system - (address 127.0.0.1). The next step would be to find out the reason - for the failure by consulting your system's documentation to - identify what error number 111 means. + A brief description of the cause of the problem. + Within this text, information relating to the condition + that caused the message to be logged will be included. + In this example, error number 111 (an operating + system-specific error number) was encountered when + trying to open a TCP connection to port 53 on the + local system (address 127.0.0.1). The next step + would be to find out the reason for the failure by + consulting your system's documentation to identify + what error number 111 means. From 1c8043e5b50bd47d7734397a08d5015e3672b9ad Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Mon, 1 Aug 2011 13:43:15 -0500 Subject: [PATCH 305/974] [trac1011] fix my typo --- doc/guide/bind10-guide.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index c7c5fd57b3..34ba092cb7 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1473,7 +1473,7 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
- Logging Message Formatn + Logging Message Format Each message written by BIND 10 to the configured logging From 4625b640b9b5892da7f35f165407ed3e850353d9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 1 Aug 2011 12:49:20 -0700 Subject: [PATCH 306/974] [trac1128] a couple of editorial fixes --- src/lib/dns/rdata/in_1/srv_33.cc | 2 +- src/lib/dns/rdata/in_1/srv_33.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/dns/rdata/in_1/srv_33.cc b/src/lib/dns/rdata/in_1/srv_33.cc index 09808b685b..ea85779ef6 100644 --- a/src/lib/dns/rdata/in_1/srv_33.cc +++ b/src/lib/dns/rdata/in_1/srv_33.cc @@ -128,7 +128,7 @@ SRV::SRV(const string& srv_str) : /// /// According to RFC2782, the Target field must be a non compressed form /// of domain name. But this implementation accepts a %SRV RR even if that -/// field is compressed. +/// field is compressed as suggested in RFC3597. /// /// \param buffer A buffer storing the wire format data. /// \param rdata_len The length of the RDATA in bytes, normally expected diff --git a/src/lib/dns/rdata/in_1/srv_33.h b/src/lib/dns/rdata/in_1/srv_33.h index aa223186fb..d067021939 100644 --- a/src/lib/dns/rdata/in_1/srv_33.h +++ b/src/lib/dns/rdata/in_1/srv_33.h @@ -26,15 +26,14 @@ // BEGIN_RDATA_NAMESPACE +struct SRVImpl; + /// \brief \c rdata::SRV class represents the SRV RDATA as defined %in /// RFC2782. /// /// This class implements the basic interfaces inherited from the abstract /// \c rdata::Rdata class, and provides trivial accessors specific to the /// SRV RDATA. - -struct SRVImpl; - class SRV : public Rdata { public: // BEGIN_COMMON_MEMBERS From 61029d971895738ba353841d99f4ca07ecf792b7 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Mon, 1 Aug 2011 16:31:32 -0500 Subject: [PATCH 307/974] [trac1011] fix typo --- doc/guide/bind10-guide.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 34ba092cb7..d6d4703780 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -547,7 +547,7 @@ Debian and Ubuntu: --prefix - Define the the installation location (the + Define the installation location (the default is /usr/local/). From 16e52275c4c9e355cf4e448a5b17136f24324d7a Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Mon, 1 Aug 2011 16:56:57 -0500 Subject: [PATCH 308/974] [trac1011] some docbook formatting for new content This needs to be indented correctly still. --- doc/guide/bind10-guide.xml | 182 ++++++++++++++++++++++--------------- 1 file changed, 110 insertions(+), 72 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index d6d4703780..3024467822 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1603,25 +1603,20 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - - -name (string) - - - - +
+ name (string) + Each logger in the system has a name, the name being that of the component using it to log messages. For instance, if you want to configure logging for the resolver module, you add an entry for a logger named 'Resolver'. This configuration will then be used by the loggers in the Resolver module, and all the libraries used by it. + - - If you want to specify logging for one specific library @@ -1691,11 +1686,10 @@ name (string) - +
-severity (string) - -
+
+ severity (string) @@ -1708,15 +1702,40 @@ severity (string) Each message is logged with an associated severity which may be one of the following (in descending order of severity): - - FATAL - ERROR - WARN - INFO - DEBUG - + + + + FATAL + + + + + + ERROR + + + + + + WARN + + + + + + INFO + + + + + + DEBUG + + + + When the severity of a logger is set to one of these @@ -1729,11 +1748,10 @@ severity (string) - +
-output_options (list) - - +
+ output_options (list) @@ -1749,11 +1767,10 @@ output_options (list) - +
-debuglevel (integer) - - +
+ debuglevel (integer) @@ -1774,11 +1791,10 @@ debuglevel (integer) - +
-additive (true or false) - - +
+ additive (true or false) @@ -1796,6 +1812,8 @@ additive (true or false)
+
+
Output Options @@ -1807,11 +1825,8 @@ additive (true or false) - - -destination (string) - - +
+ destination (string) @@ -1819,19 +1834,36 @@ destination (string) - + - * console - * file - * syslog + - + + + console + + - + + + file + + -output (string) + + + syslog + + - + + + + +
+ +
+ output (string) @@ -1840,30 +1872,38 @@ output (string) - - - * destination is 'console' + + + destination is 'console' + + 'output' must be one of 'stdout' (messages printed to standard output) or 'stderr' (messages printed to standard error). + + + - - - - - * destination is 'file' + + destination is 'file' + + The value of output is interpreted as a file name; log messages will be appended to this file. + + + - - - - - * destination is 'syslog' - + + destination is 'syslog' + + The value of output is interpreted as the syslog facility (e.g. 'local0') that should be used for log messages. + + + - + @@ -1871,11 +1911,10 @@ output (string) - +
-flush (true of false) - - +
+ flush (true of false) @@ -1886,11 +1925,10 @@ flush (true of false) - +
-maxsize (integer) - - +
+ maxsize (integer) @@ -1908,11 +1946,10 @@ maxsize (integer) - +
-maxver (integer) - - +
+ maxver (integer) @@ -1921,6 +1958,7 @@ maxver (integer) if 'file'. +
From 5fd94aa027828c50e63ae1073d9d6708e0a9c223 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Tue, 2 Aug 2011 14:10:23 +0800 Subject: [PATCH 309/974] [master] fix EXTRA_DIST for dns/testdata --- src/lib/dns/tests/testdata/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 257f2f3c9f..60735e90bd 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -101,6 +101,7 @@ EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec +EXTRA_DIST += rdata_srv_fromWire EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire From f17363ea38564867df555b6be9138d2eff28daa0 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Tue, 2 Aug 2011 19:07:02 +0800 Subject: [PATCH 310/974] [trac1130] Add basic extraction function --- src/lib/dns/rdata/in_1/naptr_35.cc | 100 ++++++++++++++++++- src/lib/dns/rdata/in_1/naptr_35.h | 20 ++++ src/lib/dns/tests/rdata_in_naptr_unittest.cc | 10 +- 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/src/lib/dns/rdata/in_1/naptr_35.cc b/src/lib/dns/rdata/in_1/naptr_35.cc index cd58e8da8a..5fd5ae8a34 100644 --- a/src/lib/dns/rdata/in_1/naptr_35.cc +++ b/src/lib/dns/rdata/in_1/naptr_35.cc @@ -33,13 +33,34 @@ using namespace isc::util; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE -NAPTR::NAPTR(InputBuffer& buffer, size_t len) { +NAPTR::NAPTR(InputBuffer& buffer, size_t len): + replacement_(".") +{ } -NAPTR::NAPTR(const std::string& naptr_str) { +NAPTR::NAPTR(const std::string& naptr_str): + replacement_(".") +{ + istringstream iss(naptr_str); + uint16_t order; + uint16_t preference; + + iss >> order >> preference; + + if (iss.bad() || iss.fail()) { + isc_throw(InvalidRdataText, "Invalid NAPTR text format"); + } + + order_ = order; + preference_ = preference; + + string::const_iterator input_iterator = naptr_str.begin() + iss.tellg(); + flags_ = getNextCharacterString(naptr_str, input_iterator); } -NAPTR::NAPTR(const NAPTR& naptr) { +NAPTR::NAPTR(const NAPTR& naptr): + replacement_(".") +{ } void @@ -52,6 +73,7 @@ NAPTR::toWire(AbstractMessageRenderer& renderer) const { string NAPTR::toText() const { + return ""; } int @@ -59,5 +81,77 @@ NAPTR::compare(const Rdata& other) const { return 0; } +uint16_t +NAPTR::getOrder() const { + return order_; +} + +uint16_t +NAPTR::getPreference() const { + return preference_; +} + +const std::string& +NAPTR::getFlags() const { + return flags_; +} + +const std::string& +NAPTR::getServices() const { + return services_; +} + +const std::string& +NAPTR::getRegexp() const { + return regexp_; +} + +const Name& +NAPTR::getReplacement() const { + return replacement_; +} + +std::string +NAPTR::getNextCharacterString(const std::string& input_str, std::string::const_iterator& input_iterator){ + string result; + + // Skip white spaces + while (input_iterator < input_str.end() && isspace(*input_iterator)) { + ++input_iterator; + } + + // If the input string only contains white-spaces, it is an invalid + if (input_iterator >= input_str.end()) { + isc_throw(InvalidRdataText, "Invalid NAPTR text format"); + } + + // Whether the is seperated with doulble quotes symbol (") + bool quotes_seperated = (*input_iterator == '"'); + + if (quotes_seperated) { + ++input_iterator; + } + + while(input_iterator < input_str.end()){ + if (quotes_seperated) { + // If the is seperated with quotes symbol and another + // quotes symbol is encountered, it is the end of the + if (*input_iterator == '"') { + break; + } + } else if (*input_iterator == ' ') { + // If the is not seperated with quotes symbol, it is + // seperated with char + break; + } + + result.push_back(*input_iterator); + + ++input_iterator; + } + + return result; +} + // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/naptr_35.h b/src/lib/dns/rdata/in_1/naptr_35.h index be5e22df2e..2921771b8c 100644 --- a/src/lib/dns/rdata/in_1/naptr_35.h +++ b/src/lib/dns/rdata/in_1/naptr_35.h @@ -16,6 +16,7 @@ #include +#include #include // BEGIN_ISC_NAMESPACE @@ -31,7 +32,26 @@ public: // END_COMMON_MEMBERS // NAPTR specific methods + uint16_t getOrder() const; + uint16_t getPreference() const; + const std::string& getFlags() const; + const std::string& getServices() const; + const std::string& getRegexp() const; + const Name& getReplacement() const; private: + /// Extract a from a string + /// + /// \param input_str The input string + /// \param input_iterator The iterator from which to start extracting + /// \return a std::string that contains the extracted + std::string getNextCharacterString(const std::string& input_str, std::string::const_iterator& input_iterator); + + uint16_t order_; + uint16_t preference_; + std::string flags_; + std::string services_; + std::string regexp_; + Name replacement_; }; // END_RDATA_NAMESPACE diff --git a/src/lib/dns/tests/rdata_in_naptr_unittest.cc b/src/lib/dns/tests/rdata_in_naptr_unittest.cc index 0f5577b375..f6871823f4 100644 --- a/src/lib/dns/tests/rdata_in_naptr_unittest.cc +++ b/src/lib/dns/tests/rdata_in_naptr_unittest.cc @@ -30,6 +30,7 @@ using namespace std; using namespace isc::dns; using namespace isc::util; using namespace isc::dns::rdata; +using namespace isc::dns::rdata::in; namespace { class Rdata_IN_NAPTR_Test : public RdataTest { @@ -39,9 +40,16 @@ class Rdata_IN_NAPTR_Test : public RdataTest { static uint8_t naptr_rdata[] = {0x00,0x0a,0x00,0x64,0x01,0x53,0x07,0x53,0x49,0x50,0x2b,0x44,0x32,0x55,0x00,0x04,0x5f,0x73,0x69,0x70, 0x04,0x5f,0x75,0x64,0x70,0x07,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x03,0x63,0x6f,0x6d,0x00}; -static char *naptr_str = "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str = "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; TEST_F(Rdata_IN_NAPTR_Test, createFromText) { + NAPTR naptr(naptr_str); + EXPECT_EQ(10, naptr.getOrder()); + EXPECT_EQ(100, naptr.getPreference()); + EXPECT_EQ(string("S"), naptr.getFlags()); +} + +TEST_F(Rdata_IN_NAPTR_Test, createFromWire) { } } From e76dc86b0a01a54dab56cbf8552bd0c5fbb5b461 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 2 Aug 2011 22:28:14 +0200 Subject: [PATCH 311/974] [trac1061] (Co|De)structor of SQLite3Connection Most of the code is slightly modified copy-paste from the Sqlite3DataSource. No documentation or log messages and the getZone method is dummy. But it compiles and provides some kind of frame for the rest. --- src/lib/datasrc/datasrc_messages.mes | 7 + src/lib/datasrc/sqlite3_connection.cc | 280 ++++++++++++++++++ src/lib/datasrc/sqlite3_connection.h | 28 +- src/lib/datasrc/tests/Makefile.am | 1 + .../tests/sqlite3_connection_unittest.cc | 70 +++++ 5 files changed, 376 insertions(+), 10 deletions(-) create mode 100644 src/lib/datasrc/tests/sqlite3_connection_unittest.cc diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 3dc69e070d..a6f783785c 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -497,3 +497,10 @@ data source. This indicates a programming error. An internal task of unknown type was generated. +% DATASRC_SQLITE_NEWCONN TODO + +% DATASRC_SQLITE_DROPCONN TODO + +% DATASRC_SQLITE_CONNOPEN TODO + +% DATASRC_SQLITE_CONNCLOSE TODO diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index e8c25092dc..6dd8319879 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -12,3 +12,283 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + +#include +#include +#include + +namespace isc { +namespace datasrc { + +struct SQLite3Parameters { + SQLite3Parameters() : + db_(NULL), version_(-1), + q_zone_(NULL) /*, q_record_(NULL), q_addrs_(NULL), q_referral_(NULL), + q_any_(NULL), q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL), + q_prevnsec3_(NULL) */ + {} + sqlite3* db_; + int version_; + sqlite3_stmt* q_zone_; + /* + TODO: Yet unneeded statements + sqlite3_stmt* q_record_; + sqlite3_stmt* q_addrs_; + sqlite3_stmt* q_referral_; + sqlite3_stmt* q_any_; + sqlite3_stmt* q_count_; + sqlite3_stmt* q_previous_; + sqlite3_stmt* q_nsec3_; + sqlite3_stmt* q_prevnsec3_; + */ +}; + +SQLite3Connection::SQLite3Connection(const isc::data::ConstElementPtr& + config) : + dbparameters_(new SQLite3Parameters) +{ + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN); + + if (config && config->contains("database_file")) { + open(config->get("database_file")->stringValue()); + } else { + isc_throw(DataSourceError, "No SQLite database file specified"); + } +} + +namespace { + +// This is a helper class to initialize a Sqlite3 DB safely. An object of +// this class encapsulates all temporary resources that are necessary for +// the initialization, and release them in the destructor. Once everything +// is properly initialized, the move() method moves the allocated resources +// to the main object in an exception free manner. This way, the main code +// for the initialization can be exception safe, and can provide the strong +// exception guarantee. +class Initializer { +public: + ~Initializer() { + if (params_.q_zone_ != NULL) { + sqlite3_finalize(params_.q_zone_); + } + /* + if (params_.q_record_ != NULL) { + sqlite3_finalize(params_.q_record_); + } + if (params_.q_addrs_ != NULL) { + sqlite3_finalize(params_.q_addrs_); + } + if (params_.q_referral_ != NULL) { + sqlite3_finalize(params_.q_referral_); + } + if (params_.q_any_ != NULL) { + sqlite3_finalize(params_.q_any_); + } + if (params_.q_count_ != NULL) { + sqlite3_finalize(params_.q_count_); + } + if (params_.q_previous_ != NULL) { + sqlite3_finalize(params_.q_previous_); + } + if (params_.q_nsec3_ != NULL) { + sqlite3_finalize(params_.q_nsec3_); + } + if (params_.q_prevnsec3_ != NULL) { + sqlite3_finalize(params_.q_prevnsec3_); + } + */ + if (params_.db_ != NULL) { + sqlite3_close(params_.db_); + } + } + void move(SQLite3Parameters* dst) { + *dst = params_; + params_ = SQLite3Parameters(); // clear everything + } + SQLite3Parameters params_; +}; + +const char* const SCHEMA_LIST[] = { + "CREATE TABLE schema_version (version INTEGER NOT NULL)", + "INSERT INTO schema_version VALUES (1)", + "CREATE TABLE zones (id INTEGER PRIMARY KEY, " + "name STRING NOT NULL COLLATE NOCASE, " + "rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN', " + "dnssec BOOLEAN NOT NULL DEFAULT 0)", + "CREATE INDEX zones_byname ON zones (name)", + "CREATE TABLE records (id INTEGER PRIMARY KEY, " + "zone_id INTEGER NOT NULL, name STRING NOT NULL COLLATE NOCASE, " + "rname STRING NOT NULL COLLATE NOCASE, ttl INTEGER NOT NULL, " + "rdtype STRING NOT NULL COLLATE NOCASE, sigtype STRING COLLATE NOCASE, " + "rdata STRING NOT NULL)", + "CREATE INDEX records_byname ON records (name)", + "CREATE INDEX records_byrname ON records (rname)", + "CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, " + "hash STRING NOT NULL COLLATE NOCASE, " + "owner STRING NOT NULL COLLATE NOCASE, " + "ttl INTEGER NOT NULL, rdtype STRING NOT NULL COLLATE NOCASE, " + "rdata STRING NOT NULL)", + "CREATE INDEX nsec3_byhash ON nsec3 (hash)", + NULL +}; + +const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1"; + +/* TODO: Prune the statements, not everything will be needed maybe? +const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata " + "FROM records WHERE zone_id=?1 AND name=?2 AND " + "((rdtype=?3 OR sigtype=?3) OR " + "(rdtype='CNAME' OR sigtype='CNAME') OR " + "(rdtype='NS' OR sigtype='NS'))"; + +const char* const q_addrs_str = "SELECT rdtype, ttl, sigtype, rdata " + "FROM records WHERE zone_id=?1 AND name=?2 AND " + "(rdtype='A' OR sigtype='A' OR rdtype='AAAA' OR sigtype='AAAA')"; + +const char* const q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM " + "records WHERE zone_id=?1 AND name=?2 AND" + "(rdtype='NS' OR sigtype='NS' OR rdtype='DS' OR sigtype='DS' OR " + "rdtype='DNAME' OR sigtype='DNAME')"; + +const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata " + "FROM records WHERE zone_id=?1 AND name=?2"; + +const char* const q_count_str = "SELECT COUNT(*) FROM records " + "WHERE zone_id=?1 AND rname LIKE (?2 || '%');"; + +const char* const q_previous_str = "SELECT name FROM records " + "WHERE zone_id=?1 AND rdtype = 'NSEC' AND " + "rname < $2 ORDER BY rname DESC LIMIT 1"; + +const char* const q_nsec3_str = "SELECT rdtype, ttl, rdata FROM nsec3 " + "WHERE zone_id = ?1 AND hash = $2"; + +const char* const q_prevnsec3_str = "SELECT hash FROM nsec3 " + "WHERE zone_id = ?1 AND hash <= $2 ORDER BY hash DESC LIMIT 1"; + */ + +sqlite3_stmt* +prepare(sqlite3* const db, const char* const statement) { + sqlite3_stmt* prepared = NULL; + if (sqlite3_prepare_v2(db, statement, -1, &prepared, NULL) != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not prepare SQLite statement: " << + statement); + } + return (prepared); +} + +void +checkAndSetupSchema(Initializer* initializer) { + sqlite3* const db = initializer->params_.db_; + + sqlite3_stmt* prepared = NULL; + if (sqlite3_prepare_v2(db, "SELECT version FROM schema_version", -1, + &prepared, NULL) == SQLITE_OK && + sqlite3_step(prepared) == SQLITE_ROW) { + initializer->params_.version_ = sqlite3_column_int(prepared, 0); + sqlite3_finalize(prepared); + } else { + logger.info(DATASRC_SQLITE_SETUP); + if (prepared != NULL) { + sqlite3_finalize(prepared); + } + for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) { + if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) != + SQLITE_OK) { + isc_throw(SQLite3Error, + "Failed to set up schema " << SCHEMA_LIST[i]); + } + } + } + + initializer->params_.q_zone_ = prepare(db, q_zone_str); + /* TODO: Yet unneeded statements + initializer->params_.q_record_ = prepare(db, q_record_str); + initializer->params_.q_addrs_ = prepare(db, q_addrs_str); + initializer->params_.q_referral_ = prepare(db, q_referral_str); + initializer->params_.q_any_ = prepare(db, q_any_str); + initializer->params_.q_count_ = prepare(db, q_count_str); + initializer->params_.q_previous_ = prepare(db, q_previous_str); + initializer->params_.q_nsec3_ = prepare(db, q_nsec3_str); + initializer->params_.q_prevnsec3_ = prepare(db, q_prevnsec3_str); + */ +} + +} + +void +SQLite3Connection::open(const std::string& name) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNOPEN).arg(name); + if (dbparameters_->db_ != NULL) { + // There shouldn't be a way to trigger this anyway + isc_throw(DataSourceError, "Duplicate SQLite open with " << name); + } + + Initializer initializer; + + if (sqlite3_open(name.c_str(), &initializer.params_.db_) != 0) { + isc_throw(SQLite3Error, "Cannot open SQLite database file: " << name); + } + + checkAndSetupSchema(&initializer); + initializer.move(dbparameters_); +} + +SQLite3Connection::~ SQLite3Connection() { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DROPCONN); + if (dbparameters_->db_ != NULL) { + close(); + } + delete dbparameters_; +} + +void +SQLite3Connection::close(void) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNCLOSE); + if (dbparameters_->db_ == NULL) { + isc_throw(DataSourceError, + "SQLite data source is being closed before open"); + } + + // XXX: sqlite3_finalize() could fail. What should we do in that case? + sqlite3_finalize(dbparameters_->q_zone_); + dbparameters_->q_zone_ = NULL; + + /* TODO: Once they are needed or not, uncomment or drop + sqlite3_finalize(dbparameters->q_record_); + dbparameters->q_record_ = NULL; + + sqlite3_finalize(dbparameters->q_addrs_); + dbparameters->q_addrs_ = NULL; + + sqlite3_finalize(dbparameters->q_referral_); + dbparameters->q_referral_ = NULL; + + sqlite3_finalize(dbparameters->q_any_); + dbparameters->q_any_ = NULL; + + sqlite3_finalize(dbparameters->q_count_); + dbparameters->q_count_ = NULL; + + sqlite3_finalize(dbparameters->q_previous_); + dbparameters->q_previous_ = NULL; + + sqlite3_finalize(dbparameters->q_prevnsec3_); + dbparameters->q_prevnsec3_ = NULL; + + sqlite3_finalize(dbparameters->q_nsec3_); + dbparameters->q_nsec3_ = NULL; + */ + + sqlite3_close(dbparameters_->db_); + dbparameters_->db_ = NULL; +} + +std::pair +SQLite3Connection::getZone(const isc::dns::Name&) const { + return std::pair(false, 0); +} + +} +} diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index e18386cd7d..62d42e1156 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -18,23 +18,31 @@ #include -// TODO Once the whole SQLite3 thing is ported here, move the Sqlite3Error -// here and remove the header file. -#include +#include +#include + +#include namespace isc { namespace datasrc { +class SQLite3Error : public Exception { +public: + SQLite3Error(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +struct SQLite3Parameters; + class SQLite3Connection : public DatabaseConnection { public: - // TODO Should we simplify this as well and just pass config to the - // constructor and be done? (whenever the config would change, we would - // recreate new connections) - Result init() { return (init(isc::data::ElementPtr())); } - Result init(const isc::data::ConstElementPtr& config); - Result close(); - + SQLite3Connection(const isc::data::ConstElementPtr& config); + ~ SQLite3Connection(); virtual std::pair getZone(const isc::dns::Name& name) const; +private: + SQLite3Parameters* dbparameters_; + void open(const std::string& filename); + void close(); }; } diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index 9cfd0d8c29..c2e2b5caad 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -29,6 +29,7 @@ run_unittests_SOURCES += zonetable_unittest.cc run_unittests_SOURCES += memory_datasrc_unittest.cc run_unittests_SOURCES += logger_unittest.cc run_unittests_SOURCES += database_unittest.cc +run_unittests_SOURCES += sqlite3_connection_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc new file mode 100644 index 0000000000..b88e986cac --- /dev/null +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -0,0 +1,70 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include + +#include + +using namespace isc::datasrc; +using isc::data::ConstElementPtr; +using isc::data::Element; + +namespace { +// Some test data +ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON( + "{ \"database_file\": \"" TEST_DATA_DIR "/test.sqlite3\"}"); +ConstElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::fromJSON( + "{ \"database_file\": \"" TEST_DATA_DIR "/example2.com.sqlite3\"}"); +ConstElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::fromJSON( + "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}"); +ConstElementPtr SQLITE_DBFILE_BROKENDB = Element::fromJSON( + "{ \"database_file\": \"" TEST_DATA_DIR "/brokendb.sqlite3\"}"); +ConstElementPtr SQLITE_DBFILE_MEMORY = Element::fromJSON( + "{ \"database_file\": \":memory:\"}"); + +// 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. +ConstElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON( + "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}"); + +// Opening works (the content is tested in different tests) +TEST(SQLite3Open, common) { + EXPECT_NO_THROW(SQLite3Connection conn(SQLITE_DBFILE_EXAMPLE)); +} + +// Missing config +TEST(SQLite3Open, noConfig) { + EXPECT_THROW(SQLite3Connection conn(Element::fromJSON("{}")), + DataSourceError); +} + +// The file can't be opened +TEST(SQLite3Open, notExist) { + EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_NOTEXIST), SQLite3Error); +} + +// It rejects broken DB +TEST(SQLite3Open, brokenDB) { + EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_BROKENDB), SQLite3Error); +} + +// Test we can create the schema on the fly +TEST(SQLite3Open, memoryDB) { + EXPECT_NO_THROW(SQLite3Connection conn(SQLITE_DBFILE_MEMORY)); +} + +} From c42eef08cd6cb28c898d46c2168c5c08684d5c36 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 2 Aug 2011 18:08:42 -0700 Subject: [PATCH 312/974] [trac904] some pre-work refactoring: replaced - with _ so that we can import the script from python (just for development convenience). also define the gen script as noinst_SCRIPTS (agian, just for development convenience). but this will cause a regression in the process_rename_test, which was fixed, too. --- configure.ac | 2 +- src/bin/auth/tests/testdata/Makefile.am | 2 +- src/bin/tests/Makefile.am | 1 + src/bin/tests/process_rename_test.py.in | 9 ++++++--- src/lib/dns/tests/testdata/Makefile.am | 7 ++++--- .../testdata/{gen-wiredata.py.in => gen_wiredata.in} | 0 src/lib/testutils/testdata/Makefile.am | 2 +- 7 files changed, 14 insertions(+), 9 deletions(-) rename src/lib/dns/tests/testdata/{gen-wiredata.py.in => gen_wiredata.in} (100%) diff --git a/configure.ac b/configure.ac index 0ede949fde..bce2821f33 100644 --- a/configure.ac +++ b/configure.ac @@ -931,7 +931,7 @@ AC_OUTPUT([doc/version.ent src/lib/python/isc/log/tests/log_console.py src/lib/dns/gen-rdatacode.py src/lib/python/bind10_config.py - src/lib/dns/tests/testdata/gen-wiredata.py + src/lib/dns/tests/testdata/gen_wiredata.py src/lib/cc/session_config.h.pre src/lib/cc/tests/session_unittests_config.h src/lib/log/tests/console_test.sh diff --git a/src/bin/auth/tests/testdata/Makefile.am b/src/bin/auth/tests/testdata/Makefile.am index f6f1f27a81..56f8e16799 100644 --- a/src/bin/auth/tests/testdata/Makefile.am +++ b/src/bin/auth/tests/testdata/Makefile.am @@ -23,4 +23,4 @@ EXTRA_DIST += example.com EXTRA_DIST += example.sqlite3 .spec.wire: - $(abs_top_builddir)/src/lib/dns/tests/testdata/gen-wiredata.py -o $@ $< + $(PYTHON) $(abs_top_builddir)/src/lib/dns/tests/testdata/gen_wiredata.py -o $@ $< diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am index b5bcea2cfe..56ff68b0c7 100644 --- a/src/bin/tests/Makefile.am +++ b/src/bin/tests/Makefile.am @@ -1,5 +1,6 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ PYTESTS = process_rename_test.py +noinst_SCRIPTS = $(PYTESTS) # .py will be generated by configure, so we don't have to include it # in EXTRA_DIST. diff --git a/src/bin/tests/process_rename_test.py.in b/src/bin/tests/process_rename_test.py.in index 4b452109bb..f96c023841 100644 --- a/src/bin/tests/process_rename_test.py.in +++ b/src/bin/tests/process_rename_test.py.in @@ -38,8 +38,10 @@ class TestRename(unittest.TestCase): Then scan them by looking at the source text (without actually running them) """ - # Regexp to find all the *_SCRIPTS = something lines, - # including line continuations (backslash and newline) + # Regexp to find all the *_SCRIPTS = something lines (except for + # noinst_SCRIPTS, which are scripts for tests), including line + # continuations (backslash and newline) + excluded_lines = re.compile(r'^(noinst_SCRIPTS.*$)', re.MULTILINE) lines = re.compile(r'^\w+_SCRIPTS\s*=\s*((.|\\\n)*)$', re.MULTILINE) # Script name regular expression @@ -53,7 +55,8 @@ class TestRename(unittest.TestCase): if 'Makefile' in fs: makefile = ''.join(open(os.path.join(d, "Makefile")).readlines()) - for (var, _) in lines.findall(makefile): + for (var, _) in lines.findall(re.sub(excluded_lines, '', + makefile)): for (script, _) in scripts.findall(var): self.__scan(d, script, fun) diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 60735e90bd..3ad8211da9 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -47,10 +47,11 @@ BUILT_SOURCES += tsig_verify4.wire tsig_verify5.wire tsig_verify6.wire BUILT_SOURCES += tsig_verify7.wire tsig_verify8.wire tsig_verify9.wire BUILT_SOURCES += tsig_verify10.wire +noinst_SCRIPTS = gen_wiredata.py + # NOTE: keep this in sync with real file listing # so is included in tarball -EXTRA_DIST = gen-wiredata.py.in -EXTRA_DIST += edns_toWire1.spec edns_toWire2.spec +EXTRA_DIST = edns_toWire1.spec edns_toWire2.spec EXTRA_DIST += edns_toWire3.spec edns_toWire4.spec EXTRA_DIST += masterload.txt EXTRA_DIST += message_fromWire1 message_fromWire2 @@ -123,4 +124,4 @@ EXTRA_DIST += tsig_verify7.spec tsig_verify8.spec tsig_verify9.spec EXTRA_DIST += tsig_verify10.spec .spec.wire: - ./gen-wiredata.py -o $@ $< + $(PYTHON) ./gen_wiredata.py -o $@ $< diff --git a/src/lib/dns/tests/testdata/gen-wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.in similarity index 100% rename from src/lib/dns/tests/testdata/gen-wiredata.py.in rename to src/lib/dns/tests/testdata/gen_wiredata.in diff --git a/src/lib/testutils/testdata/Makefile.am b/src/lib/testutils/testdata/Makefile.am index 93b9eb903c..8a86bcfc14 100644 --- a/src/lib/testutils/testdata/Makefile.am +++ b/src/lib/testutils/testdata/Makefile.am @@ -32,4 +32,4 @@ EXTRA_DIST += test2.zone.in EXTRA_DIST += test2-new.zone.in .spec.wire: - $(abs_top_builddir)/src/lib/dns/tests/testdata/gen-wiredata.py -o $@ $< + $(PYTHON) $(abs_top_builddir)/src/lib/dns/tests/testdata/gen_wiredata.py -o $@ $< From da32354d05eb22cecdf9543f542636d44e503a20 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 3 Aug 2011 13:27:30 +0800 Subject: [PATCH 313/974] [master] commit ChangeLog entry for #1128 --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index d0410df2b5..5a145584ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +277. [func] jerry + Implement the SRV rrtype according to RFC2782. + (Trac #1128, git 5fd94aa027828c50e63ae1073d9d6708e0a9c223) + 276. [func] stephen Although the top-level loggers are named after the program (e.g. b10-auth, b10-resolver), allow the logger configuration to omit the From 608d45610e9f499fb43d2e52eba461d489a7d45f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 3 Aug 2011 10:58:47 +0200 Subject: [PATCH 314/974] [trac1061] Tests for SQLite3Connection::getZone These are not copied, as this method was not public in the original implementation. The constructor got a new parameter, the RR class, so it knows what to query from the DB. --- src/lib/datasrc/sqlite3_connection.cc | 6 +- src/lib/datasrc/sqlite3_connection.h | 8 ++- .../tests/sqlite3_connection_unittest.cc | 56 +++++++++++++++++-- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index 6dd8319879..1e39d3edfd 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -45,8 +45,10 @@ struct SQLite3Parameters { }; SQLite3Connection::SQLite3Connection(const isc::data::ConstElementPtr& - config) : - dbparameters_(new SQLite3Parameters) + config, + const isc::dns::RRClass& rrclass) : + dbparameters_(new SQLite3Parameters), + class_(rrclass.toText()) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN); diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index 62d42e1156..fbb1667d35 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -24,6 +24,10 @@ #include namespace isc { +namespace dns { +class RRClass; +} + namespace datasrc { class SQLite3Error : public Exception { @@ -36,11 +40,13 @@ struct SQLite3Parameters; class SQLite3Connection : public DatabaseConnection { public: - SQLite3Connection(const isc::data::ConstElementPtr& config); + SQLite3Connection(const isc::data::ConstElementPtr& config, + const isc::dns::RRClass& rrclass); ~ SQLite3Connection(); virtual std::pair getZone(const isc::dns::Name& name) const; private: SQLite3Parameters* dbparameters_; + std::string class_; void open(const std::string& filename); void close(); }; diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index b88e986cac..3065dfe9fb 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -15,11 +15,15 @@ #include #include +#include + #include using namespace isc::datasrc; using isc::data::ConstElementPtr; using isc::data::Element; +using isc::dns::RRClass; +using isc::dns::Name; namespace { // Some test data @@ -43,28 +47,70 @@ ConstElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON( // Opening works (the content is tested in different tests) TEST(SQLite3Open, common) { - EXPECT_NO_THROW(SQLite3Connection conn(SQLITE_DBFILE_EXAMPLE)); + EXPECT_NO_THROW(SQLite3Connection conn(SQLITE_DBFILE_EXAMPLE, + RRClass::IN())); } // Missing config TEST(SQLite3Open, noConfig) { - EXPECT_THROW(SQLite3Connection conn(Element::fromJSON("{}")), + EXPECT_THROW(SQLite3Connection conn(Element::fromJSON("{}"), + RRClass::IN()), DataSourceError); } // The file can't be opened TEST(SQLite3Open, notExist) { - EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_NOTEXIST), SQLite3Error); + EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_NOTEXIST, + RRClass::IN()), SQLite3Error); } // It rejects broken DB TEST(SQLite3Open, brokenDB) { - EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_BROKENDB), SQLite3Error); + EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_BROKENDB, + RRClass::IN()), SQLite3Error); } // Test we can create the schema on the fly TEST(SQLite3Open, memoryDB) { - EXPECT_NO_THROW(SQLite3Connection conn(SQLITE_DBFILE_MEMORY)); + EXPECT_NO_THROW(SQLite3Connection conn(SQLITE_DBFILE_MEMORY, + RRClass::IN())); +} + +// Test fixture for querying the connection +class SQLite3Conn : public ::testing::Test { +public: + SQLite3Conn() { + initConn(SQLITE_DBFILE_EXAMPLE, RRClass::IN()); + } + // So it can be re-created with different data + void initConn(const ConstElementPtr& config, const RRClass& rrclass) { + conn.reset(new SQLite3Connection(config, rrclass)); + } + // The tested connection + std::auto_ptr conn; +}; + +// This zone exists in the data, so it should be found +TEST_F(SQLite3Conn, getZone) { + std::pair result(conn->getZone(Name("example.com"))); + EXPECT_TRUE(result.first); + EXPECT_EQ(1, result.second); +} + +// But it should find only the zone, nothing below it +TEST_F(SQLite3Conn, subZone) { + EXPECT_FALSE(conn->getZone(Name("sub.example.com")).first); +} + +// This zone is not there at all +TEST_F(SQLite3Conn, noZone) { + EXPECT_FALSE(conn->getZone(Name("example.org")).first); +} + +// This zone is there, but in different class +TEST_F(SQLite3Conn, noClass) { + initConn(SQLITE_DBFILE_EXAMPLE, RRClass::CH()); + EXPECT_FALSE(conn->getZone(Name("example.com")).first); } } From 823e0fcf308c7f3fc88ba48070e12bd995e75392 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 3 Aug 2011 11:14:41 +0200 Subject: [PATCH 315/974] [trac1061] Implement SQLite3Connection::getZone --- src/lib/datasrc/sqlite3_connection.cc | 32 ++++++++++++++++++++++++--- src/lib/datasrc/sqlite3_connection.h | 2 +- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index 1e39d3edfd..e850db4250 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -135,7 +135,7 @@ const char* const SCHEMA_LIST[] = { NULL }; -const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1"; +const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"; /* TODO: Prune the statements, not everything will be needed maybe? const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata " @@ -288,8 +288,34 @@ SQLite3Connection::close(void) { } std::pair -SQLite3Connection::getZone(const isc::dns::Name&) const { - return std::pair(false, 0); +SQLite3Connection::getZone(const isc::dns::Name& name) const { + int rc; + + sqlite3_reset(dbparameters_->q_zone_); + rc = sqlite3_bind_text(dbparameters_->q_zone_, 1, name.toText().c_str(), + -1, SQLITE_STATIC); + if (rc != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not bind " << name << + " to SQL statement (zone)"); + } + rc = sqlite3_bind_text(dbparameters_->q_zone_, 2, class_.c_str(), -1, + SQLITE_STATIC); + if (rc != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not bind " << class_ << + " to SQL statement (zone)"); + } + + rc = sqlite3_step(dbparameters_->q_zone_); + std::pair result; + if (rc == SQLITE_ROW) { + result = std::pair(true, + sqlite3_column_int(dbparameters_-> + q_zone_, 0)); + } else { + result = std::pair(false, 0); + } + sqlite3_reset(dbparameters_->q_zone_); + return (result); } } diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index fbb1667d35..86ad9c386b 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -46,7 +46,7 @@ public: virtual std::pair getZone(const isc::dns::Name& name) const; private: SQLite3Parameters* dbparameters_; - std::string class_; + const std::string class_; void open(const std::string& filename); void close(); }; From e47f04584b00f6d7b5c8bf9e8ae6af9aaa6831fd Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 3 Aug 2011 11:29:52 +0200 Subject: [PATCH 316/974] [trac1061] Doxygen comments for SQLite3Connection --- src/lib/datasrc/sqlite3_connection.h | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index 86ad9c386b..266dd05ea6 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -30,6 +30,13 @@ class RRClass; namespace datasrc { +/** + * \brief Low-level database error + * + * This exception is thrown when the SQLite library complains about something. + * It might mean corrupt database file, invalid request or that something is + * rotten in the library. + */ class SQLite3Error : public Exception { public: SQLite3Error(const char* file, size_t line, const char* what) : @@ -38,16 +45,63 @@ public: struct SQLite3Parameters; +/** + * \brief Concrete implementation of DatabaseConnection for SQLite3 databases + * + * This opens one database file with our schema and serves data from there. + * According to the design, it doesn't interpret the data in any way, it just + * provides unified access to the DB. + */ class SQLite3Connection : public DatabaseConnection { public: + /** + * \brief Constructor + * + * This opens the database and becomes ready to serve data from there. + * + * This might throw SQLite3Error if the given database file doesn't work + * (it is broken, doesn't exist and can't be created, etc). It might throw + * DataSourceError if the provided config is invalid (it is missing the + * database_file element). + * + * \param config The part of config describing which database file should + * be used. + * \param rrclass Which class of data it should serve (while the database + * can contain multiple classes of data, single connection can provide + * only one class). + * \todo Should we pass the database filename instead of the config? It + * might be cleaner if this class doesn't know anything about configs. + */ SQLite3Connection(const isc::data::ConstElementPtr& config, const isc::dns::RRClass& rrclass); + /** + * \brief Destructor + * + * Closes the database. + */ ~ SQLite3Connection(); + /** + * \brief Look up a zone + * + * This implements the getZone from DatabaseConnection and looks up a zone + * in the data. It looks for a zone with the exact given origin and class + * passed to the constructor. + * + * It may throw SQLite3Error if something about the database is broken. + * + * \param name The name of zone to look up + * \return The pair contains if the lookup was successful in the first + * element and the zone id in the second if it was. + */ virtual std::pair getZone(const isc::dns::Name& name) const; private: + /// \brief Private database data SQLite3Parameters* dbparameters_; + /// \brief The class for which the queries are done const std::string class_; + /// \brief Opens the database void open(const std::string& filename); + /// \brief Closes the database void close(); }; From 3702df52de21023d90052afdc54732d9ad285b39 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 3 Aug 2011 11:36:47 +0200 Subject: [PATCH 317/974] [trac1061] Logging descriptions --- src/lib/datasrc/datasrc_messages.mes | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index a6f783785c..3fbb24d05d 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -400,12 +400,22 @@ enough information for it. The code is 1 for error, 2 for not implemented. % DATASRC_SQLITE_CLOSE closing SQLite database Debug information. The SQLite data source is closing the database file. + +% DATASRC_SQLITE_CONNOPEN Opening sqlite database file '%1' +The database file is being opened so it can start providing data. + +% DATASRC_SQLITE_CONNCLOSE Closing sqlite database +The database file is no longer needed and is being closed. + % DATASRC_SQLITE_CREATE SQLite data source created Debug information. An instance of SQLite data source is being created. % DATASRC_SQLITE_DESTROY SQLite data source destroyed Debug information. An instance of SQLite data source is being destroyed. +% DATASRC_SQLITE_DROPCONN SQLite3Connection is being deinitialized +The object around a database connection is being destroyed. + % DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1' Debug information. The SQLite data source is trying to identify which zone should hold this domain. @@ -458,6 +468,9 @@ source. The SQLite data source was asked to provide a NSEC3 record for given zone. But it doesn't contain that zone. +% DATASRC_SQLITE_NEWCONN SQLite3Connection is being initialized +A wrapper object to hold database connection is being initialized. + % DATASRC_SQLITE_OPEN opening SQLite database '%1' Debug information. The SQLite data source is loading an SQLite database in the provided file. @@ -496,11 +509,3 @@ data source. % DATASRC_UNEXPECTED_QUERY_STATE unexpected query state This indicates a programming error. An internal task of unknown type was generated. - -% DATASRC_SQLITE_NEWCONN TODO - -% DATASRC_SQLITE_DROPCONN TODO - -% DATASRC_SQLITE_CONNOPEN TODO - -% DATASRC_SQLITE_CONNCLOSE TODO From c5124556a1a8907a84bb2c2bd1912da0c0aaafcc Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 3 Aug 2011 19:18:48 +0800 Subject: [PATCH 318/974] [master] a cleanup fix: missing boost namespace prefix reviewed on jabber room --- src/lib/dns/rdata/in_1/srv_33.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/rdata/in_1/srv_33.cc b/src/lib/dns/rdata/in_1/srv_33.cc index ea85779ef6..c28c43594f 100644 --- a/src/lib/dns/rdata/in_1/srv_33.cc +++ b/src/lib/dns/rdata/in_1/srv_33.cc @@ -64,7 +64,7 @@ NumType tokenToNum(const string& num_token) { NumType num; try { - num = lexical_cast(num_token); + num = boost::lexical_cast(num_token); } catch (const boost::bad_lexical_cast& ex) { isc_throw(InvalidRdataText, "Invalid SRV numeric parameter: " << num_token); From 1c5a66507b7dc2990709308979354d8e62646a28 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 3 Aug 2011 13:55:27 +0100 Subject: [PATCH 319/974] [trac1154] Initial modifications --- src/bin/zonemgr/Makefile.am | 8 +- src/bin/zonemgr/zonemgr.py.in | 63 ++++++++----- src/bin/zonemgr/zonemgr_messages.mes | 135 +++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 src/bin/zonemgr/zonemgr_messages.mes diff --git a/src/bin/zonemgr/Makefile.am b/src/bin/zonemgr/Makefile.am index 8ab5f7a0ab..d66733363a 100644 --- a/src/bin/zonemgr/Makefile.am +++ b/src/bin/zonemgr/Makefile.am @@ -6,8 +6,10 @@ pkglibexec_SCRIPTS = b10-zonemgr b10_zonemgrdir = $(pkgdatadir) b10_zonemgr_DATA = zonemgr.spec +pyexec_DATA = zonemgr_messages.py -CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec +CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec +CLEANFILES += zonemgr_messages.py zonemgr_messages.pyc man_MANS = b10-zonemgr.8 EXTRA_DIST = $(man_MANS) b10-zonemgr.xml @@ -19,6 +21,10 @@ b10-zonemgr.8: b10-zonemgr.xml endif +# Build logging source file from message files +zonemgr_messages.py: zonemgr_messages.mes + $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/zonemgr/zonemgr_messages.mes + zonemgr.spec: zonemgr.spec.pre $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.spec.pre >$@ diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index 845190b702..971b33912a 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -37,10 +37,16 @@ from isc.datasrc import sqlite3_ds from optparse import OptionParser, OptionValueError from isc.config.ccsession import * import isc.util.process +from zonemgr_messages import * # Initialize logging for called modules. -# TODO: Log messages properly isc.log.init("b10-zonemgr") +logger = isc.log.Logger("zonemgr") + +# Constants for debug levels, to be removed when we have #1074. +DBG_START_SHUT = 0 +DBG_ZONEMGR_COMMAND = 10 +DBG_ZONEMGR_BASIC = 40 isc.util.process.rename() @@ -81,13 +87,6 @@ REFRESH_OFFSET = 3 RETRY_OFFSET = 4 EXPIRED_OFFSET = 5 -# verbose mode -VERBOSE_MODE = False - -def log_msg(msg): - if VERBOSE_MODE: - sys.stdout.write("[b10-zonemgr] %s\n" % str(msg)) - class ZonemgrException(Exception): pass @@ -161,6 +160,7 @@ class ZonemgrRefresh: def zone_refresh_success(self, zone_name_class): """Update zone info after zone refresh success""" if (self._zone_not_exist(zone_name_class)): + logger.error(ZONEMGR_UNKNOWN_ZONE_SUCCESS, zone_name_class[0], zone_name_class[1]) raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't " "belong to zonemgr" % zone_name_class) self.zonemgr_reload_zone(zone_name_class) @@ -171,6 +171,7 @@ class ZonemgrRefresh: def zone_refresh_fail(self, zone_name_class): """Update zone info after zone refresh fail""" if (self._zone_not_exist(zone_name_class)): + logger.error(ZONEMGR_UNKNOWN_ZONE_FAIL, zone_name_class[0], zone_name_class[1]) raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't " "belong to zonemgr" % zone_name_class) # Is zone expired? @@ -183,6 +184,7 @@ class ZonemgrRefresh: def zone_handle_notify(self, zone_name_class, master): """Handle zone notify""" if (self._zone_not_exist(zone_name_class)): + logger.error(ZONEMGR_UNKNOWN_ZONE_NOTIFIED, zone_name_class[0], zone_name_class[1]) raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) " "doesn't belong to zonemgr" % zone_name_class) self._set_zone_notifier_master(zone_name_class, master) @@ -195,10 +197,12 @@ class ZonemgrRefresh: def zonemgr_add_zone(self, zone_name_class): """ Add a zone into zone manager.""" - log_msg("Loading zone (%s, %s)" % zone_name_class) + + logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_LOAD_ZONE, zone_name_class[0], zone_name_class[1]) zone_info = {} zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file) if not zone_soa: + logger.error(ZONEMGR_NO_SOA, zone_name_class[0], zone_name_class[1]) raise ZonemgrException("[b10-zonemgr] zone (%s, %s) doesn't have soa." % zone_name_class) zone_info["zone_soa_rdata"] = zone_soa[7] zone_info["zone_state"] = ZONE_OK @@ -269,7 +273,7 @@ class ZonemgrRefresh: except isc.cc.session.SessionTimeout: pass # for now we just ignore the failure except socket.error: - sys.stderr.write("[b10-zonemgr] Failed to send to module %s, the session has been closed." % module_name) + logger.error(ZONEMGR_SEND_FAIL, module_name) def _find_need_do_refresh_zone(self): """Find the first zone need do refresh, if no zone need @@ -278,7 +282,8 @@ class ZonemgrRefresh: zone_need_refresh = None for zone_name_class in self._zonemgr_refresh_info.keys(): zone_state = self._get_zone_state(zone_name_class) - # If hasn't received refresh response but are within refresh timeout, skip the zone + # If hasn't received refresh response but are within refresh + # timeout, skip the zone if (ZONE_REFRESHING == zone_state and (self._get_zone_refresh_timeout(zone_name_class) > self._get_current_time())): continue @@ -298,7 +303,7 @@ class ZonemgrRefresh: def _do_refresh(self, zone_name_class): """Do zone refresh.""" - log_msg("Do refresh for zone (%s, %s)." % zone_name_class) + logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_REFRESH_ZONE, zone_name_class[0], zone_name_class[1]) self._set_zone_state(zone_name_class, ZONE_REFRESHING) self._set_zone_refresh_timeout(zone_name_class, self._get_current_time() + self._max_transfer_timeout) notify_master = self._get_zone_notifier_master(zone_name_class) @@ -355,7 +360,7 @@ class ZonemgrRefresh: if e.args[0] == errno.EINTR: (rlist, wlist, xlist) = ([], [], []) else: - sys.stderr.write("[b10-zonemgr] Error with select(); %s\n" % e) + logger.error(ZONEMGR_SELECT_ERROR, e); break for fd in rlist: @@ -375,6 +380,7 @@ class ZonemgrRefresh: """ # Small sanity check if self._running: + logger.error(ZONEMGR_TIMER_ALREADY_RUNNING) raise RuntimeError("Trying to run the timers twice at the same time") # Prepare the launch @@ -399,6 +405,7 @@ class ZonemgrRefresh: called from a different thread. """ if not self._running: + logger.error(ZONEMGR_TIMER_NOT_RUNNING) raise RuntimeError("Trying to shutdown, but not running") # Ask the thread to stop @@ -560,17 +567,17 @@ class Zonemgr: # jitter should not be bigger than half of the original value if config_data.get('refresh_jitter') > 0.5: config_data['refresh_jitter'] = 0.5 - log_msg("[b10-zonemgr] refresh_jitter is too big, its value will " - "be set to 0.5") - + logger.warn(ZONEMGR_JITTER_TOO_BIG) def _parse_cmd_params(self, args, command): zone_name = args.get("zone_name") if not zone_name: + logger.error(ZONEMGR_NO_ZONE_NAME) raise ZonemgrException("zone name should be provided") zone_class = args.get("zone_class") if not zone_class: + logger.error(ZONEMGR_NO_ZONE_CLASS) raise ZonemgrException("zone class should be provided") if (command != ZONE_NOTIFY_COMMAND): @@ -578,6 +585,7 @@ class Zonemgr: master_str = args.get("master") if not master_str: + logger.error(ZONEMGR_NO_MASTER_ADDRESS) raise ZonemgrException("master address should be provided") return ((zone_name, zone_class), master_str) @@ -593,7 +601,7 @@ class Zonemgr: """ Handle Auth notify command""" # master is the source sender of the notify message. zone_name_class, master = self._parse_cmd_params(args, command) - log_msg("Received notify command for zone (%s, %s)." % zone_name_class) + logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_NOTIFY, zone_name_class[0], zone_name_class[1]) with self._lock: self._zone_refresh.zone_handle_notify(zone_name_class, master) # Send notification to zonemgr timer thread @@ -602,6 +610,7 @@ class Zonemgr: elif command == ZONE_XFRIN_SUCCESS_COMMAND: """ Handle xfrin success command""" zone_name_class = self._parse_cmd_params(args, command) + logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_SUCCESS, zone_name_class[0], zone_name_class[1]) with self._lock: self._zone_refresh.zone_refresh_success(zone_name_class) self._master_socket.send(b" ")# make self._slave_socket readble @@ -609,14 +618,17 @@ class Zonemgr: elif command == ZONE_XFRIN_FAILED_COMMAND: """ Handle xfrin fail command""" zone_name_class = self._parse_cmd_params(args, command) + logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_FAILED, zone_name_class[0], zone_name_class[1]) with self._lock: self._zone_refresh.zone_refresh_fail(zone_name_class) self._master_socket.send(b" ")# make self._slave_socket readble elif command == "shutdown": + logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_SHUTDOWN) self.shutdown() else: + logger.warn(ZONEMGR_RECEIVE_UNKNOWN, str(command)) answer = create_answer(1, "Unknown command:" + str(command)) return answer @@ -643,25 +655,30 @@ def set_cmd_options(parser): if '__main__' == __name__: try: + logger.debug(DBG_START_SHUT, ZONEMGR_STARTING) parser = OptionParser() set_cmd_options(parser) (options, args) = parser.parse_args() VERBOSE_MODE = options.verbose + if VERBOSE_MODE: + logger.set_severity("DEBUG", 99) set_signal_handler() zonemgrd = Zonemgr() zonemgrd.run() except KeyboardInterrupt: - sys.stderr.write("[b10-zonemgr] exit zonemgr process\n") + logger.info(ZONEMGR_KEYBOARD_INTERRUPT) + except isc.cc.session.SessionError as e: - sys.stderr.write("[b10-zonemgr] Error creating zonemgr, " - "is the command channel daemon running?\n") + logger.error(ZONEMGR_SESSION_ERROR) + except isc.cc.session.SessionTimeout as e: - sys.stderr.write("[b10-zonemgr] Error creating zonemgr, " - "is the configuration manager running?\n") + logger.error(ZONEMGR_SESSION_TIMEOUT) + except isc.config.ModuleCCSessionError as e: - sys.stderr.write("[b10-zonemgr] exit zonemgr process: %s\n" % str(e)) + logger.error(ZONEMGR_CCSESSION_ERROR, str(e)) if zonemgrd and zonemgrd.running: zonemgrd.shutdown() + logger.debug(DBG_START_SHUT, ZONEMGR_SHUTDOWN) diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes new file mode 100644 index 0000000000..d829a6312a --- /dev/null +++ b/src/bin/zonemgr/zonemgr_messages.mes @@ -0,0 +1,135 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# No namespace declaration - these constants go in the global namespace +# of the zonemgr messages python module. + +% ZONEMGR_CCSESSION_ERROR command channel session error: %1 +An error was encountered on the command channel. The message indicates +the nature of the error. + +% ZONEMGR_JITTER_TOO_BIG refresh_jitter is too big, setting to 0.5 +The value specified in the configuration for the refresh jitter is too large +so its value has been set to the maximum of 0.5. + +% ZONEMGR_KEYBOARD_INTERRUPT exiting zonemgr process as result of keyboard interrupt +An informational message output when the zone manager was being run at a +terminal and it was terminated via a keyboard interrupt signal. + +% ZONEMGR_LOAD_ZONE loading zone %1 (class %2) +This is a debug message indicating that the zone of the specified class +is being loaded into the zone manager. + +% ZONEMGR_NO_MASTER_ADDRESS internal BIND 10 command did not contain address of master +A command received by the zone manager from another BIND 10 module did +not contain the address of the master server from which a NOTIFY message +was received. This may be due to an internal programming error; please +submit a bug report. + +% ZONEMGR_NO_SOA zone %1 (class %2) does not have an SOA record +When loading the named zone of the specified class the zone manager +discovered that the data did not contain an SOA record. The load has +been abandoned. + +% ZONEMGR_NO_ZONE_CLASS internal BIND 10 command did not contain class of zone +A command received by the zone manager from another BIND 10 module did +not contain the class of the zone on which the zone manager should act. +This may be due to an internal programming error; please submit a +bug report. + +% ZONEMGR_NO_ZONE_NAME internal BIND 10 command did not contain name of zone +A command received by the zone manager from another BIND 10 module did +not contain the name of the zone on which the zone manager should act. +This may be due to an internal programming error; please submit a +bug report. + +% ZONEMGR_RECEIVE_NOTIFY received NOTIFY command for zone %1 (class %2) +This is a debug message indicating that the zone manager has received a +NOTIFY command over the command channel. + +% ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command +This is a debug message indicating that the zone manager has received a +SHUTDOWN command over the command channel. + +% ZONEMGR_RECEIVE_UNKNOWN received unknown command '%1' +This is a warning message indicating that the zone manager has received +the stated command over the command channel. This is not known to the +zone zone manager and although the command is ignored, its receipt may +indicate an internal error. Please submit a bug report. + +% ZONEMGR_RECEIVE_XFRIN_FAILED received XFRIN FAILED command for zone %1 (class %2) +This is a debug message indicating that the zone manager has received an +XFRIN FAILED command over the command channel. + +% ZONEMGR_RECEIVE_XFRIN_SUCCESS received XFRIN SUCCESS command for zone %1 (class %2) +This is a debug message indicating that the zone manager has received an +XFRIN SUCCESS command over the command channel. + +% ZONEMGR_REFRESH_ZONE refreshing zone %1 (class %2) +The zone manager is refreshing the named zone of the specified class +with updated information. + +% ZONEMGR_SELECT_ERROR error with select(): %1 +An attempt to wait for input from a socket failed. The failing operation +is a call to the operating system's select() function, which failed for +the given reason. + +% ZONEMGR_SEND_FAIL failed to send command to %1, session has been closed +The zone manager attempted to send a command to the named BIND 10 module, +but the send failed. The session between the modules has been closed. + +% ZONEMGR_SESSION_ERROR unable to establish session to command channel daemon +The zonemgr process was not able to be started because it could not +connect to the command channel daemon. The most usual cause of this +problem is that the daemon is not running. + +% ZONEMGR_SESSION_TIMEOUT timeout on session to command channel daemon +The zonemgr process was not able to be started because it timed out when +connecting to the command channel daemon. The most usual cause of this +problem is that the daemon is not running. + +% ZONEMGR_SHUTDOWN zone manager has shut down +A debug message, output when the zone manager has shut down completely. + +% ZONEMGR_STARTING zone manager starting +A debug message output when the zone manager starts up. + +% ZONEMGR_TIMER_ALREADY_RUNNING trying to start a timer but one is already running +This message is issued when an attempt is made to start a timer thread +but the thread is already running. It indicates either an error in the +program logic or a problem with stopping a previous instance of the timer. +Please submit a bug report. + +% ZONEMGR_TIMER_NOT_RUNNING trying to shutdown but zone manager is not running +At attempt was made to stop the zone manager's internal timer thread +but it was not running. This may indicate an internal program error. +Please submit a bug report. + +% ZONEMGR_UNKNOWN_ZONE_FAIL zone %1 (class %2) is not known to the zone manager +An XFRIN operation has failed but the zone that was the subject of the +operation is not being managed by the zone manager. This may indicate +an error in the program (as the operation should not have been initiated +if this were the case). Please submit a bug report. + +% ZONEMGR_UNKNOWN_ZONE_NOTIFY notified zone %1 (class %2) is not known to the zone manager +A NOTIFY was received but the zone that was the subject of the operation +is not being managed by the zone manager. This may indicate an error +in the program (as the operation should not have been initiated if this +were the case). Please submit a bug report. + +% ZONEMGR_UNKNOWN_ZONE_SUCCESS zone %1 (class %2) is not known to the zone manager +An XFRIN operation has succeeded but the zone received is not being +managed by the zone manager. This may indicate an error in the program +(as the operation should not have been initiated if this were the case). +Please submit a bug report. From 6ee994f190d58df863c71389bf9f8edd38d8e3eb Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 3 Aug 2011 14:50:40 +0100 Subject: [PATCH 320/974] [trac1154] Fix typo in message ID --- src/bin/zonemgr/zonemgr_messages.mes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes index d829a6312a..b23f54a151 100644 --- a/src/bin/zonemgr/zonemgr_messages.mes +++ b/src/bin/zonemgr/zonemgr_messages.mes @@ -122,7 +122,7 @@ operation is not being managed by the zone manager. This may indicate an error in the program (as the operation should not have been initiated if this were the case). Please submit a bug report. -% ZONEMGR_UNKNOWN_ZONE_NOTIFY notified zone %1 (class %2) is not known to the zone manager +% ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1 (class %2) is not known to the zone manager A NOTIFY was received but the zone that was the subject of the operation is not being managed by the zone manager. This may indicate an error in the program (as the operation should not have been initiated if this From dfc13c8130787ee07e2386773a221524ac6d802b Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 3 Aug 2011 14:51:01 +0100 Subject: [PATCH 321/974] [trac1154] Remove redundant VERBOSE_MODE variable --- src/bin/zonemgr/zonemgr.py.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index 971b33912a..00facf1dc3 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -659,8 +659,7 @@ if '__main__' == __name__: parser = OptionParser() set_cmd_options(parser) (options, args) = parser.parse_args() - VERBOSE_MODE = options.verbose - if VERBOSE_MODE: + if options.verbose: logger.set_severity("DEBUG", 99) set_signal_handler() From 96249117c97e625ec93d94939e9d75fad18ac2df Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 3 Aug 2011 15:20:05 +0100 Subject: [PATCH 322/974] [trac1154] Add message file to the makefile's EXTRA_DIST variable --- src/bin/zonemgr/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/zonemgr/Makefile.am b/src/bin/zonemgr/Makefile.am index d66733363a..34e662276d 100644 --- a/src/bin/zonemgr/Makefile.am +++ b/src/bin/zonemgr/Makefile.am @@ -12,7 +12,7 @@ CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec CLEANFILES += zonemgr_messages.py zonemgr_messages.pyc man_MANS = b10-zonemgr.8 -EXTRA_DIST = $(man_MANS) b10-zonemgr.xml +EXTRA_DIST = $(man_MANS) b10-zonemgr.xml zonemgr_messages.mes if ENABLE_MAN From 3cebb4e77088feb357b485aeeda26429f98dce9b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 3 Aug 2011 16:50:59 +0200 Subject: [PATCH 323/974] Move sockcreator.py to lib It caused trouble here, maybe this helps --- configure.ac | 3 +- src/bin/bind10/Makefile.am | 6 +--- src/bin/bind10/bind10_src.py.in | 6 ++-- src/bin/bind10/tests/Makefile.am | 2 +- src/lib/python/isc/Makefile.am | 2 +- src/lib/python/isc/bind10/Makefile.am | 4 +++ .../python/isc}/bind10/__init__.py | 0 .../python/isc}/bind10/sockcreator.py | 0 src/lib/python/isc/bind10/tests/Makefile.am | 29 +++++++++++++++++++ .../isc/bind10/tests/sockcreator_test.py} | 2 +- 10 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 src/lib/python/isc/bind10/Makefile.am rename src/{bin => lib/python/isc}/bind10/__init__.py (100%) rename src/{bin => lib/python/isc}/bind10/sockcreator.py (100%) create mode 100644 src/lib/python/isc/bind10/tests/Makefile.am rename src/{bin/bind10/tests/sockcreator_test.py.in => lib/python/isc/bind10/tests/sockcreator_test.py} (99%) diff --git a/configure.ac b/configure.ac index 0ede949fde..4ff0d07975 100644 --- a/configure.ac +++ b/configure.ac @@ -839,6 +839,8 @@ AC_CONFIG_FILES([Makefile src/lib/python/isc/notify/Makefile src/lib/python/isc/notify/tests/Makefile src/lib/python/isc/testutils/Makefile + src/lib/python/isc/bind10/Makefile + src/lib/python/isc/bind10/tests/Makefile src/lib/config/Makefile src/lib/config/tests/Makefile src/lib/config/tests/testdata/Makefile @@ -907,7 +909,6 @@ AC_OUTPUT([doc/version.ent src/bin/bind10/bind10_src.py src/bin/bind10/run_bind10.sh src/bin/bind10/tests/bind10_test.py - src/bin/bind10/tests/sockcreator_test.py src/bin/bindctl/run_bindctl.sh src/bin/bindctl/bindctl_main.py src/bin/bindctl/tests/bindctl_test diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am index 1a5ce64a54..6ab88d89ce 100644 --- a/src/bin/bind10/Makefile.am +++ b/src/bin/bind10/Makefile.am @@ -1,11 +1,7 @@ SUBDIRS = . tests sbin_SCRIPTS = bind10 -CLEANFILES = bind10 bind10_src.pyc bind10_messages.py bind10_messages.pyc \ - sockcreator.pyc - -python_PYTHON = __init__.py sockcreator.py -pythondir = $(pyexecdir)/bind10 +CLEANFILES = bind10 bind10_src.pyc bind10_messages.py bind10_messages.pyc pkglibexecdir = $(libexecdir)/@PACKAGE@ pyexec_DATA = bind10_messages.py diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index bbb17a2347..b497f7c922 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -67,7 +67,7 @@ import isc.util.process import isc.net.parse import isc.log from bind10_messages import * -import bind10.sockcreator +import isc.bind10.sockcreator isc.log.init("b10-boss") logger = isc.log.Logger("boss") @@ -337,8 +337,8 @@ class BoB: def start_creator(self): self.curproc = 'b10-sockcreator' - self.sockcreator = bind10.sockcreator.Creator("@@LIBEXECDIR@@:" + - os.environ['PATH']) + self.sockcreator = isc.bind10.sockcreator.Creator("@@LIBEXECDIR@@:" + + os.environ['PATH']) def stop_creator(self, kill=False): if self.sockcreator is None: diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index 6d758b3e0e..d9e012fdfe 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -1,7 +1,7 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ #PYTESTS = args_test.py bind10_test.py # NOTE: this has a generated test found in the builddir -PYTESTS = bind10_test.py sockcreator_test.py +PYTESTS = bind10_test.py # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am index b391c1ee81..d94100bc94 100644 --- a/src/lib/python/isc/Makefile.am +++ b/src/lib/python/isc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = datasrc cc config log net notify util testutils acl +SUBDIRS = datasrc cc config log net notify util testutils acl bind10 python_PYTHON = __init__.py diff --git a/src/lib/python/isc/bind10/Makefile.am b/src/lib/python/isc/bind10/Makefile.am new file mode 100644 index 0000000000..43a7605f7d --- /dev/null +++ b/src/lib/python/isc/bind10/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = . tests + +python_PYTHON = __init__.py sockcreator.py +pythondir = $(pyexecdir)/isc/bind10 diff --git a/src/bin/bind10/__init__.py b/src/lib/python/isc/bind10/__init__.py similarity index 100% rename from src/bin/bind10/__init__.py rename to src/lib/python/isc/bind10/__init__.py diff --git a/src/bin/bind10/sockcreator.py b/src/lib/python/isc/bind10/sockcreator.py similarity index 100% rename from src/bin/bind10/sockcreator.py rename to src/lib/python/isc/bind10/sockcreator.py diff --git a/src/lib/python/isc/bind10/tests/Makefile.am b/src/lib/python/isc/bind10/tests/Makefile.am new file mode 100644 index 0000000000..f498b86840 --- /dev/null +++ b/src/lib/python/isc/bind10/tests/Makefile.am @@ -0,0 +1,29 @@ +PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ +#PYTESTS = args_test.py bind10_test.py +# NOTE: this has a generated test found in the builddir +PYTESTS = sockcreator_test.py + +EXTRA_DIST = $(PYTESTS) + +# If necessary (rare cases), explicitly specify paths to dynamic libraries +# required by loadable python modules. +LIBRARY_PATH_PLACEHOLDER = +if SET_ENV_LIBRARY_PATH +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +endif + +# test using command-line arguments, so use check-local target instead of TESTS +check-local: +if ENABLE_PYTHON_COVERAGE + touch $(abs_top_srcdir)/.coverage + rm -f .coverage + ${LN_S} $(abs_top_srcdir)/.coverage .coverage +endif + for pytest in $(PYTESTS) ; do \ + echo Running test: $$pytest ; \ + $(LIBRARY_PATH_PLACEHOLDER) \ + env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ + BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \ + $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ + done + diff --git a/src/bin/bind10/tests/sockcreator_test.py.in b/src/lib/python/isc/bind10/tests/sockcreator_test.py similarity index 99% rename from src/bin/bind10/tests/sockcreator_test.py.in rename to src/lib/python/isc/bind10/tests/sockcreator_test.py index 53e7035e51..4453184ef5 100644 --- a/src/bin/bind10/tests/sockcreator_test.py.in +++ b/src/lib/python/isc/bind10/tests/sockcreator_test.py @@ -26,7 +26,7 @@ import socket from isc.net.addr import IPAddr import isc.log from libutil_io_python import send_fd -from bind10.sockcreator import Parser, CreatorError, WrappedSocket +from isc.bind10.sockcreator import Parser, CreatorError, WrappedSocket class FakeCreator: """ From dae8a2aabc0cc9c9f3794276676872014c5a58fa Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 3 Aug 2011 10:36:50 -0500 Subject: [PATCH 324/974] [move-sockcreator] add src/bin/sockcreator to PATH too or it will run the potentially-older installed version. --- src/bin/bind10/run_bind10.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in index bb44ca099a..b5b9721542 100755 --- a/src/bin/bind10/run_bind10.sh.in +++ b/src/bin/bind10/run_bind10.sh.in @@ -20,7 +20,7 @@ export PYTHON_EXEC 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/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:$PATH +PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH export PATH PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs: From f00712037fa4b4cbd0d677d998df3728c0c4d8fe Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 3 Aug 2011 15:48:29 -0700 Subject: [PATCH 325/974] [trac904] updated the file name for the chmod +x setting --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bce2821f33..9e8623ea85 100644 --- a/configure.ac +++ b/configure.ac @@ -965,7 +965,7 @@ AC_OUTPUT([doc/version.ent chmod +x src/bin/msgq/run_msgq.sh chmod +x src/bin/msgq/tests/msgq_test chmod +x src/lib/dns/gen-rdatacode.py - chmod +x src/lib/dns/tests/testdata/gen-wiredata.py + chmod +x src/lib/dns/tests/testdata/gen_wiredata.py chmod +x src/lib/log/tests/console_test.sh chmod +x src/lib/log/tests/destination_test.sh chmod +x src/lib/log/tests/init_logger_test.sh From 7489fa475c3f5963323a6b660e4544e48f45d37c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 09:29:25 +0200 Subject: [PATCH 326/974] [trac1160] fixed doxygen comment --- src/lib/util/strutil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/util/strutil.h b/src/lib/util/strutil.h index 1cb7bdd064..021c236b5e 100644 --- a/src/lib/util/strutil.h +++ b/src/lib/util/strutil.h @@ -175,7 +175,7 @@ std::string getToken(std::istringstream& iss); /// BitSize is the maximum number of bits that the resulting integer can take. /// This function first checks whether the given token can be converted to /// an integer of NumType type. It then confirms the conversion result is -/// within the valid range, i.e., [0, 2^NumType - 1]. The second check is +/// within the valid range, i.e., [0, 2^BitSize - 1]. The second check is /// necessary because lexical_cast where T is an unsigned integer type /// doesn't correctly reject negative numbers when compiled with SunStudio. /// From ac08c1b86b979574678aa110f19fb744719def21 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 4 Aug 2011 11:23:49 +0200 Subject: [PATCH 327/974] [trac1067] isc::NotImplemented exception --- src/lib/exceptions/exceptions.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/exceptions/exceptions.h b/src/lib/exceptions/exceptions.h index d0f1d74748..433bb7ddcd 100644 --- a/src/lib/exceptions/exceptions.h +++ b/src/lib/exceptions/exceptions.h @@ -136,6 +136,18 @@ public: isc::Exception(file, line, what) {} }; +/// +/// \brief A generic exception that is thrown when a function is +/// not implemented. +/// +/// This may be due to unfinished implementation or in case the +/// function isn't even planned to be provided for that situation. +class NotImplemented : public Exception { +public: + NotImplemented(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + /// /// A shortcut macro to insert known values into exception arguments. /// From f33ffa77fdcc3e40ec42268ea09b67ac65982f1f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 4 Aug 2011 11:24:04 +0200 Subject: [PATCH 328/974] [trac1067] Interface and default for getIterator The default throws isc::NotImplemented to allow data sources not supporting this feature. --- src/lib/datasrc/client.h | 35 ++++++++++++++++++ src/lib/datasrc/tests/Makefile.am | 1 + src/lib/datasrc/tests/client_unittest.cc | 47 ++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 src/lib/datasrc/tests/client_unittest.cc diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index 9fe6519532..67e008f2f4 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -16,12 +16,18 @@ #define __DATA_SOURCE_CLIENT_H 1 #include +#include + +#include #include namespace isc { namespace datasrc { +class ZoneIterator; +typedef boost::shared_ptr ZoneIteratorPtr; + /// \brief The base class of data source clients. /// /// This is an abstract base class that defines the common interface for @@ -143,6 +149,35 @@ public: /// \param name A domain name for which the search is performed. /// \return A \c FindResult object enclosing the search result (see above). virtual FindResult findZone(const isc::dns::Name& name) const = 0; + /// \brief Returns an iterator to the given zone + /// + /// This allows for traversing the whole zone. The returned object can + /// provide the RRsets one by one. + /// + /// This throws DataSourceError when the zone does not exist in the + /// datasource. + /// + /// The default implementation throws isc::NotImplemented. This allows + /// for easy and fast deployment of minimal custom data sources, where + /// the user/implementator doesn't have to care about anything else but + /// the actual queries. Also, in some cases, it isn't possible to traverse + /// the zone from logic point of view (eg. dynamically generated zone + /// data). + /// + /// It is not fixed if a concrete implementation of this method can throw + /// anything else. + /// + /// \param name The name of zone apex to be traversed. It doesn't do + /// nearest match as findZone. + /// \return Pointer to the iterator. + virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const { + // This is here to both document the parameter in doxygen (therefore it + // needs a name) and avoid unused parameter warning. + static_cast(name); + + isc_throw(isc::NotImplemented, + "Data source doesn't support iteration"); + } }; } } diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index c2e2b5caad..1e2dd9e20d 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -30,6 +30,7 @@ run_unittests_SOURCES += memory_datasrc_unittest.cc run_unittests_SOURCES += logger_unittest.cc run_unittests_SOURCES += database_unittest.cc run_unittests_SOURCES += sqlite3_connection_unittest.cc +run_unittests_SOURCES += client_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/datasrc/tests/client_unittest.cc b/src/lib/datasrc/tests/client_unittest.cc new file mode 100644 index 0000000000..1a88f181b3 --- /dev/null +++ b/src/lib/datasrc/tests/client_unittest.cc @@ -0,0 +1,47 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include + +#include + +using namespace isc::datasrc; +using isc::dns::Name; + +namespace { + +/* + * The DataSourceClient can't be created as it has pure virtual methods. + * So we implement them as NOPs and test the other methods. + */ +class NopClient : public DataSourceClient { +public: + virtual FindResult findZone(const isc::dns::Name&) const { + return (FindResult(result::NOTFOUND, ZoneFinderPtr())); + } +}; + +class ClientTest : public ::testing::Test { +public: + NopClient client_; +}; + +// The default implementation is NotImplemented +TEST_F(ClientTest, defaultIterator) { + EXPECT_THROW(client_.getIterator(Name(".")), isc::NotImplemented); +} + +} From 98953e35ee95489f01fbe87e55fe91d9571fcb48 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 4 Aug 2011 10:54:14 +0100 Subject: [PATCH 329/974] [trac1154] Updated messages after review --- src/bin/zonemgr/zonemgr.py.in | 19 +++++----- src/bin/zonemgr/zonemgr_messages.mes | 56 ++++++++++++++++------------ 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index 00facf1dc3..87a00921a5 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -96,7 +96,6 @@ class ZonemgrRefresh: do zone refresh. Zone timers can be started by calling run_timer(), and it can be stopped by calling shutdown() in another thread. - """ def __init__(self, cc, db_file, slave_socket, config_data): @@ -374,13 +373,14 @@ class ZonemgrRefresh: def run_timer(self, daemon=False): """ - Keep track of zone timers. Spawns and starts a thread. The thread object is returned. + Keep track of zone timers. Spawns and starts a thread. The thread object + is returned. You can stop it by calling shutdown(). """ # Small sanity check if self._running: - logger.error(ZONEMGR_TIMER_ALREADY_RUNNING) + logger.error(ZONEMGR_TIMER_THREAD_RUNNING) raise RuntimeError("Trying to run the timers twice at the same time") # Prepare the launch @@ -405,7 +405,7 @@ class ZonemgrRefresh: called from a different thread. """ if not self._running: - logger.error(ZONEMGR_TIMER_NOT_RUNNING) + logger.error(ZONEMGR_NO_TIMER_THREAD) raise RuntimeError("Trying to shutdown, but not running") # Ask the thread to stop @@ -526,8 +526,8 @@ class Zonemgr: return db_file def shutdown(self): - """Shutdown the zonemgr process. the thread which is keeping track of zone - timers should be terminated. + """Shutdown the zonemgr process. The thread which is keeping track of + zone timers should be terminated. """ self._zone_refresh.shutdown() @@ -593,9 +593,10 @@ class Zonemgr: def command_handler(self, command, args): """Handle command receivd from command channel. - ZONE_NOTIFY_COMMAND is issued by Auth process; ZONE_XFRIN_SUCCESS_COMMAND - and ZONE_XFRIN_FAILED_COMMAND are issued by Xfrin process; shutdown is issued - by a user or Boss process. """ + ZONE_NOTIFY_COMMAND is issued by Auth process; + ZONE_XFRIN_SUCCESS_COMMAND and ZONE_XFRIN_FAILED_COMMAND are issued by + Xfrin process; + shutdown is issued by a user or Boss process. """ answer = create_answer(0) if command == ZONE_NOTIFY_COMMAND: """ Handle Auth notify command""" diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes index b23f54a151..cba3331743 100644 --- a/src/bin/zonemgr/zonemgr_messages.mes +++ b/src/bin/zonemgr/zonemgr_messages.mes @@ -29,11 +29,11 @@ terminal and it was terminated via a keyboard interrupt signal. % ZONEMGR_LOAD_ZONE loading zone %1 (class %2) This is a debug message indicating that the zone of the specified class -is being loaded into the zone manager. +is being loaded. % ZONEMGR_NO_MASTER_ADDRESS internal BIND 10 command did not contain address of master -A command received by the zone manager from another BIND 10 module did -not contain the address of the master server from which a NOTIFY message +A command received by the zone manager from the Auth module did not +contain the address of the master server from which a NOTIFY message was received. This may be due to an internal programming error; please submit a bug report. @@ -42,6 +42,11 @@ When loading the named zone of the specified class the zone manager discovered that the data did not contain an SOA record. The load has been abandoned. +% ZONEMGR_NO_TIMER_THREAD trying to stop zone timer thread but it is not running +At attempt was made to stop the timer thread (used to track when zones +should be refreshed) but it was not running. This may indicate an +internal program error. Please submit a bug report. + % ZONEMGR_NO_ZONE_CLASS internal BIND 10 command did not contain class of zone A command received by the zone manager from another BIND 10 module did not contain the class of the zone on which the zone manager should act. @@ -56,25 +61,34 @@ bug report. % ZONEMGR_RECEIVE_NOTIFY received NOTIFY command for zone %1 (class %2) This is a debug message indicating that the zone manager has received a -NOTIFY command over the command channel. +NOTIFY command over the command channel. The command is sent by the Auth +process when it is acting as a slave server for the zone and causes the +zone manager to record the master server for the zone and start a timer; +when the time rexpires, the master will be polled to see if it contains +new data. % ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command -This is a debug message indicating that the zone manager has received a -SHUTDOWN command over the command channel. +This is a debug message indicating that the zone manager has received +a SHUTDOWN command over the command channel from the Boss process. +It will act on this command and shut down. % ZONEMGR_RECEIVE_UNKNOWN received unknown command '%1' This is a warning message indicating that the zone manager has received -the stated command over the command channel. This is not known to the -zone zone manager and although the command is ignored, its receipt may -indicate an internal error. Please submit a bug report. +the stated command over the command channel. The command is not known +to the zone manager and although the command is ignored, its receipt +may indicate an internal error. Please submit a bug report. % ZONEMGR_RECEIVE_XFRIN_FAILED received XFRIN FAILED command for zone %1 (class %2) -This is a debug message indicating that the zone manager has received an -XFRIN FAILED command over the command channel. +This is a debug message indicating that the zone manager has received +an XFRIN FAILED command over the command channel. The command is sent +by the Xfrin process when a transfer of zone data into the system has +failed, and causes the zone manager to schedule another transfer attempt. % ZONEMGR_RECEIVE_XFRIN_SUCCESS received XFRIN SUCCESS command for zone %1 (class %2) -This is a debug message indicating that the zone manager has received an -XFRIN SUCCESS command over the command channel. +This is a debug message indicating that the zone manager has received +an XFRIN SUCCESS command over the command channel. The command is sent +by the Xfrin process when the transfer of zone data into the system has +succeeded, and causes the data to be loaded and served by BIND 10. % ZONEMGR_REFRESH_ZONE refreshing zone %1 (class %2) The zone manager is refreshing the named zone of the specified class @@ -105,16 +119,12 @@ A debug message, output when the zone manager has shut down completely. % ZONEMGR_STARTING zone manager starting A debug message output when the zone manager starts up. -% ZONEMGR_TIMER_ALREADY_RUNNING trying to start a timer but one is already running -This message is issued when an attempt is made to start a timer thread -but the thread is already running. It indicates either an error in the -program logic or a problem with stopping a previous instance of the timer. -Please submit a bug report. - -% ZONEMGR_TIMER_NOT_RUNNING trying to shutdown but zone manager is not running -At attempt was made to stop the zone manager's internal timer thread -but it was not running. This may indicate an internal program error. -Please submit a bug report. +% ZONEMGR_TIMER_THREAD_RUNNING trying to start timer thread but one is already running +This message is issued when an attempt is made to start the timer +thread (which keeps track of when zones need a refresh) but one is +already running. It indicates either an error in the program logic or +a problem with stopping a previous instance of the timer. Please submit +a bug report. % ZONEMGR_UNKNOWN_ZONE_FAIL zone %1 (class %2) is not known to the zone manager An XFRIN operation has failed but the zone that was the subject of the From f1d52ff7171da920acc7583fa427a95386312908 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 4 Aug 2011 12:14:15 +0200 Subject: [PATCH 330/974] [trac1067] The ZoneIterator interface --- src/lib/datasrc/Makefile.am | 2 +- src/lib/datasrc/iterator.h | 60 +++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/lib/datasrc/iterator.h diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index e6bff58fea..3f8f54dfe8 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -21,7 +21,7 @@ libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc libdatasrc_la_SOURCES += zone.h libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc -libdatasrc_la_SOURCES += client.h +libdatasrc_la_SOURCES += client.h iterator.h libdatasrc_la_SOURCES += database.h database.cc libdatasrc_la_SOURCES += sqlite3_connection.h sqlite3_connection.cc nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc diff --git a/src/lib/datasrc/iterator.h b/src/lib/datasrc/iterator.h new file mode 100644 index 0000000000..3e3ce1455e --- /dev/null +++ b/src/lib/datasrc/iterator.h @@ -0,0 +1,60 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include + +namespace isc { +namespace datasrc { + +/** + * \brief Read-only iterator to a zone. + * + * You can get an instance of (descendand of) ZoneIterator from + * DataSourceClient::getIterator() method. The actual concrete implementation + * will be different depending on the actual data source used. This is the + * abstract interface. + * + * There's no way to start iterating from the beginning again or return. + */ +class ZoneIterator : public boost::noncopyable { +public: + /** + * \brief Destructor + * + * Virtual destructor. It is empty, but ensures the right destructor from + * descendant is called. + */ + virtual ~ ZoneIterator() { } + /** + * \brief Get next RRset from the zone. + * + * This returns the next RRset in the zone as a shared pointer. The + * shared pointer is used to allow both accessing in-memory data and + * automatic memory management. + * + * Any special order is not guaranteed. + * + * While this can potentially throw anything (including standard allocation + * errors), it should be rare. + * + * \return Pointer to the next RRset or NULL pointer when the iteration + * gets to the end of the zone. + */ + virtual isc::dns::ConstRRsetPtr getNextRRset() = 0; +}; + +} +} From 009d45dfbb20a54ea402e7e8f18bc2d253f41ad6 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 4 Aug 2011 12:15:24 +0200 Subject: [PATCH 331/974] [trac1067] Tests for in-memory iteration --- src/lib/datasrc/client.h | 1 + .../datasrc/tests/memory_datasrc_unittest.cc | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index 67e008f2f4..6a3936ad2c 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -25,6 +25,7 @@ namespace isc { namespace datasrc { +// The iterator.h is not included on purpose, most application won't need it class ZoneIterator; typedef boost::shared_ptr ZoneIteratorPtr; diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 22723fc76d..12f4d8d21e 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -29,6 +29,8 @@ #include #include +#include +#include #include @@ -138,6 +140,43 @@ TEST_F(InMemoryClientTest, add_find_Zone) { getOrigin()); } +TEST_F(InMemoryClientTest, iterator) { + // Just some preparations of data + boost::shared_ptr + zone(new InMemoryZoneFinder(RRClass::IN(), Name("a"))); + RRsetPtr aRRsetA(new RRset(Name("a"), RRClass::IN(), RRType::A(), + RRTTL(300))); + aRRsetA->addRdata(rdata::in::A("192.0.2.1")); + RRsetPtr aRRsetAAAA(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(), + RRTTL(300))); + aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::1")); + aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::2")); + RRsetPtr subRRsetA(new RRset(Name("sub.x.a"), RRClass::IN(), RRType::A(), + RRTTL(300))); + subRRsetA->addRdata(rdata::in::A("192.0.2.2")); + EXPECT_EQ(result::SUCCESS, memory_client.addZone( + ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), + Name("a"))))); + // First, the zone is not there, so it should throw + EXPECT_THROW(memory_client.getIterator(Name("b")), DataSourceError); + // Now, an empty zone + ZoneIteratorPtr iterator(memory_client.getIterator(Name("a"))); + EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset()); + // It throws Unexpected when we are past the end + EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected); + EXPECT_EQ(result::SUCCESS, zone->add(aRRsetA)); + EXPECT_EQ(result::SUCCESS, zone->add(aRRsetAAAA)); + EXPECT_EQ(result::SUCCESS, zone->add(subRRsetA)); + // Check it with full zone, one by one. + // It should be in ascending order in case of InMemory data source + // (isn't guaranteed in general) + iterator = memory_client.getIterator(Name("a")); + EXPECT_EQ(aRRsetA, iterator->getNextRRset()); + EXPECT_EQ(aRRsetAAAA, iterator->getNextRRset()); + EXPECT_EQ(subRRsetA, iterator->getNextRRset()); + EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset()); +} + TEST_F(InMemoryClientTest, getZoneCount) { EXPECT_EQ(0, memory_client.getZoneCount()); memory_client.addZone( From 0b98878ed8a185cbc3b78c860019416bfed317bb Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 13:29:59 +0200 Subject: [PATCH 332/974] [master] catch by reference (caught by cppcheck) --- src/lib/dns/rdata/any_255/tsig_250.cc | 2 +- src/lib/dns/rdata/in_1/srv_33.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc index aeb1b3b847..04a4dc4f7b 100644 --- a/src/lib/dns/rdata/any_255/tsig_250.cc +++ b/src/lib/dns/rdata/any_255/tsig_250.cc @@ -153,7 +153,7 @@ TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) { impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id, error, other_data); - } catch (StringTokenError ste) { + } catch (const StringTokenError& ste) { isc_throw(InvalidRdataText, "Invalid TSIG text: " << ste.what() << ": " << tsig_str); } diff --git a/src/lib/dns/rdata/in_1/srv_33.cc b/src/lib/dns/rdata/in_1/srv_33.cc index 50ae6655c2..93b5d4d60a 100644 --- a/src/lib/dns/rdata/in_1/srv_33.cc +++ b/src/lib/dns/rdata/in_1/srv_33.cc @@ -84,7 +84,7 @@ SRV::SRV(const string& srv_str) : } impl_ = new SRVImpl(priority, weight, port, targetname); - } catch (StringTokenError ste) { + } catch (const StringTokenError& ste) { isc_throw(InvalidRdataText, "Invalid SRV text: " << ste.what() << ": " << srv_str); } From 4db53f3593e24b80a33b608432ef463acbec295e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 4 Aug 2011 15:24:34 +0200 Subject: [PATCH 333/974] [trac1067] Iterating through InMemory --- src/lib/datasrc/memory_datasrc.cc | 133 +++++++++++++++--- src/lib/datasrc/memory_datasrc.h | 4 + .../datasrc/tests/memory_datasrc_unittest.cc | 6 +- 3 files changed, 118 insertions(+), 25 deletions(-) diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 26223dad90..5f7fce3b40 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -25,6 +25,8 @@ #include #include #include +#include +#include using namespace std; using namespace isc::dns; @@ -32,6 +34,27 @@ using namespace isc::dns; namespace isc { namespace datasrc { +namespace { +// Some type aliases +/* + * Each domain consists of some RRsets. They will be looked up by the + * RRType. + * + * The use of map is questionable with regard to performance - there'll + * be usually only few RRsets in the domain, so the log n benefit isn't + * much and a vector/array might be faster due to its simplicity and + * continuous memory location. But this is unlikely to be a performance + * critical place and map has better interface for the lookups, so we use + * that. + */ +typedef map Domain; +typedef Domain::value_type DomainPair; +typedef boost::shared_ptr DomainPtr; +// The tree stores domains +typedef RBTree DomainTree; +typedef RBNode DomainNode; +} + // Private data and hidden methods of InMemoryZoneFinder struct InMemoryZoneFinder::InMemoryZoneFinderImpl { // Constructor @@ -44,25 +67,6 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl { DomainPtr origin_domain(new Domain); origin_data_->setData(origin_domain); } - - // Some type aliases - /* - * Each domain consists of some RRsets. They will be looked up by the - * RRType. - * - * The use of map is questionable with regard to performance - there'll - * be usually only few RRsets in the domain, so the log n benefit isn't - * much and a vector/array might be faster due to its simplicity and - * continuous memory location. But this is unlikely to be a performance - * critical place and map has better interface for the lookups, so we use - * that. - */ - typedef map Domain; - typedef Domain::value_type DomainPair; - typedef boost::shared_ptr DomainPtr; - // The tree stores domains - typedef RBTree DomainTree; - typedef RBNode DomainNode; static const DomainNode::Flags DOMAINFLAG_WILD = DomainNode::FLAG_USER1; // Information about the zone @@ -634,7 +638,7 @@ InMemoryZoneFinder::load(const string& filename) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()). arg(filename); // Load it into a temporary tree - InMemoryZoneFinderImpl::DomainTree tmp; + DomainTree tmp; masterLoad(filename.c_str(), getOrigin(), getClass(), boost::bind(&InMemoryZoneFinderImpl::addFromLoad, impl_, _1, &tmp)); // If it went well, put it inside @@ -700,8 +704,93 @@ InMemoryClient::addZone(ZoneFinderPtr zone_finder) { InMemoryClient::FindResult InMemoryClient::findZone(const isc::dns::Name& name) const { LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_ZONE).arg(name); - return (FindResult(impl_->zone_table.findZone(name).code, - impl_->zone_table.findZone(name).zone)); + ZoneTable::FindResult result(impl_->zone_table.findZone(name)); + return (FindResult(result.code, result.zone)); } + +namespace { + +class MemoryIterator : public ZoneIterator { +private: + RBTreeNodeChain chain_; + Domain::const_iterator dom_iterator_; + const DomainTree& tree_; + const DomainNode* node_; + bool ready_; +public: + MemoryIterator(const DomainTree& tree, const Name& origin) : + tree_(tree), + ready_(true) + { + // Find the first node (origin) and preserve the node chain for future + // searches + DomainTree::Result result(tree_.find(origin, &node_, chain_, + NULL, NULL)); + // It can't happen that the origin is not in there + if (result != DomainTree::EXACTMATCH) { + isc_throw(Unexpected, + "In-memory zone corrupted, missing origin node"); + } + // Initialize the iterator if there's somewhere to point to + if (node_ != NULL && node_->getData() != DomainPtr()) { + dom_iterator_ = node_->getData()->begin(); + } + } + virtual ConstRRsetPtr getNextRRset() { + if (!ready_) { + isc_throw(Unexpected, "Iterating past the zone end"); + } + /* + * This cycle finds the first nonempty node with yet unused RRset. + * If it is NULL, we run out of nodes. If it is empty, it doesn't + * contain any RRsets. If we are at the end, just get to next one. + */ + while (node_ != NULL && (node_->getData() == DomainPtr() || + dom_iterator_ == node_->getData()->end())) { + node_ = tree_.nextNode(chain_); + // If there's a node, initialize the iterator and check next time + // if the map is empty or not + if (node_ != NULL && node_->getData() != NULL) { + dom_iterator_ = node_->getData()->begin(); + } + } + if (node_ == NULL) { + // That's all, folks + ready_ = false; + return ConstRRsetPtr(); + } + // The iterator points to the next yet unused RRset now + ConstRRsetPtr result(dom_iterator_->second); + // This one is used, move it to the next time for next call + ++ dom_iterator_; + + return (result); + } +}; + +} // End of anonymous namespace + +ZoneIteratorPtr +InMemoryClient::getIterator(const Name& name) const { + ZoneTable::FindResult result(impl_->zone_table.findZone(name)); + if (result.code != result::SUCCESS) { + isc_throw(DataSourceError, "No such zone: " + name.toText()); + } + + const InMemoryZoneFinder* + zone(dynamic_cast(result.zone.get())); + if (zone == NULL) { + /* + * TODO: This can happen only during some of the tests and only as + * a temporary solution. This should be fixed by #1159 and then + * this cast and check shouldn't be necessary. We don't have + * test for handling a "can not happen" condition. + */ + isc_throw(Unexpected, "The zone at " + name.toText() + + " is not InMemoryZoneFinder"); + } + return ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name)); +} + } // end of namespace datasrc } // end of namespace dns diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 9707797299..0f35f6eb8f 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -182,6 +182,7 @@ private: struct InMemoryZoneFinderImpl; InMemoryZoneFinderImpl* impl_; //@} + friend class InMemoryClient; }; /// \brief A data source client that holds all necessary data in memory. @@ -258,6 +259,9 @@ public: /// For other details see \c DataSourceClient::findZone(). virtual FindResult findZone(const isc::dns::Name& name) const; + /// \brief Implementation of the getIterator method + virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const; + private: // TODO: Do we still need the PImpl if nobody should manipulate this class // directly any more (it should be handled through DataSourceClient)? diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 12f4d8d21e..f47032fe66 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -154,11 +154,11 @@ TEST_F(InMemoryClientTest, iterator) { RRsetPtr subRRsetA(new RRset(Name("sub.x.a"), RRClass::IN(), RRType::A(), RRTTL(300))); subRRsetA->addRdata(rdata::in::A("192.0.2.2")); - EXPECT_EQ(result::SUCCESS, memory_client.addZone( - ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(), - Name("a"))))); + EXPECT_EQ(result::SUCCESS, memory_client.addZone(zone)); // First, the zone is not there, so it should throw EXPECT_THROW(memory_client.getIterator(Name("b")), DataSourceError); + // This zone is not there either, even when there's a zone containing this + EXPECT_THROW(memory_client.getIterator(Name("x.a")), DataSourceError); // Now, an empty zone ZoneIteratorPtr iterator(memory_client.getIterator(Name("a"))); EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset()); From 54ef8963e504e22dcf29405412a95100a210efe5 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 4 Aug 2011 16:54:04 +0200 Subject: [PATCH 334/974] [1154] Fix typo --- src/bin/zonemgr/zonemgr_messages.mes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes index cba3331743..d15ce05251 100644 --- a/src/bin/zonemgr/zonemgr_messages.mes +++ b/src/bin/zonemgr/zonemgr_messages.mes @@ -43,7 +43,7 @@ discovered that the data did not contain an SOA record. The load has been abandoned. % ZONEMGR_NO_TIMER_THREAD trying to stop zone timer thread but it is not running -At attempt was made to stop the timer thread (used to track when zones +An attempt was made to stop the timer thread (used to track when zones should be refreshed) but it was not running. This may indicate an internal program error. Please submit a bug report. From fea1f88cd0bb5bdeefc6048b122da4328635163d Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 4 Aug 2011 17:18:54 +0200 Subject: [PATCH 335/974] [1061] Address review comments Mostly comments and cleanups, some simplification of interface and change from auto_ptr to shared_ptr. --- src/lib/datasrc/database.cc | 10 ++++--- src/lib/datasrc/database.h | 24 +++++---------- src/lib/datasrc/sqlite3_connection.cc | 16 +++++----- src/lib/datasrc/sqlite3_connection.h | 18 ++++-------- src/lib/datasrc/tests/database_unittest.cc | 6 ++-- .../tests/sqlite3_connection_unittest.cc | 29 +++++-------------- 6 files changed, 38 insertions(+), 65 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 5fe9f7ba8e..2264f2c7ab 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -22,7 +22,8 @@ using isc::dns::Name; namespace isc { namespace datasrc { -DatabaseClient::DatabaseClient(std::auto_ptr connection) : +DatabaseClient::DatabaseClient(boost::shared_ptr + connection) : connection_(connection) { if (connection_.get() == NULL) { @@ -37,7 +38,7 @@ DatabaseClient::findZone(const Name& name) const { // Try exact first if (zone.first) { return (FindResult(result::SUCCESS, - ZoneFinderPtr(new Finder(*connection_, + ZoneFinderPtr(new Finder(connection_, zone.second)))); } // Than super domains @@ -46,7 +47,7 @@ DatabaseClient::findZone(const Name& name) const { zone = connection_->getZone(name.split(i)); if (zone.first) { return (FindResult(result::PARTIALMATCH, - ZoneFinderPtr(new Finder(*connection_, + ZoneFinderPtr(new Finder(connection_, zone.second)))); } } @@ -54,7 +55,8 @@ DatabaseClient::findZone(const Name& name) const { return (FindResult(result::NOTFOUND, ZoneFinderPtr())); } -DatabaseClient::Finder::Finder(DatabaseConnection& connection, int zone_id) : +DatabaseClient::Finder::Finder(boost::shared_ptr + connection, int zone_id) : connection_(connection), zone_id_(zone_id) { } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 5693479a77..8e5c1564e5 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -50,7 +50,7 @@ public: * It is empty, but needs a virtual one, since we will use the derived * classes in polymorphic way. */ - virtual ~ DatabaseConnection() { } + virtual ~DatabaseConnection() { } /** * \brief Retrieve a zone identifier * @@ -94,24 +94,14 @@ public: * * It initializes the client with a connection. * - * It throws isc::InvalidParameter if connection is NULL. It might throw + * \exception isc::InvalidParameter if connection is NULL. It might throw * standard allocation exception as well, but doesn't throw anything else. * - * \note Some objects returned from methods of this class (like ZoneFinder) - * hold references to the connection. As the lifetime of the connection - * is bound to this object, the returned objects must not be used after - * descruction of the DatabaseClient. - * - * \todo Should we use shared_ptr instead? On one side, we would get rid of - * the restriction and maybe could easy up some shutdown scenarios with - * multi-threaded applications, on the other hand it is more expensive - * and looks generally unneeded. - * * \param connection The connection to use to get data. As the parameter * suggests, the client takes ownership of the connection and will * delete it when itself deleted. */ - DatabaseClient(std::auto_ptr connection); + DatabaseClient(boost::shared_ptr connection); /** * \brief Corresponding ZoneFinder implementation * @@ -138,7 +128,7 @@ public: * DatabaseConnection::getZone and which will be passed to further * calls to the connection. */ - Finder(DatabaseConnection& connection, int zone_id); + Finder(boost::shared_ptr connection, int zone_id); virtual isc::dns::Name getOrigin() const; virtual isc::dns::RRClass getClass() const; virtual FindResult find(const isc::dns::Name& name, @@ -162,10 +152,10 @@ public: * normal applications shouldn't need it. */ const DatabaseConnection& connection() const { - return (connection_); + return (*connection_); } private: - DatabaseConnection& connection_; + boost::shared_ptr connection_; const int zone_id_; }; /** @@ -183,7 +173,7 @@ public: virtual FindResult findZone(const isc::dns::Name& name) const; private: /// \brief Our connection. - const std::auto_ptr connection_; + const boost::shared_ptr connection_; }; } diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index e850db4250..35db44620d 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -44,19 +44,14 @@ struct SQLite3Parameters { */ }; -SQLite3Connection::SQLite3Connection(const isc::data::ConstElementPtr& - config, +SQLite3Connection::SQLite3Connection(const std::string& filename, const isc::dns::RRClass& rrclass) : dbparameters_(new SQLite3Parameters), class_(rrclass.toText()) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN); - if (config && config->contains("database_file")) { - open(config->get("database_file")->stringValue()); - } else { - isc_throw(DataSourceError, "No SQLite database file specified"); - } + open(filename); } namespace { @@ -237,7 +232,7 @@ SQLite3Connection::open(const std::string& name) { initializer.move(dbparameters_); } -SQLite3Connection::~ SQLite3Connection() { +SQLite3Connection::~SQLite3Connection() { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DROPCONN); if (dbparameters_->db_ != NULL) { close(); @@ -291,6 +286,8 @@ std::pair SQLite3Connection::getZone(const isc::dns::Name& name) const { int rc; + // Take the statement (simple SELECT id FROM zones WHERE...) + // and prepare it (bind the parameters to it) sqlite3_reset(dbparameters_->q_zone_); rc = sqlite3_bind_text(dbparameters_->q_zone_, 1, name.toText().c_str(), -1, SQLITE_STATIC); @@ -305,6 +302,7 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const { " to SQL statement (zone)"); } + // Get the data there and see if it found anything rc = sqlite3_step(dbparameters_->q_zone_); std::pair result; if (rc == SQLITE_ROW) { @@ -314,7 +312,9 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const { } else { result = std::pair(false, 0); } + // Free resources sqlite3_reset(dbparameters_->q_zone_); + return (result); } diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index 266dd05ea6..484571599e 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -19,7 +19,6 @@ #include #include -#include #include @@ -59,27 +58,22 @@ public: * * This opens the database and becomes ready to serve data from there. * - * This might throw SQLite3Error if the given database file doesn't work - * (it is broken, doesn't exist and can't be created, etc). It might throw - * DataSourceError if the provided config is invalid (it is missing the - * database_file element). + * \exception SQLite3Error will be thrown if the given database file + * doesn't work (it is broken, doesn't exist and can't be created, etc). * - * \param config The part of config describing which database file should - * be used. + * \param filename The database file to be used. * \param rrclass Which class of data it should serve (while the database * can contain multiple classes of data, single connection can provide * only one class). - * \todo Should we pass the database filename instead of the config? It - * might be cleaner if this class doesn't know anything about configs. */ - SQLite3Connection(const isc::data::ConstElementPtr& config, + SQLite3Connection(const std::string& filename, const isc::dns::RRClass& rrclass); /** * \brief Destructor * * Closes the database. */ - ~ SQLite3Connection(); + ~SQLite3Connection(); /** * \brief Look up a zone * @@ -87,7 +81,7 @@ public: * in the data. It looks for a zone with the exact given origin and class * passed to the constructor. * - * It may throw SQLite3Error if something about the database is broken. + * \exception SQLite3Error if something about the database is broken. * * \param name The name of zone to look up * \return The pair contains if the lookup was successful in the first diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index b60d5c0ced..c271a76dc8 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -52,12 +52,12 @@ public: */ void createClient() { current_connection_ = new MockConnection(); - client_.reset(new DatabaseClient(auto_ptr( + client_.reset(new DatabaseClient(shared_ptr( current_connection_))); } // Will be deleted by client_, just keep the current value for comparison. MockConnection* current_connection_; - auto_ptr client_; + shared_ptr client_; /** * Check the zone finder is a valid one and references the zone ID and * connection available here. @@ -92,7 +92,7 @@ TEST_F(DatabaseClientTest, superZone) { } TEST_F(DatabaseClientTest, noConnException) { - EXPECT_THROW(DatabaseClient(auto_ptr()), + EXPECT_THROW(DatabaseClient(shared_ptr()), isc::InvalidParameter); } diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 3065dfe9fb..6d2a945a7f 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -27,23 +27,17 @@ using isc::dns::Name; namespace { // Some test data -ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON( - "{ \"database_file\": \"" TEST_DATA_DIR "/test.sqlite3\"}"); -ConstElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::fromJSON( - "{ \"database_file\": \"" TEST_DATA_DIR "/example2.com.sqlite3\"}"); -ConstElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::fromJSON( - "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}"); -ConstElementPtr SQLITE_DBFILE_BROKENDB = Element::fromJSON( - "{ \"database_file\": \"" TEST_DATA_DIR "/brokendb.sqlite3\"}"); -ConstElementPtr SQLITE_DBFILE_MEMORY = Element::fromJSON( - "{ \"database_file\": \":memory:\"}"); +std::string SQLITE_DBFILE_EXAMPLE = TEST_DATA_DIR "/test.sqlite3"; +std::string SQLITE_DBFILE_EXAMPLE2 = TEST_DATA_DIR "/example2.com.sqlite3"; +std::string SQLITE_DBFILE_EXAMPLE_ROOT = TEST_DATA_DIR "/test-root.sqlite3"; +std::string SQLITE_DBFILE_BROKENDB = TEST_DATA_DIR "/brokendb.sqlite3"; +std::string SQLITE_DBFILE_MEMORY = "memory"; // 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. -ConstElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON( - "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}"); +std::string SQLITE_DBFILE_NOTEXIST = TEST_DATA_DIR "/nodir/notexist"; // Opening works (the content is tested in different tests) TEST(SQLite3Open, common) { @@ -51,13 +45,6 @@ TEST(SQLite3Open, common) { RRClass::IN())); } -// Missing config -TEST(SQLite3Open, noConfig) { - EXPECT_THROW(SQLite3Connection conn(Element::fromJSON("{}"), - RRClass::IN()), - DataSourceError); -} - // The file can't be opened TEST(SQLite3Open, notExist) { EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_NOTEXIST, @@ -83,8 +70,8 @@ public: initConn(SQLITE_DBFILE_EXAMPLE, RRClass::IN()); } // So it can be re-created with different data - void initConn(const ConstElementPtr& config, const RRClass& rrclass) { - conn.reset(new SQLite3Connection(config, rrclass)); + void initConn(const std::string& filename, const RRClass& rrclass) { + conn.reset(new SQLite3Connection(filename, rrclass)); } // The tested connection std::auto_ptr conn; From 9f5c36321d6843ba5b2a0e9e6c10c3ffee7b14fc Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 09:18:09 -0700 Subject: [PATCH 336/974] [904] added general description of the script. also ocrrected the file name (it was given the wrong name in the first commit). --- .../{gen_wiredata.in => gen_wiredata.py.in} | 331 +++++++++++++++++- 1 file changed, 324 insertions(+), 7 deletions(-) rename src/lib/dns/tests/testdata/{gen_wiredata.in => gen_wiredata.py.in} (66%) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in similarity index 66% rename from src/lib/dns/tests/testdata/gen_wiredata.in rename to src/lib/dns/tests/testdata/gen_wiredata.py.in index 818c6e958a..9e0b4cc5f3 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -15,6 +15,320 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +Generator of various types of DNS data in the hex format. + +This script reads a human readable specification file (called "spec +file" hereafter) that defines some type of DNS data (an RDATA, an RR, +or a complete message) and dumps the defined data to a separate file +as a "wire format" sequence parsable by the +UnitTestUtil::readWireData() function (currently defined as part of +libdns++ tests). Many DNS related tests involve wire format test +data, so it will be convenient if we can define the data in a more +intuitive way than writing the entire hex sequence by hand. + +Here is a simple example. Consider the following spec file: + + [custom] + sections: a + [a] + as_rr: True + +When the script reads this file, it detects the file specifies a single +component (called "section" here) that consists of a single A RDATA, +which must be dumped as an RR (not only the part of RDATA). It then +dumps the following content: + + # A RR (QNAME=example.com Class=IN(1) TTL=86400 RDLEN=4) + 076578616d706c6503636f6d00 0001 0001 00015180 0004 + # Address=192.0.2.1 + c0000201 + +As can be seen, the script automatically completes all variable +parameters of RRs: owner name, class, TTL, RDATA length and data. For +testing purposes many of these will be the same common one (like +"example.com" or 192.0.2.1), so it would be convenient if we only have +to specify non default parameters. To change the RDATA (i.e., the +IPv4 address), we should add the following line at the end of the spec +file: + + address: 192.0.2.2 + +Then the last two lines of the output file will be as follows: + + # Address=192.0.2.2 + c0000202 + +In some cases we would rather specify malformed data for tests. This +script has the ability to specify broken parameters for many types of +data. For example, we can generate data that would look like an A RR +but the RDLEN is 3 by adding the following line to the spec file: + + rdlen: 3 + +Then the first two lines of the output file will be as follows: + + # A RR (QNAME=example.com Class=IN(1) TTL=86400 RDLEN=3) + 076578616d706c6503636f6d00 0001 0001 00015180 0003 + +** USAGE ** + + gen_wiredata.py [-o output_file] spec_file + +If the -o option is missing, and if the spec_file has a suffix (such as +in the form of "data.spec"), the output file name will be the prefix +part of it (as in "data"); if -o is missing and the spec_file does not +have a suffix, the script will fail. + +** SPEC FILE SYNTAX ** + +A spec file accepted in this script should be in the form of a +configuration file that is parsable by the Python's standard +configparser module. In short, it consists of sections; each section +is identified in the form of [section_name] followed by "name: value" +entries. Lines beginning with # or ; will be treated as comments. +Refer to the configparser module documentation for further details of +the general syntax. + +This script has two major modes: the custom mode and the DNS query +mode. The former generates an arbitrary combination of DNS message +header, question section, RDATAs or RRs. It is mainly intended to +generate a test data for a single type of RDATA or RR, or for +complicated complete DNS messages. The DNS query mode is actually a +special case of the custom mode, which is a shortcut to generate a +simple DNS query message (with or without EDNS). + +* Custom mode syntax * + +By default this script assumes the DNS query mode. To specify the +custom mode, there must be a special "custom" section in the spec +file, which should contain 'sections' entry. This value of this +entryis colon-separated string fields, each of which is either +"header", "question", "edns", "name", or a string specifying an RR +type. For RR types the string is lower-cased string mnemonic that +identifies the type: 'a' for type A, 'ns' for type NS, and so on +(note: in the current implementation it's case sensitive, and must be +lower cased). + +Each of these fields is interpreted as a section name of the spec +(configuration), and in that section parameters specific to the +semantics of the field can be configured. + +A "header" section specifies the content of a DNS message header. +See the documentation of the DNSHeader class of this module for +configurable parameters. + +A "question" section specifies the content of a single question that +is normally to be placed in the Question section of a DNS message. +See the documentation of the DNSQuestion class of this module for +configurable parameters. + +An "edns" section specifies the content of an EDNS OPT RR. See the +documentation of the EDNS class of this module for configurable +parameters. + +A "name" section specifies a domain name with or without compression. +This is specifically intended to be used for testing name related +functionalities and would rarely be used with other sections. See the +documentation of the Name class of this module for configurable +parameters. + +In a specific section for an RR or RDATA, possible entries depend on +the type. But there are some common configurable entries. See the +description of the RR class. The most important one would be "as_rr". +It controls whether the entry should be treated as an RR (with name, +type, class and TTL) or only as an RDATA. By default as_rr is +"False", so if an entry is to be intepreted as an RR, an as_rr entry +must be explicitly specified with a value of "True". + +Another common entry is "rdlen". It specifies the RDLEN field value +of the RR (note: this is included when the entry is interpreted as +RDATA, too). By default this value is automatically determined by the +RR type and (it has a variable length) from other fields of RDATA, but +as shown in the above example, it can be explicitly set, possibly to a +bogus value for testing against invalid data. + +For type specific entries (and their defaults when provided), see the +documentation of the corresponding Python class defined in this +module. In general, there should be a class named the same mnemonic +of the corresponding RR type for each supported type, and they are a +subclass of the RR class. For example, the "NS" class is defined for +RR type NS. + +Look again at the A RR example shown at the beginning of this +description. There's a "custom" section, which consists of a +"sections" entry whose value is a single "a", which means the data to +be generated is an A RR or RDATA. There's a corresponding "a" +section, which only specifies that it should be interpreted as an RR +(all field values of the RR are derived from the default). + +If you want to generate a data sequence for two ore more RRs or +RDATAs, you can specify them in the form of colon-separated fields for +the "sections" entry. For example, to generate a sequence of A and NS +RRs in that order, the "custom" section would be something like this: + + [custom] + sections: a:ns + +and there must be an "ns" section in addtion to "a". + +If a sequence of two or more RRs/RDATAs of the same RR type should be +generated, these should be uniquely indexed with the "/" separator. +For example, to generate two A RRs, the "custom" section would be as +follows: + + [custom] + sections: a/1:a/2 + +and there must be "a/1" and "a/2" sections. + +Another practical example that would be used for many tests is to +generate data for a complete DNS ressponse message. The spec file of +such an example configuration would look like as follows: + + [custom] + sections: header:question:a + [header] + qr: 1 + ancount: 1 + [question] + [a] + as_rr: True + +With this configuration, this script will generate test data for a DNS +response to a query for example.com/IN/A containing one corresponding +A RR in the answer section. + +* DNS query mode syntax * + +If the spec file does not contain a "custom" section (that has a +"sections" entry), this script assumes the DNS query mode. This mode +is actually a special case of custom mode; it implicitly assumes the +"sections" entry whose value is "header:question:edns". + +In this mode it is expected that the spec file also contains at least +a "header" and "question" sections, and optionally an "edns" section. +But the script does not warn or fail even if the expected sections are +missing. + +* Entry value types * + +As described above, a section of the spec file accepts entries +specific to the semantics of the section. They generally correspond +to DNS message or RR fields. + +Many of them are expected to be integral values, for which either decimal or +hexadecimal representation is accepted, for example: + + rr_ttl: 3600 + tag: 0x1234 + +Some others are expected to be string. A string value does not have +to be quated: + + address: 192.0.2.2 + +but can also be quoated with single quotes: + + address: '192.0.2.2' + +Note 1: a string that can be interpreted as an integer must be quated. +For example, if you want to set a "string" entry to "3600", it should +be: + + string: '3600' + +instead of + + string: 3600 + +Note 2: a string enclosed with double quotes is not accepted: + + # This doesn't work: + address: "192.0.2.2" + +In general, string values are converted to hexadecimal sequences +according to the semantics of the entry. For instance, a textual IPv4 +address in the above example will be converted to a hexadecimal +sequence corresponding to a 4-byte integer. So, in many cases, the +acceptable syntax for a particular string entry value should be +obvious from the context. There are still some exceptional cases +especially for complicated RR field values, for which the +corresponding class documentation should be referenced. + +One special string syntax that would be worth noting is domain names, +which would natually be used in many kinds of entries. The simplest +form of acceptable syntax is a textual representation of domain names +such as "example.com" (note: names are always assumed to be +"absolute", so the trailing dot can be omitted). But a domain name in +the wire format can also contain a compression pointer. This script +provides a simple support for name compression with a special notation +of "ptr=nn" where nn is the numeric pointer value (decimal). For example, +if the NSDNAME field of an NS RDATA is specified as follows: + + nsname: ns.ptr=12 + +this script will generate the following output: + + # NS name=ns.ptr=12 + 026e73c00c + +** EXTEND THE SCRIPT ** + +This script is expected to be extended as we add more support for +various types of RR. It is encouraged to add support for a new type +of RR to this script as we see the need for testing that type. Here +is a simple instruction of how to do that. + +Assume you are adding support for "FOO" RR. Also assume that the FOO +RDATA contains a single field named "value". + +What you are expected to do is as follows: + +- Add a dictionary entry of "'foo': (FOO, {})" to config_param of the + get_config_param() function (this step could be automated; we may do + it in a future version) + +- Define a new class named "FOO" inherited from the RR class. Also + define a class variable named "value" for the FOO RDATA field (the + variable name can be different from the field name, but it's + convenient if it can be easily identifiable.) with an appropriate + default value (if possible): + + class FOO(RR): + value = 10 + + The name of the variable will be (automatically) used as the + corresponding entry name in the spec file. So, a spec file that + sets this field to 20 would look like this: + + [foo] + value: 20 + +- Define the "dump()" method for class FOO. It must call + self.dump_header() (which is derived from class RR) at the + beginning. It then prints the RDATA field values in an appropriate + way. Assuming the value is a 16-bit integer field, a complete + dump() method would look like this: + + def dump(self, f): + if self.rdlen is None: + self.rdlen = 2 + self.dump_header(f, self.rdlen) + f.write('# Value=%d\\n' % (self.value)) + f.write('%04x\\n' % (self.value)) + + The first f.write() call is not mandatory, but is encouraged to + provide so that the generated files will be more human readable. + Depending on the complexity of the RDATA fields, the dump() + implementation would be more complicated. In particular, if the + RDATA length is variable and the RDLEN field value is not specified + in the spec file, the dump() method is normally expected to + calculate the correct length and pass it to dump_header(). See the + implementation of various derived classes of class RR for actual + examples. +""" + import configparser, re, time, socket, sys from datetime import datetime from optparse import OptionParser @@ -231,12 +545,15 @@ class RR: derived class for the RR type SOA should be named "SOA". Configurable parameters are as follows: - - as_rr (bool): Whether or not the data is to be dumped as an RR. False - by default. - - rr_class (string): The RR class of the data. Only meaningful when the - data is dumped as an RR. Default is 'IN'. - - rr_ttl (integer): The TTL value of the RR. Only meaningful when the - data is dumped as an RR. Default is 86400 (1 day). + - as_rr (bool): Whether or not the data is to be dumped as an RR. + False by default. + - rr_name (string): The owner name of the RR. The string must be + interpreted as a valid domain name (compression pointer can be + contained). Default is 'example.com.' + - rr_class (string): The RR class of the data. Only meaningful + when the data is dumped as an RR. Default is 'IN'. + - rr_ttl (integer): The TTL value of the RR. Only meaningful when + the data is dumped as an RR. Default is 86400 (1 day). ''' def __init__(self): @@ -595,7 +912,7 @@ if __name__ == "__main__": print_header(output, configfile) - # First try the 'custom' mode; if it fails assume the standard mode. + # First try the 'custom' mode; if it fails assume the query mode. try: sections = config.get('custom', 'sections').split(':') except configparser.NoSectionError: From 7fbc6a734b2a9e33100e57cbea0ce1d20cdf4491 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 09:52:43 -0700 Subject: [PATCH 337/974] [904] added one simple RR class (AAAA) to confirm the documentation is correct about extending the script. --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index 9e0b4cc5f3..7d58192268 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -588,6 +588,17 @@ class A(RR): f.write('%02x%02x%02x%02x\n' % (bin_address[0], bin_address[1], bin_address[2], bin_address[3])) +class AAAA(RR): + rdlen = 16 + address = '2001:db8::1' + + def dump(self, f): + self.dump_header(f, self.rdlen) + f.write('# Address=%s\n' % (self.address)) + bin_address = socket.inet_pton(socket.AF_INET6, self.address) + [f.write('%02x' % x) for x in bin_address] + f.write('\n') + class NS(RR): rdlen = None # auto calculate nsname = 'ns.example.com' @@ -874,7 +885,7 @@ def get_config_param(section): 'header' : (DNSHeader, header_xtables), 'question' : (DNSQuestion, question_xtables), 'edns' : (EDNS, {}), 'a' : (A, {}), 'ns' : (NS, {}), - 'soa' : (SOA, {}), 'txt' : (TXT, {}), + 'soa' : (SOA, {}), 'txt' : (TXT, {}), 'aaaa' : (AAAA, {}), 'rp' : (RP, {}), 'rrsig' : (RRSIG, {}), 'nsec' : (NSEC, {}), 'nsec3' : (NSEC3, {}), 'tsig' : (TSIG, {}) } From b34e7172b5f663faf3add7f6e72a3e2d8ffe680a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 10:26:44 -0700 Subject: [PATCH 338/974] [904] small refactoring: make RP a derived class of RR (for consistency and reducing code duplicate); move the configuration of rdlen to RR class; omitting RDLEN is supported at that level. --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index 7d58192268..3037c2a645 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -552,8 +552,15 @@ class RR: contained). Default is 'example.com.' - rr_class (string): The RR class of the data. Only meaningful when the data is dumped as an RR. Default is 'IN'. - - rr_ttl (integer): The TTL value of the RR. Only meaningful when + - rr_ttl (int): The TTL value of the RR. Only meaningful when the data is dumped as an RR. Default is 86400 (1 day). + - rdlen (int): 16-bit RDATA length. It can be None (i.e. omitted + in the spec file), in which case the actual length of the + generated RDATA is automatically determined and used; if + negative, the RDLEN field will be omitted from the output data. + (Note that omitting RDLEN with as_rr being True is mostly + meaningless, although the script doesn't complain about it). + Default is None. ''' def __init__(self): @@ -562,26 +569,36 @@ class RR: self.rr_name = 'example.com' self.rr_class = 'IN' self.rr_ttl = 86400 + self.rdlen = None + def dump_header(self, f, rdlen): type_txt = self.__class__.__name__ type_code = parse_value(type_txt, dict_rrtype) + rdlen_spec = '' + rdlen_data = '' + if rdlen >= 0: + rdlen_spec = ', RDLEN=%d' % rdlen + rdlen_data = ' %04x' % rdlen if self.as_rr: rrclass = parse_value(self.rr_class, dict_rrclass) - f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d RDLEN=%d)\n' % + f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d%s)\n' % (type_txt, self.rr_name, - code_totext(rrclass, rdict_rrclass), self.rr_ttl, rdlen)) - f.write('%s %04x %04x %08x %04x\n' % + code_totext(rrclass, rdict_rrclass), self.rr_ttl, + rdlen_spec)) + f.write('%s %04x %04x %08x%s\n' % (encode_name(self.rr_name), type_code, rrclass, - self.rr_ttl, rdlen)) + self.rr_ttl, rdlen_data)) else: - f.write('\n# %s RDATA (RDLEN=%d)\n' % (type_txt, rdlen)) - f.write('%04x\n' % rdlen) + f.write('\n# %s RDATA%s\n' % (type_txt, rdlen_spec)) + f.write('%s\n' % rdlen_data) class A(RR): - rdlen = 4 # fixed by default + RDLEN_DEFAULT = 4 # fixed by default address = '192.0.2.1' def dump(self, f): + if self.rdlen is None: + self.rdlen = self.RDLEN_DEFAULT self.dump_header(f, self.rdlen) f.write('# Address=%s\n' % (self.address)) bin_address = socket.inet_aton(self.address) @@ -589,10 +606,12 @@ class A(RR): bin_address[2], bin_address[3])) class AAAA(RR): - rdlen = 16 + RDLEN_DEFAULT = 16 # fixed by default address = '2001:db8::1' def dump(self, f): + if self.rdlen is None: + self.rdlen = self.RDLEN_DEFAULT self.dump_header(f, self.rdlen) f.write('# Address=%s\n' % (self.address)) bin_address = socket.inet_pton(socket.AF_INET6, self.address) @@ -600,7 +619,6 @@ class AAAA(RR): f.write('\n') class NS(RR): - rdlen = None # auto calculate nsname = 'ns.example.com' def dump(self, f): @@ -612,7 +630,6 @@ class NS(RR): f.write('%s\n' % nsname_wire) class SOA(RR): - rdlen = None # auto-calculate mname = 'ns.example.com' rname = 'root.example.com' serial = 2010012601 @@ -636,7 +653,6 @@ class SOA(RR): self.minimum)) class TXT(RR): - rdlen = None # auto-calculate nstring = 1 # number of character-strings stringlen = -1 # default string length, auto-calculate string = 'Test String' # default string @@ -668,17 +684,12 @@ class TXT(RR): ' ' if len(wirestring_list[i]) > 0 else '', wirestring_list[i])) -class RP: +class RP(RR): '''Implements rendering RP RDATA in the wire format. Configurable parameters are as follows: - - rdlen: 16-bit RDATA length. If omitted, the accurate value is auto - calculated and used; if negative, the RDLEN field will be omitted from - the output data. - - mailbox: The mailbox field. - - text: The text field. - All of these parameters have the default values and can be omitted. + - mailbox: The mailbox field. Default is 'root.example.com' + - text: The text field. Default is 'rp-text.example.com' ''' - rdlen = None # auto-calculate mailbox = 'root.example.com' text = 'rp-text.example.com' def dump(self, f): @@ -688,11 +699,7 @@ class RP: self.rdlen = (len(mailbox_wire) + len(text_wire)) / 2 else: self.rdlen = int(self.rdlen) - if self.rdlen >= 0: - f.write('\n# RP RDATA (RDLEN=%d)\n' % self.rdlen) - f.write('%04x\n' % self.rdlen) - else: - f.write('\n# RP RDATA (RDLEN omitted)\n') + self.dump_header(f, self.rdlen) f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text)) f.write('%s %s\n' % (mailbox_wire, text_wire)) @@ -824,7 +831,6 @@ class RRSIG: f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire)) class TSIG(RR): - rdlen = None # auto-calculate algorithm = 'hmac-sha256' time_signed = 1286978795 # arbitrarily chosen default fudge = 300 From 928dacfdf443393618edf7124a46c599bd760784 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 10:35:57 -0700 Subject: [PATCH 339/974] [904] made RRSIG a derived class of RR --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index 3037c2a645..e9f795a88d 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -794,8 +794,7 @@ class NSEC3(NSECBASE): ' ' if len(self.hash) > 0 else '', encode_string(self.hash))) -class RRSIG: - rdlen = -1 # auto-calculate +class RRSIG(RR): covered = 1 # A algorithm = 5 # RSA-SHA1 labels = -1 # auto-calculate (#labels of signer) @@ -810,20 +809,18 @@ class RRSIG: def dump(self, f): name_wire = encode_name(self.signer) sig_wire = '%x' % self.signature - rdlen = self.rdlen - if rdlen < 0: - rdlen = int(18 + len(name_wire) / 2 + len(str(sig_wire)) / 2) - labels = self.labels - if labels < 0: - labels = count_namelabels(self.signer) - f.write('\n# RRSIG RDATA (RDLEN=%d)\n' % rdlen) - f.write('%04x\n' % rdlen); + if self.rdlen is None: + self.rdlen = int(18 + len(name_wire) / 2 + len(str(sig_wire)) / 2) + self.dump_header(f, self.rdlen) + + if self.labels < 0: + self.labels = count_namelabels(self.signer) f.write('# Covered=%s Algorithm=%s Labels=%d OrigTTL=%d\n' % (code_totext(self.covered, rdict_rrtype), - code_totext(self.algorithm, rdict_algorithm), labels, + code_totext(self.algorithm, rdict_algorithm), self.labels, self.originalttl)) f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm, - labels, self.originalttl)) + self.labels, self.originalttl)) f.write('# Expiration=%s, Inception=%s\n' % (str(self.expiration), str(self.inception))) f.write('%08x %08x\n' % (self.expiration, self.inception)) From d7713e5c5033ccb0b51769d7f28d91619655b24d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 10:47:07 -0700 Subject: [PATCH 340/974] [904] made NSEC variants a derived class of RR (with some editorial cleanups) --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index e9f795a88d..c560d9c611 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -319,7 +319,7 @@ What you are expected to do is as follows: f.write('%04x\\n' % (self.value)) The first f.write() call is not mandatory, but is encouraged to - provide so that the generated files will be more human readable. + be provided so that the generated files will be more human readable. Depending on the complexity of the RDATA fields, the dump() implementation would be more complicated. In particular, if the RDATA length is variable and the RDLEN field value is not specified @@ -578,14 +578,14 @@ class RR: rdlen_data = '' if rdlen >= 0: rdlen_spec = ', RDLEN=%d' % rdlen - rdlen_data = ' %04x' % rdlen + rdlen_data = '%04x' % rdlen if self.as_rr: rrclass = parse_value(self.rr_class, dict_rrclass) f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d%s)\n' % (type_txt, self.rr_name, code_totext(rrclass, rdict_rrclass), self.rr_ttl, rdlen_spec)) - f.write('%s %04x %04x %08x%s\n' % + f.write('%s %04x %04x %08x %s\n' % (encode_name(self.rr_name), type_code, rrclass, self.rr_ttl, rdlen_data)) else: @@ -703,7 +703,7 @@ class RP(RR): f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text)) f.write('%s %s\n' % (mailbox_wire, text_wire)) -class NSECBASE: +class NSECBASE(RR): '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for these RRs. The NSEC and NSEC3 classes will be inherited from this class.''' @@ -747,7 +747,6 @@ class NSECBASE: (block_list[i], maplen_list[i], bitmap_list[i])) class NSEC(NSECBASE): - rdlen = None # auto-calculate nextname = 'next.example.com' def dump_fixedpart(self, f, bitmap_totallen): name_wire = encode_name(self.nextname) @@ -755,14 +754,12 @@ class NSEC(NSECBASE): # if rdlen needs to be calculated, it must be based on the bitmap # length, because the configured maplen can be fake. self.rdlen = int(len(name_wire) / 2) + bitmap_totallen - f.write('\n# NSEC RDATA (RDLEN=%d)\n' % self.rdlen) - f.write('%04x\n' % self.rdlen); + self.dump_header(f, self.rdlen) f.write('# Next Name=%s (%d bytes)\n' % (self.nextname, int(len(name_wire) / 2))) f.write('%s\n' % name_wire) class NSEC3(NSECBASE): - rdlen = None # auto-calculate hashalg = 1 # SHA-1 optout = False # opt-out flag mbz = 0 # other flag fields (none defined yet) @@ -777,8 +774,7 @@ class NSEC3(NSECBASE): # length, because the configured maplen can be fake. self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \ + bitmap_totallen - f.write('\n# NSEC3 RDATA (RDLEN=%d)\n' % self.rdlen) - f.write('%04x\n' % self.rdlen) + self.dump_header(f, self.rdlen) optout_val = 1 if self.optout else 0 f.write('# Hash Alg=%s, Opt-Out=%d, Other Flags=%0x, Iterations=%d\n' % (code_totext(self.hashalg, rdict_nsec3_algorithm), From 1feeca7c2209819dd181f1fbaaa75026d3e38aa2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 15:43:28 -0700 Subject: [PATCH 341/974] [904] documented RR class configurations --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 225 ++++++++++++++++-- 1 file changed, 205 insertions(+), 20 deletions(-) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index c560d9c611..62d6a26d28 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -312,11 +312,11 @@ What you are expected to do is as follows: dump() method would look like this: def dump(self, f): - if self.rdlen is None: - self.rdlen = 2 - self.dump_header(f, self.rdlen) - f.write('# Value=%d\\n' % (self.value)) - f.write('%04x\\n' % (self.value)) + if self.rdlen is None: + self.rdlen = 2 + self.dump_header(f, self.rdlen) + f.write('# Value=%d\\n' % (self.value)) + f.write('%04x\\n' % (self.value)) The first f.write() call is not mandatory, but is encouraged to be provided so that the generated files will be more human readable. @@ -375,7 +375,6 @@ rdict_nsec3_algorithm = dict([(dict_nsec3_algorithm[k], k.upper()) for k in \ header_xtables = { 'qr' : dict_qr, 'opcode' : dict_opcode, 'rcode' : dict_rcode } question_xtables = { 'rrtype' : dict_rrtype, 'rrclass' : dict_rrclass } -rrsig_xtables = { 'algorithm' : dict_algorithm } def parse_value(value, xtable = {}): if re.search(re_hex, value): @@ -593,6 +592,13 @@ class RR: f.write('%s\n' % rdlen_data) class A(RR): + '''Implements rendering A RDATA (of class IN) in the test data format. + + Configurable parameter is as follows (see the description of the + same name of attribute for the default value): + - address (string): The address field. This must be a valid textual + IPv4 address. + ''' RDLEN_DEFAULT = 4 # fixed by default address = '192.0.2.1' @@ -606,6 +612,14 @@ class A(RR): bin_address[2], bin_address[3])) class AAAA(RR): + '''Implements rendering AAAA RDATA (of class IN) in the test data + format. + + Configurable parameter is as follows (see the description of the + same name of attribute for the default value): + - address (string): The address field. This must be a valid textual + IPv6 address. + ''' RDLEN_DEFAULT = 16 # fixed by default address = '2001:db8::1' @@ -619,6 +633,14 @@ class AAAA(RR): f.write('\n') class NS(RR): + '''Implements rendering NS RDATA in the test data format. + + Configurable parameter is as follows (see the description of the + same name of attribute for the default value): + - nsname (string): The NSDNAME field. The string must be + interpreted as a valid domain name. + ''' + nsname = 'ns.example.com' def dump(self, f): @@ -630,6 +652,19 @@ class NS(RR): f.write('%s\n' % nsname_wire) class SOA(RR): + '''Implements rendering SOA RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - mname/rname (string): The MNAME/RNAME fields, respectively. The + string must be interpreted as a valid domain name. + - serial (32-bit int): The SERIAL field + - refresh (32-bit int): The REFRESH field + - retry (32-bit int): The RETRY field + - expire (32-bit int): The EXPIRE field + - minimum (32-bit int): The MINIMUM field + ''' + mname = 'ns.example.com' rname = 'root.example.com' serial = 2010012601 @@ -653,9 +688,30 @@ class SOA(RR): self.minimum)) class TXT(RR): - nstring = 1 # number of character-strings - stringlen = -1 # default string length, auto-calculate - string = 'Test String' # default string + '''Implements rendering TXT RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - nstring (int): number of character-strings + - stringlenN (int) (int, N = 0, ..., nstring-1): the length of the + N-th character-string. + - stringN (string, N = 0, ..., nstring-1): the N-th + character-string. + - stringlen (int): the default string. If nstring >= 1 and the + corresponding stringlenN isn't specified in the spec file, this + value will be used. If this parameter isn't specified either, + the length of the string will be used. Note that it means + this parameter (or any stringlenN) doesn't have to be specified + unless you want to intentially build a broken character string. + - string (string): the default string. If nstring >= 1 and the + corresponding stringN isn't specified in the spec file, this + string will be used. + ''' + + nstring = 1 + stringlen = None + string = 'Test String' + def dump(self, f): stringlen_list = [] string_list = [] @@ -672,7 +728,7 @@ class TXT(RR): stringlen_list.append(self.__dict__[key_stringlen]) else: stringlen_list.append(self.stringlen) - if stringlen_list[-1] < 0: + if stringlen_list[-1] is None: stringlen_list[-1] = int(len(wirestring_list[-1]) / 2) if self.rdlen is None: self.rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring @@ -685,10 +741,13 @@ class TXT(RR): wirestring_list[i])) class RP(RR): - '''Implements rendering RP RDATA in the wire format. - Configurable parameters are as follows: - - mailbox: The mailbox field. Default is 'root.example.com' - - text: The text field. Default is 'rp-text.example.com' + '''Implements rendering RP RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - mailbox (string): The mailbox field. + - text (string): The text field. + These strings must be interpreted as a valid domain name. ''' mailbox = 'root.example.com' text = 'rp-text.example.com' @@ -706,7 +765,26 @@ class RP(RR): class NSECBASE(RR): '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for these RRs. The NSEC and NSEC3 classes will be inherited from this - class.''' + class. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - nbitmap (int): The number of type bitmaps. + The following three define the bitmaps. If suffixed with "N" + (0 <= N < nbitmaps), it means the definition for the N-th bitmap. + If there is no suffix (e.g., just "block", it means the default + for any unspecified values) + - block[N] (8-bit int): The Window Block. + - maplen[N] (8-bit int): The Bitmap Length. The default "maplen" + can also be unspecified (with being set to None), in which case + the corresponding length will be calculated from the bitmap. + - bitmap[N] (string): The Bitmap. This must be the hexadecimal + representation of the bitmap field. For example, for a bitmap + where the 7th and 15th bits (and only these bits) are set, it + must be '0101'. Note also that the value must be quated with + single quatations because it could also be interpreted as an + integer. + ''' nbitmap = 1 # number of bitmaps block = 0 maplen = None # default bitmap length, auto-calculate @@ -747,6 +825,15 @@ class NSECBASE(RR): (block_list[i], maplen_list[i], bitmap_list[i])) class NSEC(NSECBASE): + '''Implements rendering NSEC RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - Type bitmap related parameters: see class NSECBASE + - nextname (string): The Next Domain Name field. The string must be + interpreted as a valid domain name. + ''' + nextname = 'next.example.com' def dump_fixedpart(self, f, bitmap_totallen): name_wire = encode_name(self.nextname) @@ -760,6 +847,28 @@ class NSEC(NSECBASE): f.write('%s\n' % name_wire) class NSEC3(NSECBASE): + '''Implements rendering NSEC3 RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - Type bitmap related parameters: see class NSECBASE + - hashalg (8-bit int): The Hash Algorithm field. Note that + currently the only defined algorithm is SHA-1, for which a value + of 1 will be used, and it's the default. So this implementation + does not support any string representation right now. + - optout (bool): The Opt-Out flag of the Flags field. + - mbz (7-bit int): The rest of the Flags field. This value will + be left shifted for 1 bit and then OR-ed with optout to + construct the complete Flags field. + - iterations (16-bit int): The Iterations field. + - saltlen (int): The Salt Length field. + - salt (string): The Salt field. It is converted to a sequence of + ascii codes and its hexadecimal representation will be used. + - hashlen (int): The Hash Length field. + - hash (string): The Next Hashed Owner Name field. This parameter + is interpreted as "salt". + ''' + hashalg = 1 # SHA-1 optout = False # opt-out flag mbz = 0 # other flag fields (none defined yet) @@ -791,9 +900,37 @@ class NSEC3(NSECBASE): encode_string(self.hash))) class RRSIG(RR): - covered = 1 # A - algorithm = 5 # RSA-SHA1 - labels = -1 # auto-calculate (#labels of signer) + '''Implements rendering RRSIG RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - covered (int or string): The Type Covered field. If specified + as an integer, it must be the 16-bit RR type value of the + covered type. If specifed as a string, it must be the textual + mnemonic of the type. + - algorithm (int or string): The Algorithm field. If specified + as an integer, it must be the 8-bit algorithm number as defined + in RFC4034. If specifed as a string, it must be one of the keys + of dict_algorithm (case insensitive). + - labels (int): The Labels field. If omitted (the corresponding + variable being set to None), the number of labels of "signer" + (excluding the trailing null label as specified in RFC4034) will + be used. + - originalttl (32-bit int): The Original TTL field. + - expiration (32-bit int): The Expiration TTL field. + - inception (32-bit int): The Inception TTL field. + - tag (16-bit int): The Key Tag field. + - signer (string): The Signer's Name field. The string must be + interpreted as a valid domain name. + - signature (int): The Signature field. Right now only a simple + integer form is supported. A prefix of "0" will be prepended if + the resulting hexadecimal representation consists of an odd + number of characters. + ''' + + covered = 'A' + algorithm = 'RSASHA1' + labels = None # auto-calculate (#labels of signer) originalttl = 3600 expiration = int(time.mktime(datetime.strptime('20100131120000', dnssec_timefmt).timetuple())) @@ -802,14 +939,21 @@ class RRSIG(RR): tag = 0x1035 signer = 'example.com' signature = 0x123456789abcdef123456789abcdef + def dump(self, f): name_wire = encode_name(self.signer) - sig_wire = '%x' % self.signature + sig_wire = '%x' % self.signature + if len(sig_wire) % 2 != 0: + sig_wire = '0' + sig_wire if self.rdlen is None: self.rdlen = int(18 + len(name_wire) / 2 + len(str(sig_wire)) / 2) self.dump_header(f, self.rdlen) - if self.labels < 0: + if type(self.covered) is str: + self.covered = dict_rrtype[self.covered.lower()] + if type(self.algorithm) is str: + self.algorithm = dict_algorithm[self.algorithm.lower()] + if self.labels is None: self.labels = count_namelabels(self.signer) f.write('# Covered=%s Algorithm=%s Labels=%d OrigTTL=%d\n' % (code_totext(self.covered, rdict_rrtype), @@ -824,6 +968,47 @@ class RRSIG(RR): f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire)) class TSIG(RR): + '''Implements rendering TSIG RDATA in the test data format. + + As a meta RR type TSIG uses some non common parameters. This + class overrides some of the default attributes of the RR class + accordingly: + - rr_class is set to 'ANY' + - rr_ttl is set to 0 + Like other derived classes these can be overridden via the spec + file. + + Other configurable parameters are as follows (see the description + of the same name of attribute for the default value): + - algorithm (string): The Algorithm Name field. The value is + generally interpreted as a domain name string, and will + typically be one of the standard algorithm names defined in + RFC4635. For convenience, however, a shortcut value "hmac-md5" + is allowed instead of the standard "hmac-md5.sig-alg.reg.int". + - time_signed (48-bit int): The Time Signed field. + - fudge (16-bit int): The Fudge field. + - mac_size (int): The MAC Size field. If omitted, the common value + determined by the algorithm will be used. + - mac (int or string): The MAC field. If specified as an integer, + the integer value is used as the MAC, possibly with prepended + 0's so that the total length will be mac_size. If specifed as a + string, it is converted to a sequence of ascii codes and its + hexadecimal representation will be used. So, for example, if + "mac" is set to 'abc', it will be converted to '616263'. Note + that in this case the length of "mac" may not be equal to + mac_size. If unspecified, the mac_size number of '78' (ascii + code of 'x') will be used. + - original_id (16-bit int): The Original ID field. + - error (16-bit int): The Error field. + - other_len (int): The Other Len field. + - other_data (int or string): The Other Data field. This is + interpreted just like "mac" except that other_len is used + instead of mac_size. If unspecified this will be empty unless + the "error" is set to 18 (which means the "BADTIME" error), in + which case a hexadecimal representation of "time_signed + fudge + + 1" will be used. + ''' + algorithm = 'hmac-sha256' time_signed = 1286978795 # arbitrarily chosen default fudge = 300 From faa90e91384af409419363aca539709e2985708b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 16:12:38 -0700 Subject: [PATCH 342/974] [904] improved extendability by eliminating the need for adding new RR class to config_param. It's not self-configured using a reflection technique. --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index 62d6a26d28..8890d1f12f 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -285,10 +285,6 @@ RDATA contains a single field named "value". What you are expected to do is as follows: -- Add a dictionary entry of "'foo': (FOO, {})" to config_param of the - get_config_param() function (this step could be automated; we may do - it in a future version) - - Define a new class named "FOO" inherited from the RR class. Also define a class variable named "value" for the FOO RDATA field (the variable name can be different from the field name, but it's @@ -1064,15 +1060,22 @@ class TSIG(RR): f.write('%04x%s\n' % (other_len, ' ' + other_data if len(other_data) > 0 else '')) +# Build section-class mapping +config_param = { 'name' : (Name, {}), + 'header' : (DNSHeader, header_xtables), + 'question' : (DNSQuestion, question_xtables), + 'edns' : (EDNS, {}) } +for rrtype in dict_rrtype.keys(): + # For any supported RR types add the tuple of (RR_CLASS, {}). + # We expect KeyError as not all the types are supported, and simply + # ignore them. + try: + cur_mod = sys.modules[__name__] + config_param[rrtype] = (cur_mod.__dict__[rrtype.upper()], {}) + except KeyError: + pass + def get_config_param(section): - config_param = {'name' : (Name, {}), - 'header' : (DNSHeader, header_xtables), - 'question' : (DNSQuestion, question_xtables), - 'edns' : (EDNS, {}), 'a' : (A, {}), 'ns' : (NS, {}), - 'soa' : (SOA, {}), 'txt' : (TXT, {}), 'aaaa' : (AAAA, {}), - 'rp' : (RP, {}), 'rrsig' : (RRSIG, {}), - 'nsec' : (NSEC, {}), 'nsec3' : (NSEC3, {}), - 'tsig' : (TSIG, {}) } s = section m = re.match('^([^:]+)/\d+$', section) if m: From ba80991049e1e361d2b1de08160c91e5bd38b728 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 16:26:05 -0700 Subject: [PATCH 343/974] [904] documented EDNS class parameters. also fixed a bug of mbz handling. --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index 8890d1f12f..385f36c400 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -502,6 +502,20 @@ class DNSQuestion: f.write(' %04x %04x\n' % (self.rrtype, self.rrclass)) class EDNS: + '''Implements rendering EDNS OPT RR in the test data format. + + Configurable parameter is as follows (see the description of the + same name of attribute for the default value): + - name (string): The owner name of the OPT RR. The string must be + interpreted as a valid domain name. + - udpsize (16-bit int): The UDP payload size (set as the RR class) + - extrcode (8-bit int): The upper 8 bits of the extended RCODE. + - version (8-bit int): The EDNS version. + - do (int): The DNSSEC DO bit. The bit will be set if this value + is 1; otherwise the bit will be unset. + - mbz (15-bit int): The rest of the flags field. + - rdlen (16-bit int): The RDLEN field. + ''' name = '.' udpsize = 4096 extrcode = 0 @@ -517,7 +531,7 @@ class EDNS: 1 if self.do else 0)) code_vers = (self.extrcode << 8) | (self.version & 0x00ff) - extflags = (self.do << 15) | (self.mbz & 0x8000) + extflags = (self.do << 15) | (self.mbz & ~0x8000) f.write('%s %04x %04x %04x %04x\n' % (encode_name(self.name), dict_rrtype['opt'], self.udpsize, code_vers, extflags)) From 925045f2ad19d5dccb7dde77530ea16ea7b6341b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 16:34:24 -0700 Subject: [PATCH 344/974] [904] documented DNSQuestion parameters. --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index 385f36c400..9c5ceb83d5 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -489,9 +489,25 @@ class DNSHeader: self.nscount, self.arcount)) class DNSQuestion: + '''Implements rendering a DNS question in the test data format. + + Configurable parameter is as follows (see the description of the + same name of attribute for the default value): + - name (string): The QNAME. The string must be interpreted as a + valid domain name. + - rrtype (int or string): The question type. If specified + as an integer, it must be the 16-bit RR type value of the + covered type. If specifed as a string, it must be the textual + mnemonic of the type. + - rrclass (int or string): The question class. If specified as an + integer, it must be the 16-bit RR class value of the covered + type. If specifed as a string, it must be the textual mnemonic + of the class. + ''' name = 'example.com.' rrtype = parse_value('A', dict_rrtype) rrclass = parse_value('IN', dict_rrclass) + def dump(self, f): f.write('\n# Question Section\n') f.write('# QNAME=%s QTYPE=%s QCLASS=%s\n' % @@ -514,7 +530,9 @@ class EDNS: - do (int): The DNSSEC DO bit. The bit will be set if this value is 1; otherwise the bit will be unset. - mbz (15-bit int): The rest of the flags field. - - rdlen (16-bit int): The RDLEN field. + - rdlen (16-bit int): The RDLEN field. Note: right now specifying + a non 0 value (except for making bogus data) doesn't make sense + because there is no way to configure RDATA. ''' name = '.' udpsize = 4096 From dc979c6874916221df10de3557db0d1b4a19d221 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 16:55:28 -0700 Subject: [PATCH 345/974] [904] documented Name and DNSHeader parameters. --- src/lib/dns/tests/testdata/gen_wiredata.py.in | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/dns/tests/testdata/gen_wiredata.py.in index 9c5ceb83d5..8e1f0798bd 100755 --- a/src/lib/dns/tests/testdata/gen_wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen_wiredata.py.in @@ -441,6 +441,17 @@ def print_header(f, input_file): ''') class Name: + '''Implements rendering a single domain name in the test data format. + + Configurable parameter is as follows (see the description of the + same name of attribute for the default value): + - name (string): A textual representation of the name, such as + 'example.com'. + - pointer (int): If specified, compression pointer will be + prepended to the generated data with the offset being the value + of this parameter. + ''' + name = 'example.com' pointer = None # no compression by default def dump(self, f): @@ -458,12 +469,33 @@ class Name: f.write('\n') class DNSHeader: + '''Implements rendering a DNS Header section in the test data format. + + Configurable parameter is as follows (see the description of the + same name of attribute for the default value): + - id (16-bit int): + - qr, aa, tc, rd, ra, ad, cd (0 or 1): Standard header bits as + defined in RFC1035 and RFC4035. If set to 1, the corresponding + bit will be set; if set to 0, it will be cleared. + - mbz (0-3): The reserved field of the 3rd and 4th octets of the + header. + - rcode (4-bit int or string): The RCODE field. If specified as a + string, it must be the commonly used textual mnemonic of the RCODEs + (NOERROR, FORMERR, etc, case insensitive). + - opcode (4-bit int or string): The OPCODE field. If specified as + a string, it must be the commonly used textual mnemonic of the + OPCODEs (QUERY, NOTIFY, etc, case insensitive). + - qdcount, ancount, nscount, arcount (16-bit int): The QD/AN/NS/AR + COUNT fields, respectively. + ''' + id = 0x1035 (qr, aa, tc, rd, ra, ad, cd) = 0, 0, 0, 0, 0, 0, 0 mbz = 0 rcode = 0 # noerror opcode = 0 # query (qdcount, ancount, nscount, arcount) = 1, 0, 0, 0 + def dump(self, f): f.write('\n# Header Section\n') f.write('# ID=' + str(self.id)) From 0e662967ac5a6c8e187725828cd20b826ca00000 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Aug 2011 17:23:22 -0700 Subject: [PATCH 346/974] [904] moved the script to lib/util/python/ --- configure.ac | 5 +++-- src/bin/auth/tests/testdata/Makefile.am | 2 +- src/lib/dns/tests/testdata/Makefile.am | 4 +--- src/lib/testutils/testdata/Makefile.am | 2 +- src/lib/util/Makefile.am | 2 +- .../{dns/tests/testdata => util/python}/gen_wiredata.py.in | 0 6 files changed, 7 insertions(+), 8 deletions(-) rename src/lib/{dns/tests/testdata => util/python}/gen_wiredata.py.in (100%) diff --git a/configure.ac b/configure.ac index 9e8623ea85..4343487507 100644 --- a/configure.ac +++ b/configure.ac @@ -871,6 +871,7 @@ AC_CONFIG_FILES([Makefile src/lib/util/Makefile src/lib/util/io/Makefile src/lib/util/unittests/Makefile + src/lib/util/python/Makefile src/lib/util/pyunittests/Makefile src/lib/util/tests/Makefile src/lib/acl/Makefile @@ -931,7 +932,6 @@ AC_OUTPUT([doc/version.ent src/lib/python/isc/log/tests/log_console.py src/lib/dns/gen-rdatacode.py src/lib/python/bind10_config.py - src/lib/dns/tests/testdata/gen_wiredata.py src/lib/cc/session_config.h.pre src/lib/cc/tests/session_unittests_config.h src/lib/log/tests/console_test.sh @@ -941,6 +941,7 @@ AC_OUTPUT([doc/version.ent src/lib/log/tests/severity_test.sh src/lib/log/tests/tempdir.h src/lib/util/python/mkpywrapper.py + src/lib/util/python/gen_wiredata.py src/lib/server_common/tests/data_path.h tests/system/conf.sh tests/system/glue/setup.sh @@ -965,13 +966,13 @@ AC_OUTPUT([doc/version.ent chmod +x src/bin/msgq/run_msgq.sh chmod +x src/bin/msgq/tests/msgq_test chmod +x src/lib/dns/gen-rdatacode.py - chmod +x src/lib/dns/tests/testdata/gen_wiredata.py chmod +x src/lib/log/tests/console_test.sh chmod +x src/lib/log/tests/destination_test.sh chmod +x src/lib/log/tests/init_logger_test.sh chmod +x src/lib/log/tests/local_file_test.sh chmod +x src/lib/log/tests/severity_test.sh chmod +x src/lib/util/python/mkpywrapper.py + chmod +x src/lib/util/python/gen_wiredata.py chmod +x src/lib/python/isc/log/tests/log_console.py chmod +x tests/system/conf.sh ]) diff --git a/src/bin/auth/tests/testdata/Makefile.am b/src/bin/auth/tests/testdata/Makefile.am index 56f8e16799..c86722f81d 100644 --- a/src/bin/auth/tests/testdata/Makefile.am +++ b/src/bin/auth/tests/testdata/Makefile.am @@ -23,4 +23,4 @@ EXTRA_DIST += example.com EXTRA_DIST += example.sqlite3 .spec.wire: - $(PYTHON) $(abs_top_builddir)/src/lib/dns/tests/testdata/gen_wiredata.py -o $@ $< + $(PYTHON) $(top_builddir)/src/lib/util/python/gen_wiredata.py -o $@ $< diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 3ad8211da9..743b5d2418 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -47,8 +47,6 @@ BUILT_SOURCES += tsig_verify4.wire tsig_verify5.wire tsig_verify6.wire BUILT_SOURCES += tsig_verify7.wire tsig_verify8.wire tsig_verify9.wire BUILT_SOURCES += tsig_verify10.wire -noinst_SCRIPTS = gen_wiredata.py - # NOTE: keep this in sync with real file listing # so is included in tarball EXTRA_DIST = edns_toWire1.spec edns_toWire2.spec @@ -124,4 +122,4 @@ EXTRA_DIST += tsig_verify7.spec tsig_verify8.spec tsig_verify9.spec EXTRA_DIST += tsig_verify10.spec .spec.wire: - $(PYTHON) ./gen_wiredata.py -o $@ $< + $(PYTHON) $(top_builddir)/src/lib/util/python/gen_wiredata.py -o $@ $< diff --git a/src/lib/testutils/testdata/Makefile.am b/src/lib/testutils/testdata/Makefile.am index 8a86bcfc14..918d5c55d3 100644 --- a/src/lib/testutils/testdata/Makefile.am +++ b/src/lib/testutils/testdata/Makefile.am @@ -32,4 +32,4 @@ EXTRA_DIST += test2.zone.in EXTRA_DIST += test2-new.zone.in .spec.wire: - $(PYTHON) $(abs_top_builddir)/src/lib/dns/tests/testdata/gen_wiredata.py -o $@ $< + $(PYTHON) $(top_builddir)/src/lib/util/python/gen_wiredata.py -o $@ $< diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am index 3db9ac4cfa..0b78b295af 100644 --- a/src/lib/util/Makefile.am +++ b/src/lib/util/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . io unittests tests pyunittests +SUBDIRS = . io unittests tests pyunittests python AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util diff --git a/src/lib/dns/tests/testdata/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in similarity index 100% rename from src/lib/dns/tests/testdata/gen_wiredata.py.in rename to src/lib/util/python/gen_wiredata.py.in From efea6557fd364ee42c84c08df28efa9797f1c9c8 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Fri, 5 Aug 2011 14:45:41 +0800 Subject: [PATCH 347/974] [trac1130] Finish the basic function of NAPTR rrtype support --- src/lib/dns/rdata/in_1/naptr_35.cc | 163 ++++++++++++++++--- src/lib/dns/rdata/in_1/naptr_35.h | 21 ++- src/lib/dns/tests/rdata_in_naptr_unittest.cc | 74 ++++++++- 3 files changed, 231 insertions(+), 27 deletions(-) diff --git a/src/lib/dns/rdata/in_1/naptr_35.cc b/src/lib/dns/rdata/in_1/naptr_35.cc index 5fd5ae8a34..72a191e166 100644 --- a/src/lib/dns/rdata/in_1/naptr_35.cc +++ b/src/lib/dns/rdata/in_1/naptr_35.cc @@ -20,7 +20,6 @@ #include -#include #include #include #include @@ -36,6 +35,13 @@ using namespace isc::util; NAPTR::NAPTR(InputBuffer& buffer, size_t len): replacement_(".") { + order_ = buffer.readUint16(); + preference_ = buffer.readUint16(); + + flags_ = getNextCharacterString(buffer, len); + services_ = getNextCharacterString(buffer, len); + regexp_ = getNextCharacterString(buffer, len); + replacement_ = Name(buffer); } NAPTR::NAPTR(const std::string& naptr_str): @@ -56,76 +62,162 @@ NAPTR::NAPTR(const std::string& naptr_str): string::const_iterator input_iterator = naptr_str.begin() + iss.tellg(); flags_ = getNextCharacterString(naptr_str, input_iterator); + + services_ = getNextCharacterString(naptr_str, input_iterator); + + regexp_ = getNextCharacterString(naptr_str, input_iterator); + + skipLeftSpaces(naptr_str, input_iterator); + + if (input_iterator < naptr_str.end()) { + string replacementStr(input_iterator, naptr_str.end()); + + replacement_ = Name(replacementStr); + } else { + isc_throw(InvalidRdataText, "Invalid NAPTR text format"); + } } NAPTR::NAPTR(const NAPTR& naptr): - replacement_(".") + Rdata(), order_(naptr.order_), preference_(naptr.preference_), + flags_(naptr.flags_), services_(naptr.services_), regexp_(naptr.regexp_), + replacement_(naptr.replacement_) { } void NAPTR::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(order_); + buffer.writeUint16(preference_); + + buffer.writeUint8(flags_.size()); + buffer.writeData(flags_.c_str(), flags_.size()); + + buffer.writeUint8(services_.size()); + buffer.writeData(services_.c_str(), services_.size()); + + buffer.writeUint8(regexp_.size()); + buffer.writeData(regexp_.c_str(), regexp_.size()); + + replacement_.toWire(buffer); } void NAPTR::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(order_); + renderer.writeUint16(preference_); + + renderer.writeUint8(flags_.size()); + renderer.writeData(flags_.c_str(), flags_.size()); + + renderer.writeUint8(services_.size()); + renderer.writeData(services_.c_str(), services_.size()); + + renderer.writeUint8(regexp_.size()); + renderer.writeData(regexp_.c_str(), regexp_.size()); + + replacement_.toWire(renderer); } string NAPTR::toText() const { - return ""; + string result; + result += lexical_cast(order_); + result += " "; + result += lexical_cast(preference_); + result += " \""; + result += flags_; + result += "\" \""; + result += services_; + result += "\" \""; + result += regexp_; + result += "\" "; + result += replacement_.toText(); + return (result); } int NAPTR::compare(const Rdata& other) const { - return 0; + const NAPTR other_naptr = dynamic_cast(other); + + if (order_ < other_naptr.order_) { + return (-1); + } else if (order_ > other_naptr.order_) { + return (1); + } + + if (preference_ < other_naptr.preference_) { + return (-1); + } else if (preference_ > other_naptr.preference_) { + return (1); + } + + if (flags_ < other_naptr.flags_) { + return (-1); + } else if (flags_ > other_naptr.flags_) { + return (1); + } + + if (services_ < other_naptr.services_) { + return (-1); + } else if (services_ > other_naptr.services_) { + return (1); + } + + if (regexp_ < other_naptr.regexp_) { + return (-1); + } else if (regexp_ > other_naptr.regexp_) { + return (1); + } + + return (compareNames(replacement_, other_naptr.replacement_)); } uint16_t NAPTR::getOrder() const { - return order_; + return (order_); } uint16_t NAPTR::getPreference() const { - return preference_; + return (preference_); } const std::string& NAPTR::getFlags() const { - return flags_; + return (flags_); } const std::string& NAPTR::getServices() const { - return services_; + return (services_); } const std::string& NAPTR::getRegexp() const { - return regexp_; + return (regexp_); } const Name& NAPTR::getReplacement() const { - return replacement_; + return (replacement_); } std::string -NAPTR::getNextCharacterString(const std::string& input_str, std::string::const_iterator& input_iterator){ +NAPTR::getNextCharacterString(const std::string& input_str, + std::string::const_iterator& input_iterator) +{ string result; - // Skip white spaces - while (input_iterator < input_str.end() && isspace(*input_iterator)) { - ++input_iterator; - } + skipLeftSpaces(input_str, input_iterator); - // If the input string only contains white-spaces, it is an invalid + // If the input string only contains white-spaces, it is an invalid + // if (input_iterator >= input_str.end()) { isc_throw(InvalidRdataText, "Invalid NAPTR text format"); } - // Whether the is seperated with doulble quotes symbol (") + // Whether the is seperated with doulble quotes symbol(") bool quotes_seperated = (*input_iterator == '"'); if (quotes_seperated) { @@ -134,14 +226,17 @@ NAPTR::getNextCharacterString(const std::string& input_str, std::string::const_i while(input_iterator < input_str.end()){ if (quotes_seperated) { - // If the is seperated with quotes symbol and another - // quotes symbol is encountered, it is the end of the + // If the is seperated with quotes symbol and + // another quotes symbol is encountered, it is the end of the + // if (*input_iterator == '"') { + ++input_iterator; break; } } else if (*input_iterator == ' ') { - // If the is not seperated with quotes symbol, it is - // seperated with char + // If the is not seperated with quotes symbol, + // it is seperated with char + ++input_iterator; break; } @@ -150,7 +245,31 @@ NAPTR::getNextCharacterString(const std::string& input_str, std::string::const_i ++input_iterator; } - return result; + return (result); +} + +std::string +NAPTR::getNextCharacterString(InputBuffer& buffer, size_t len) { + uint8_t str_len = buffer.readUint8(); + + size_t pos = buffer.getPosition(); + if (len - pos < str_len) { + isc_throw(InvalidRdataLength, "Invalid NAPTR string length"); + } + + uint8_t buf[MAX_CHARSTRING_LEN]; + buffer.readData(buf, str_len); + return (string(buf, buf + str_len)); +} + +void +NAPTR::skipLeftSpaces(const std::string& input_str, + std::string::const_iterator& input_iterator) +{ + // Skip white spaces + while (input_iterator < input_str.end() && isspace(*input_iterator)) { + ++input_iterator; + } } // END_RDATA_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/naptr_35.h b/src/lib/dns/rdata/in_1/naptr_35.h index 2921771b8c..fa93c1408c 100644 --- a/src/lib/dns/rdata/in_1/naptr_35.h +++ b/src/lib/dns/rdata/in_1/naptr_35.h @@ -18,6 +18,7 @@ #include #include +#include // BEGIN_ISC_NAMESPACE @@ -39,13 +40,27 @@ public: const std::string& getRegexp() const; const Name& getReplacement() const; private: - /// Extract a from a string + /// Get a from a string /// /// \param input_str The input string - /// \param input_iterator The iterator from which to start extracting - /// \return a std::string that contains the extracted + /// \param input_iterator The iterator from which to start extracting, the iterator will be updated + /// to new position after the function is returned + /// \return A std::string that contains the extracted std::string getNextCharacterString(const std::string& input_str, std::string::const_iterator& input_iterator); + /// Get a from a input buffer + /// + /// \param buffer The input buffer + /// \param len The input buffer total length + /// \return A std::string that contains the extracted + std::string getNextCharacterString(util::InputBuffer& buffer, size_t len); + + /// Skip the left whitespaces of the input string + /// + /// \param input_str The input string + /// \param input_iterator From which the skipping started + void skipLeftSpaces(const std::string& input_str, std::string::const_iterator& input_iterator); + uint16_t order_; uint16_t preference_; std::string flags_; diff --git a/src/lib/dns/tests/rdata_in_naptr_unittest.cc b/src/lib/dns/tests/rdata_in_naptr_unittest.cc index f6871823f4..aae80ebbfd 100644 --- a/src/lib/dns/tests/rdata_in_naptr_unittest.cc +++ b/src/lib/dns/tests/rdata_in_naptr_unittest.cc @@ -37,19 +37,89 @@ class Rdata_IN_NAPTR_Test : public RdataTest { }; // 10 100 "S" "SIP+D2U" "" _sip._udp.example.com. -static uint8_t naptr_rdata[] = {0x00,0x0a,0x00,0x64,0x01,0x53,0x07,0x53,0x49,0x50,0x2b,0x44,0x32,0x55,0x00,0x04,0x5f,0x73,0x69,0x70, - 0x04,0x5f,0x75,0x64,0x70,0x07,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x03,0x63,0x6f,0x6d,0x00}; +static uint8_t naptr_rdata[] = {0x00,0x0a,0x00,0x64,0x01,0x53,0x07,0x53,0x49, + 0x50,0x2b,0x44,0x32,0x55,0x00,0x04,0x5f,0x73,0x69,0x70,0x04,0x5f,0x75,0x64, + 0x70,0x07,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x03,0x63,0x6f,0x6d,0x00}; static const char *naptr_str = "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small1 = "9 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small2 = "10 90 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small3 = "10 100 \"R\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small4 = "10 100 \"S\" \"SIP+C2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small5 = "10 100 \"S\" \"SIP+D2U\" \"\" _rip._udp.example.com."; + +static const char *naptr_str_large1 = "11 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large2 = "10 110 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large3 = "10 100 \"T\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large4 = "10 100 \"S\" \"SIP+E2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large5 = "10 100 \"S\" \"SIP+D2U\" \"\" _tip._udp.example.com."; TEST_F(Rdata_IN_NAPTR_Test, createFromText) { NAPTR naptr(naptr_str); EXPECT_EQ(10, naptr.getOrder()); EXPECT_EQ(100, naptr.getPreference()); EXPECT_EQ(string("S"), naptr.getFlags()); + EXPECT_EQ(string("SIP+D2U"), naptr.getServices()); + EXPECT_EQ(string(""), naptr.getRegexp()); + EXPECT_EQ(Name("_sip._udp.example.com."), naptr.getReplacement()); } TEST_F(Rdata_IN_NAPTR_Test, createFromWire) { + InputBuffer input_buffer(naptr_rdata, sizeof(naptr_rdata)); + NAPTR naptr(input_buffer, sizeof(naptr_rdata)); + EXPECT_EQ(10, naptr.getOrder()); + EXPECT_EQ(100, naptr.getPreference()); + EXPECT_EQ(string("S"), naptr.getFlags()); + EXPECT_EQ(string("SIP+D2U"), naptr.getServices()); + EXPECT_EQ(string(""), naptr.getRegexp()); + EXPECT_EQ(Name("_sip._udp.example.com."), naptr.getReplacement()); +} + +TEST_F(Rdata_IN_NAPTR_Test, toWire) { + NAPTR naptr(naptr_str); + naptr.toWire(obuffer); + + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(), + obuffer.getLength(), naptr_rdata, sizeof(naptr_rdata)); +} + +TEST_F(Rdata_IN_NAPTR_Test, toWireRenderer) { + NAPTR naptr(naptr_str); + + naptr.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(), + obuffer.getLength(), naptr_rdata, sizeof(naptr_rdata)); +} + +TEST_F(Rdata_IN_NAPTR_Test, toText) { + NAPTR naptr(naptr_str); + EXPECT_EQ(naptr_str, naptr.toText()); +} + +TEST_F(Rdata_IN_NAPTR_Test, compare) { + NAPTR naptr(naptr_str); + NAPTR naptr_small1(naptr_str_small1); + NAPTR naptr_small2(naptr_str_small2); + NAPTR naptr_small3(naptr_str_small3); + NAPTR naptr_small4(naptr_str_small4); + NAPTR naptr_small5(naptr_str_small5); + NAPTR naptr_large1(naptr_str_large1); + NAPTR naptr_large2(naptr_str_large2); + NAPTR naptr_large3(naptr_str_large3); + NAPTR naptr_large4(naptr_str_large4); + NAPTR naptr_large5(naptr_str_large5); + + EXPECT_EQ(0, naptr.compare(NAPTR(naptr_str))); + EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small1))); + EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small2))); + EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small3))); + EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small4))); + EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small5))); + EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large1))); + EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large2))); + EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large3))); + EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large4))); + EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large5))); } } From ee2a86bd4c1472e606b3d59ef5c4392b61d7ab48 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Aug 2011 10:27:16 +0200 Subject: [PATCH 348/974] [master] fix bug in bindctl 'config add' reviewed on jabber --- src/lib/python/isc/config/ccsession.py | 4 ++-- src/lib/python/isc/config/tests/ccsession_test.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index 81507290c9..4fa9d58f9c 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -425,7 +425,7 @@ class UIModuleCCSession(MultiConfigData): raise ModuleCCSessionError("Bad config version") self._set_current_config(config) - def _add_value_to_list(self, identifier, value): + def _add_value_to_list(self, identifier, value, module_spec): cur_list, status = self.get_value(identifier) if not cur_list: cur_list = [] @@ -491,7 +491,7 @@ class UIModuleCCSession(MultiConfigData): if set_value_str is not None: value_str += set_value_str value = isc.cc.data.parse_value_str(value_str) - self._add_value_to_list(identifier, value) + self._add_value_to_list(identifier, value, module_spec) elif 'named_set_item_spec' in module_spec: item_name = None item_value = None diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py index c820ad913d..351c8e666d 100644 --- a/src/lib/python/isc/config/tests/ccsession_test.py +++ b/src/lib/python/isc/config/tests/ccsession_test.py @@ -745,6 +745,8 @@ class TestUIModuleCCSession(unittest.TestCase): uccs.remove_value, "Spec2/item5[123]", None) uccs.remove_value("Spec2/item5[0]", None) self.assertEqual({'Spec2': {'item5': []}}, uccs._local_changes) + uccs.add_value("Spec2/item5", None); + self.assertEqual({'Spec2': {'item5': ['']}}, uccs._local_changes) def test_add_remove_value_named_set(self): fake_conn = fakeUIConn() From 07cd1647921e0e94432cecb2f7a5413cd8f3884e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 5 Aug 2011 12:52:13 +0200 Subject: [PATCH 349/974] [1067] Interface definitions for iterating database * DatabaseClient::getIterator * DatabaseConnection::getIteratorContext * DatabaseConnection::IteratorContext Nothing is implemented, it's just the header parts and doxygen comments. --- src/lib/datasrc/database.h | 97 ++++++++++++++++++++++ src/lib/datasrc/tests/database_unittest.cc | 7 ++ 2 files changed, 104 insertions(+) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 5693479a77..b44211daab 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -17,6 +17,8 @@ #include +#include + namespace isc { namespace datasrc { @@ -71,6 +73,84 @@ public: * an opaque handle. */ virtual std::pair getZone(const isc::dns::Name& name) const = 0; + /** + * \brief This holds the internal context of ZoneIterator for databases + * + * While the ZoneIterator implementation from DatabaseClient does all the + * translation from strings to DNS classes and validation, this class + * holds the pointer to where the database is at reading the data. + * + * It can either hold shared pointer to the connection which created it + * and have some kind of statement inside (in case single database + * connection can handle multiple concurrent SQL statements) or it can + * create a new connection (or, if it is more convenient, the connection + * itself can inherit both from DatabaseConnection and IteratorContext + * and just clone itself). + */ + class IteratorContext : public boost::noncopyable { + public: + /** + * \brief Destructor + * + * Virtual destructor, so any descendand class is destroyed correctly. + */ + virtual ~IteratorContext() { } + /** + * \brief Function to provide next resource record + * + * This function should provide data about the next resource record + * from the iterated zone. The data are not converted yet. + * + * The order of RRs is not strictly set, but the RRs for single RRset + * must not be interlieved with any other RRs (eg. RRsets must be + * "together"). + * + * \param name The name of the RR will be returned here. + * \param rtype The string representation of RRType will be returned + * through this parameter. + * \param ttl The time to live output parameter. + * \param data This is where the string representation of data will be + * put. + * \return If there was RR returned. Once it returns false, the zone + * was iterated to its end. + * \todo Do we consider databases where it is stored in binary blob + * format? + */ + virtual bool getNext(std::string& name, std::string& rtype, int& ttl, + std::string& data) = 0; + }; + typedef boost::shared_ptr IteratorContextPtr; + /** + * \brief Creates an iterator context for given zone. + * + * This should create a new iterator context to be used by + * DatabaseConnection's ZoneIterator. It can be created based on the name + * or the ID (returned from getZone()), what is more comfortable for the + * database implementation. Both are provided (and are guaranteed to match, + * the DatabaseClient first looks up the zone ID and then calls this). + * + * The default implementation throws isc::NotImplemented, to allow + * "minimal" implementations of the connection not supporting optional + * functionality. + * + * \param name The name of the zone. + * \param id The ID of the zone, returned from getZone(). + * \return Newly created iterator context. Must not be NULL. + */ + virtual IteratorContextPtr getIteratorContext(const isc::dns::Name& name, + int id) const + { + /* + * This is a compromise. We need to document the parameters in doxygen, + * so they need a name, but then it complains about unused parameter. + * This is a NOP that "uses" the parameters. + */ + static_cast(name); + static_cast(id); + + isc_throw(isc::NotImplemented, + "This database datasource can't be iterated"); + } }; /** @@ -181,6 +261,23 @@ public: * returned, though. */ virtual FindResult findZone(const isc::dns::Name& name) const; + /** + * \brief Get the zone iterator + * + * The iterator allows going through the whole zone content. If the + * underlying DatabaseConnection is implemented correctly, it should + * be possible to have multiple ZoneIterators at once and query data + * at the same time. + * + * \exception DataSourceError if the zone doesn't exist. + * \exception isc::NotImplemented if the underlying DatabaseConnection + * doesn't implement iteration. + * \exception Anything else the underlying DatabaseConnection might + * want to throw. + * \param name The origin of the zone to iterate. + * \return Shared pointer to the iterator (it will never be NULL) + */ + virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const; private: /// \brief Our connection. const std::auto_ptr connection_; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index b60d5c0ced..4a160d1b20 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -41,6 +41,13 @@ public: } }; +// This tests the default getIteratorContext behaviour, throwing NotImplemented +TEST(DatabaseConnectionTest, getIteratorContext) { + // The parameters don't matter + EXPECT_THROW(MockConnection().getIteratorContext(Name("."), 1), + isc::NotImplemented); +} + class DatabaseClientTest : public ::testing::Test { public: DatabaseClientTest() { From 885d7987eefb0b8b694626b0831ed93123fb8d8d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Aug 2011 15:06:29 +0200 Subject: [PATCH 350/974] [trac1061] fix name for in-memory sqlite db --- src/lib/datasrc/tests/sqlite3_connection_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 6d2a945a7f..1bdbe90206 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -31,7 +31,7 @@ std::string SQLITE_DBFILE_EXAMPLE = TEST_DATA_DIR "/test.sqlite3"; std::string SQLITE_DBFILE_EXAMPLE2 = TEST_DATA_DIR "/example2.com.sqlite3"; std::string SQLITE_DBFILE_EXAMPLE_ROOT = TEST_DATA_DIR "/test-root.sqlite3"; std::string SQLITE_DBFILE_BROKENDB = TEST_DATA_DIR "/brokendb.sqlite3"; -std::string SQLITE_DBFILE_MEMORY = "memory"; +std::string SQLITE_DBFILE_MEMORY = ":memory:"; // 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, From d23cde8c4285cf55b007b300123c41fa852d38d9 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 3 Aug 2011 14:51:51 +0200 Subject: [PATCH 351/974] [trac1062] initial addition of searchForRecords and getNextRecord --- src/lib/datasrc/database.h | 23 +++++++ src/lib/datasrc/sqlite3_connection.cc | 73 ++++++++++++++++++---- src/lib/datasrc/sqlite3_connection.h | 2 + src/lib/datasrc/tests/database_unittest.cc | 2 + 4 files changed, 87 insertions(+), 13 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 8e5c1564e5..2ed9cd5f51 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -71,6 +71,29 @@ public: * an opaque handle. */ virtual std::pair getZone(const isc::dns::Name& name) const = 0; + + /** + * \brief Starts a new search for records of the given name in the given zone + * + * \param zone_id The zone to search in, as returned by getZone() + * \param name The name of the records to find + */ + virtual void searchForRecords(int zone_id, const std::string& name) const = 0; + + /** + * \brief Retrieves the next record from the search started with searchForRecords() + * + * Returns a boolean specifying whether or not there was more data to read. + * In the case of a database error, a DatasourceError is thrown. + * + * \exception DatasourceError if there was an error reading from the database + * + * \param columns This vector will be cleared, and the fields of the record will + * be appended here as strings (in the order rdtype, ttl, sigtype, + * and rdata). If there was no data, the vector is untouched. + * \return true if there was a next record, false if there was not + */ + virtual bool getNextRecord(std::vector& columns) const = 0; }; /** diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index 35db44620d..cab92388ad 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -24,19 +24,20 @@ namespace datasrc { struct SQLite3Parameters { SQLite3Parameters() : db_(NULL), version_(-1), - q_zone_(NULL) /*, q_record_(NULL), q_addrs_(NULL), q_referral_(NULL), - q_any_(NULL), q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL), + q_zone_(NULL), q_any_(NULL) + /*q_record_(NULL), q_addrs_(NULL), q_referral_(NULL), + q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL), q_prevnsec3_(NULL) */ {} sqlite3* db_; int version_; sqlite3_stmt* q_zone_; + sqlite3_stmt* q_any_; /* TODO: Yet unneeded statements sqlite3_stmt* q_record_; sqlite3_stmt* q_addrs_; sqlite3_stmt* q_referral_; - sqlite3_stmt* q_any_; sqlite3_stmt* q_count_; sqlite3_stmt* q_previous_; sqlite3_stmt* q_nsec3_; @@ -69,6 +70,9 @@ public: if (params_.q_zone_ != NULL) { sqlite3_finalize(params_.q_zone_); } + if (params_.q_any_ != NULL) { + sqlite3_finalize(params_.q_any_); + } /* if (params_.q_record_ != NULL) { sqlite3_finalize(params_.q_record_); @@ -79,9 +83,6 @@ public: if (params_.q_referral_ != NULL) { sqlite3_finalize(params_.q_referral_); } - if (params_.q_any_ != NULL) { - sqlite3_finalize(params_.q_any_); - } if (params_.q_count_ != NULL) { sqlite3_finalize(params_.q_count_); } @@ -132,6 +133,9 @@ const char* const SCHEMA_LIST[] = { const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"; +const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata " + "FROM records WHERE zone_id=?1 AND name=?2"; + /* TODO: Prune the statements, not everything will be needed maybe? const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata " "FROM records WHERE zone_id=?1 AND name=?2 AND " @@ -148,9 +152,6 @@ const char* const q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM " "(rdtype='NS' OR sigtype='NS' OR rdtype='DS' OR sigtype='DS' OR " "rdtype='DNAME' OR sigtype='DNAME')"; -const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata " - "FROM records WHERE zone_id=?1 AND name=?2"; - const char* const q_count_str = "SELECT COUNT(*) FROM records " "WHERE zone_id=?1 AND rname LIKE (?2 || '%');"; @@ -200,11 +201,11 @@ checkAndSetupSchema(Initializer* initializer) { } initializer->params_.q_zone_ = prepare(db, q_zone_str); + initializer->params_.q_any_ = prepare(db, q_any_str); /* TODO: Yet unneeded statements initializer->params_.q_record_ = prepare(db, q_record_str); initializer->params_.q_addrs_ = prepare(db, q_addrs_str); initializer->params_.q_referral_ = prepare(db, q_referral_str); - initializer->params_.q_any_ = prepare(db, q_any_str); initializer->params_.q_count_ = prepare(db, q_count_str); initializer->params_.q_previous_ = prepare(db, q_previous_str); initializer->params_.q_nsec3_ = prepare(db, q_nsec3_str); @@ -252,6 +253,9 @@ SQLite3Connection::close(void) { sqlite3_finalize(dbparameters_->q_zone_); dbparameters_->q_zone_ = NULL; + sqlite3_finalize(dbparameters_->q_any_); + dbparameters_->q_any_ = NULL; + /* TODO: Once they are needed or not, uncomment or drop sqlite3_finalize(dbparameters->q_record_); dbparameters->q_record_ = NULL; @@ -262,9 +266,6 @@ SQLite3Connection::close(void) { sqlite3_finalize(dbparameters->q_referral_); dbparameters->q_referral_ = NULL; - sqlite3_finalize(dbparameters->q_any_); - dbparameters->q_any_ = NULL; - sqlite3_finalize(dbparameters->q_count_); dbparameters->q_count_ = NULL; @@ -318,5 +319,51 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const { return (result); } +void +SQLite3Connection::searchForRecords(int zone_id, const std::string& name) const { + sqlite3_reset(dbparameters_->q_any_); + sqlite3_clear_bindings(dbparameters_->q_any_); + sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id); + // use transient since name is a ref and may disappear + sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, + SQLITE_TRANSIENT); +}; + +namespace { +const char* +convertToPlainChar(const unsigned char* ucp) { + if (ucp == NULL) { + return (""); + } + const void* p = ucp; + return (static_cast(p)); +} +} + +bool +SQLite3Connection::getNextRecord(std::vector& columns) const { + sqlite3_stmt* current_stmt = dbparameters_->q_any_; + const int rc = sqlite3_step(current_stmt); + + if (rc == SQLITE_ROW) { + columns.clear(); + for (int column = 0; column < 4; ++column) { + columns.push_back(convertToPlainChar(sqlite3_column_text( + current_stmt, column))); + } + return true; + } else if (rc == SQLITE_DONE) { + // reached the end of matching rows + sqlite3_reset(current_stmt); + sqlite3_clear_bindings(current_stmt); + return false; + } + sqlite3_reset(current_stmt); + isc_throw(DataSourceError, "Unexpected failure in sqlite3_step"); + + // Compilers might not realize isc_throw always throws + return false; +} + } } diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index 484571599e..bb1a30f867 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -88,6 +88,8 @@ public: * element and the zone id in the second if it was. */ virtual std::pair getZone(const isc::dns::Name& name) const; + virtual void searchForRecords(int zone_id, const std::string& name) const; + virtual bool getNextRecord(std::vector& columns) const; private: /// \brief Private database data SQLite3Parameters* dbparameters_; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index c271a76dc8..be55a487dc 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -39,6 +39,8 @@ public: return (std::pair(false, 0)); } } + virtual void searchForRecords(int, const std::string&) const {}; + virtual bool getNextRecord(std::vector&) const { return false; }; }; class DatabaseClientTest : public ::testing::Test { From ff14da4f9b706a47f152491eae60586b75430c6e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 3 Aug 2011 17:55:32 +0200 Subject: [PATCH 352/974] [trac1062] initial addition of find() code --- src/lib/datasrc/database.cc | 69 ++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 2264f2c7ab..dfde94014e 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -16,6 +16,9 @@ #include #include +#include +#include +#include using isc::dns::Name; @@ -62,13 +65,71 @@ DatabaseClient::Finder::Finder(boost::shared_ptr { } ZoneFinder::FindResult -DatabaseClient::Finder::find(const isc::dns::Name&, - const isc::dns::RRType&, +DatabaseClient::Finder::find(const isc::dns::Name& name, + const isc::dns::RRType& type, isc::dns::RRsetList*, const FindOptions) const { - // TODO Implement - return (FindResult(SUCCESS, isc::dns::ConstRRsetPtr())); + bool records_found = false; + connection_.searchForRecords(zone_id_, name.toText()); + + isc::dns::RRsetPtr result_rrset; + + std::vector columns; + while (connection_.getNextRecord(columns)) { + if (!records_found) { + records_found = true; + } + + if (columns.size() != 4) { + isc_throw(DataSourceError, + "Datasource backend did not return 4 columns in getNextRecord()"); + } + + const isc::dns::RRType cur_type(columns[0]); + const isc::dns::RRTTL cur_ttl(columns[1]); + //cur_sigtype(columns[2]); + + if (cur_type == type) { + if (!result_rrset) { + result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, + getClass(), + cur_type, + cur_ttl)); + } else { + // We have existing data from earlier calls, do some checks + // and updates if necessary + if (cur_ttl < result_rrset->getTTL()) { + result_rrset->setTTL(cur_ttl); + } + } + + result_rrset->addRdata(isc::dns::rdata::createRdata(cur_type, + getClass(), + columns[3])); + } else if (cur_type == isc::dns::RRType::CNAME()) { + // There should be no other data, so cur_rrset should be empty, + // except for signatures + if (result_rrset && result_rrset->getRdataCount() > 0) { + isc_throw(DataSourceError, "CNAME found but it is not the only record for " + name.toText()); + } + result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, + getClass(), + cur_type, + cur_ttl)); + result_rrset->addRdata(isc::dns::rdata::createRdata(cur_type, + getClass(), + columns[3])); + } + } + + if (result_rrset) { + return (FindResult(SUCCESS, result_rrset)); + } else if (records_found) { + return (FindResult(NXRRSET, isc::dns::ConstRRsetPtr())); + } else { + return (FindResult(NXDOMAIN, isc::dns::ConstRRsetPtr())); + } } Name From 1b96c2563342098e05ac4b240c66e60222249cf4 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 11:27:58 +0200 Subject: [PATCH 353/974] [trac1062] update tests --- src/lib/datasrc/sqlite3_connection.cc | 1 + .../tests/sqlite3_connection_unittest.cc | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index cab92388ad..b9e595884e 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -359,6 +359,7 @@ SQLite3Connection::getNextRecord(std::vector& columns) const { return false; } sqlite3_reset(current_stmt); + sqlite3_clear_bindings(current_stmt); isc_throw(DataSourceError, "Unexpected failure in sqlite3_step"); // Compilers might not realize isc_throw always throws diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 1bdbe90206..1c80eb5990 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -100,4 +100,40 @@ TEST_F(SQLite3Conn, noClass) { EXPECT_FALSE(conn->getZone(Name("example.com")).first); } +namespace { + // Simple function to cound the number of records for + // any name + size_t countRecords(std::auto_ptr& conn, + int zone_id, const std::string& name) { + conn->searchForRecords(zone_id, name); + size_t count = 0; + std::vector columns; + while (conn->getNextRecord(columns)) { + EXPECT_EQ(4, columns.size()); + ++count; + } + return count; + } +} +} + +TEST_F(SQLite3Conn, getRecords) { + std::pair zone_info(conn->getZone(Name("example.com"))); + ASSERT_TRUE(zone_info.first); + + int zone_id = zone_info.second; + ASSERT_EQ(1, zone_id); + + // without search, getNext() should return false + std::vector columns; + EXPECT_FALSE(conn->getNextRecord(columns)); + EXPECT_EQ(0, columns.size()); + + EXPECT_EQ(4, countRecords(conn, zone_id, "foo.example.com.")); + EXPECT_EQ(15, countRecords(conn, zone_id, "example.com.")); + EXPECT_EQ(0, countRecords(conn, zone_id, "foo.bar.")); + EXPECT_EQ(0, countRecords(conn, zone_id, "")); + + EXPECT_FALSE(conn->getNextRecord(columns)); + EXPECT_EQ(0, columns.size()); } From 71b0ae9ddbcbf4093900ff879e2e1c82be89867f Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 14:32:53 +0200 Subject: [PATCH 354/974] [trac1062] tests and some additional code --- src/lib/datasrc/database.cc | 8 +- src/lib/datasrc/database.h | 8 +- src/lib/datasrc/sqlite3_connection.cc | 4 +- src/lib/datasrc/sqlite3_connection.h | 4 +- src/lib/datasrc/tests/database_unittest.cc | 119 ++++++++++++++++++++- 5 files changed, 134 insertions(+), 9 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index dfde94014e..8b4a669539 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -74,6 +74,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, connection_.searchForRecords(zone_id_, name.toText()); isc::dns::RRsetPtr result_rrset; + ZoneFinder::Result result_status = NXRRSET; std::vector columns; while (connection_.getNextRecord(columns)) { @@ -96,6 +97,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, getClass(), cur_type, cur_ttl)); + result_status = SUCCESS; } else { // We have existing data from earlier calls, do some checks // and updates if necessary @@ -120,11 +122,15 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_rrset->addRdata(isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); + result_status = CNAME; + } else if (cur_type == isc::dns::RRType::RRSIG()) { + // if we have data already, check covered type + // if not, covered type must be CNAME or type requested } } if (result_rrset) { - return (FindResult(SUCCESS, result_rrset)); + return (FindResult(result_status, result_rrset)); } else if (records_found) { return (FindResult(NXRRSET, isc::dns::ConstRRsetPtr())); } else { diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 2ed9cd5f51..a1f566a23f 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -78,7 +78,7 @@ public: * \param zone_id The zone to search in, as returned by getZone() * \param name The name of the records to find */ - virtual void searchForRecords(int zone_id, const std::string& name) const = 0; + virtual void searchForRecords(int zone_id, const std::string& name) = 0; /** * \brief Retrieves the next record from the search started with searchForRecords() @@ -93,7 +93,7 @@ public: * and rdata). If there was no data, the vector is untouched. * \return true if there was a next record, false if there was not */ - virtual bool getNextRecord(std::vector& columns) const = 0; + virtual bool getNextRecord(std::vector& columns) = 0; }; /** @@ -154,6 +154,10 @@ public: Finder(boost::shared_ptr connection, int zone_id); virtual isc::dns::Name getOrigin() const; virtual isc::dns::RRClass getClass() const; + + /** + * \brief Find an RRset in the datasource + */ virtual FindResult find(const isc::dns::Name& name, const isc::dns::RRType& type, isc::dns::RRsetList* target = NULL, diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index b9e595884e..fa5f8310d2 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -320,7 +320,7 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const { } void -SQLite3Connection::searchForRecords(int zone_id, const std::string& name) const { +SQLite3Connection::searchForRecords(int zone_id, const std::string& name) { sqlite3_reset(dbparameters_->q_any_); sqlite3_clear_bindings(dbparameters_->q_any_); sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id); @@ -341,7 +341,7 @@ convertToPlainChar(const unsigned char* ucp) { } bool -SQLite3Connection::getNextRecord(std::vector& columns) const { +SQLite3Connection::getNextRecord(std::vector& columns) { sqlite3_stmt* current_stmt = dbparameters_->q_any_; const int rc = sqlite3_step(current_stmt); diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index bb1a30f867..ca41a0621c 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -88,8 +88,8 @@ public: * element and the zone id in the second if it was. */ virtual std::pair getZone(const isc::dns::Name& name) const; - virtual void searchForRecords(int zone_id, const std::string& name) const; - virtual bool getNextRecord(std::vector& columns) const; + virtual void searchForRecords(int zone_id, const std::string& name); + virtual bool getNextRecord(std::vector& columns); private: /// \brief Private database data SQLite3Parameters* dbparameters_; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index be55a487dc..f9b8a0a41b 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -18,6 +18,10 @@ #include #include +#include +#include + +#include using namespace isc::datasrc; using namespace std; @@ -32,6 +36,8 @@ namespace { */ class MockConnection : public DatabaseConnection { public: + MockConnection() { fillData(); } + virtual std::pair getZone(const Name& name) const { if (name == Name("example.org")) { return (std::pair(true, 42)); @@ -39,8 +45,74 @@ public: return (std::pair(false, 0)); } } - virtual void searchForRecords(int, const std::string&) const {}; - virtual bool getNextRecord(std::vector&) const { return false; }; + + virtual void searchForRecords(int zone_id, const std::string& name) { + // we're not aiming for efficiency in this test, simply + // copy the relevant vector from records + cur_record = 0; + + if (zone_id == 42) { + if (records.count(name) > 0) { + cur_name = records.find(name)->second; + } else { + cur_name.clear(); + } + } else { + cur_name.clear(); + } + }; + + virtual bool getNextRecord(std::vector& columns) { + if (cur_record < cur_name.size()) { + columns = cur_name[cur_record++]; + return true; + } else { + return false; + } + }; + +private: + std::map > > records; + // used as internal index for getNextRecord() + size_t cur_record; + // used as temporary storage after searchForRecord() and during + // getNextRecord() calls, as well as during the building of the + // fake data + std::vector< std::vector > cur_name; + + void addRecord(const std::string& name, + const std::string& type, + const std::string& sigtype, + const std::string& rdata) { + std::vector columns; + columns.push_back(name); + columns.push_back(type); + columns.push_back(sigtype); + columns.push_back(rdata); + cur_name.push_back(columns); + } + + void addCurName(const std::string& name) { + records[name] = cur_name; + cur_name.clear(); + } + + void fillData() { + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("AAAA", "3600", "", "2001:db8::1"); + addRecord("AAAA", "3600", "", "2001:db8::2"); + addCurName("www.example.org."); + addRecord("CNAME", "3600", "", "www.example.org."); + addCurName("cname.example.org."); + + // also add some intentionally bad data + cur_name.push_back(std::vector()); + addCurName("emptyvector.example.org."); + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("CNAME", "3600", "", "www.example.org."); + addCurName("badcname.example.org."); + + } }; class DatabaseClientTest : public ::testing::Test { @@ -98,4 +170,47 @@ TEST_F(DatabaseClientTest, noConnException) { isc::InvalidParameter); } +TEST_F(DatabaseClientTest, find) { + DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); + ASSERT_EQ(result::SUCCESS, zone.code); + shared_ptr finder( + dynamic_pointer_cast(zone.zone_finder)); + EXPECT_EQ(42, finder->zone_id()); + isc::dns::Name name("www.example.org."); + + ZoneFinder::FindResult result1 = finder->find(name, isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(ZoneFinder::SUCCESS, result1.code); + EXPECT_EQ(1, result1.rrset->getRdataCount()); + EXPECT_EQ(isc::dns::RRType::A(), result1.rrset->getType()); + + ZoneFinder::FindResult result2 = finder->find(name, isc::dns::RRType::AAAA(), + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(ZoneFinder::SUCCESS, result2.code); + EXPECT_EQ(2, result2.rrset->getRdataCount()); + EXPECT_EQ(isc::dns::RRType::AAAA(), result2.rrset->getType()); + + ZoneFinder::FindResult result3 = finder->find(name, isc::dns::RRType::TXT(), + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(ZoneFinder::NXRRSET, result3.code); + EXPECT_EQ(isc::dns::ConstRRsetPtr(), result3.rrset); + + ZoneFinder::FindResult result4 = finder->find(isc::dns::Name("cname.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(ZoneFinder::CNAME, result4.code); + EXPECT_EQ(1, result4.rrset->getRdataCount()); + EXPECT_EQ(isc::dns::RRType::CNAME(), result4.rrset->getType()); + + EXPECT_THROW(finder->find(isc::dns::Name("emptyvector.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_THROW(finder->find(isc::dns::Name("badcname.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + +} + } From 82667b0cdd6592053f5b2f4cfa1cbd0ec92db0b2 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 14:49:49 +0200 Subject: [PATCH 355/974] [trac1062] minor cleanup --- src/lib/datasrc/database.cc | 5 +---- src/lib/datasrc/tests/database_unittest.cc | 6 ++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 8b4a669539..776b7faead 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -74,7 +74,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, connection_.searchForRecords(zone_id_, name.toText()); isc::dns::RRsetPtr result_rrset; - ZoneFinder::Result result_status = NXRRSET; + ZoneFinder::Result result_status = SUCCESS; std::vector columns; while (connection_.getNextRecord(columns)) { @@ -123,9 +123,6 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, getClass(), columns[3])); result_status = CNAME; - } else if (cur_type == isc::dns::RRType::RRSIG()) { - // if we have data already, check covered type - // if not, covered type must be CNAME or type requested } } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f9b8a0a41b..695805a1af 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -202,6 +202,12 @@ TEST_F(DatabaseClientTest, find) { EXPECT_EQ(1, result4.rrset->getRdataCount()); EXPECT_EQ(isc::dns::RRType::CNAME(), result4.rrset->getType()); + ZoneFinder::FindResult result5 = finder->find(isc::dns::Name("doesnotexist.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(ZoneFinder::NXDOMAIN, result5.code); + EXPECT_EQ(isc::dns::ConstRRsetPtr(), result5.rrset); + EXPECT_THROW(finder->find(isc::dns::Name("emptyvector.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), From bc281e8b48c92102d3c64318e07598c8e96e493c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 21:27:38 +0200 Subject: [PATCH 356/974] [trac1062] initial support for RRSIGS for matches and CNAME --- src/lib/datasrc/database.cc | 22 ++++++++++ src/lib/datasrc/database.h | 5 +++ src/lib/datasrc/tests/database_unittest.cc | 49 ++++++++++++++++++++++ src/lib/dns/rdata/generic/rrsig_46.cc | 5 +++ src/lib/dns/rdata/generic/rrsig_46.h | 3 ++ 5 files changed, 84 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 776b7faead..fb8b452467 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -18,6 +18,8 @@ #include #include #include +#include + #include using isc::dns::Name; @@ -123,6 +125,26 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, getClass(), columns[3])); result_status = CNAME; + } else if (cur_type == isc::dns::RRType::RRSIG()) { + isc::dns::rdata::RdataPtr cur_rrsig( + isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); + const isc::dns::RRType& type_covered = + static_cast( + cur_rrsig.get())->typeCovered(); + // Ignore the RRSIG data we got if it does not cover the type + // that was requested or CNAME + // see if we have RRset data yet, and whether it has an RRsig yet + if (type_covered == type || type_covered == isc::dns::RRType::CNAME()) { + if (!result_rrset) { + // no data at all yet, assume the RRset data is coming, and + // that the type covered will match + result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, + getClass(), + type_covered, + cur_ttl)); + } + result_rrset->addRRsig(cur_rrsig); + } } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index a1f566a23f..047db3d090 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -157,6 +157,11 @@ public: /** * \brief Find an RRset in the datasource + * + * target is unused at this point, it was used in the original + * API to store the results for ANY queries, and we may reuse it + * for that, but we might choose a different approach. + * */ virtual FindResult find(const isc::dns::Name& name, const isc::dns::RRType& type, diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 695805a1af..3ad7c6cc1d 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -98,6 +98,7 @@ private: } void fillData() { + // some plain data addRecord("A", "3600", "", "192.0.2.1"); addRecord("AAAA", "3600", "", "2001:db8::1"); addRecord("AAAA", "3600", "", "2001:db8::2"); @@ -105,6 +106,27 @@ private: addRecord("CNAME", "3600", "", "www.example.org."); addCurName("cname.example.org."); + // some DNSSEC-'signed' data + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addRecord("AAAA", "3600", "", "2001:db8::1"); + addRecord("AAAA", "3600", "", "2001:db8::2"); + addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addCurName("signed1.example.org."); + + // let's pretend we have a database that is not careful + // about the order in which it returns data + addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addRecord("AAAA", "3600", "", "2001:db8::2"); + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addRecord("AAAA", "3600", "", "2001:db8::1"); + addCurName("signed2.example.org."); + + addRecord("CNAME", "3600", "", "www.example.org."); + addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addCurName("signedcname.example.org."); + // also add some intentionally bad data cur_name.push_back(std::vector()); addCurName("emptyvector.example.org."); @@ -183,12 +205,14 @@ TEST_F(DatabaseClientTest, find) { ASSERT_EQ(ZoneFinder::SUCCESS, result1.code); EXPECT_EQ(1, result1.rrset->getRdataCount()); EXPECT_EQ(isc::dns::RRType::A(), result1.rrset->getType()); + EXPECT_EQ(isc::dns::RRsetPtr(), result1.rrset->getRRsig()); ZoneFinder::FindResult result2 = finder->find(name, isc::dns::RRType::AAAA(), NULL, ZoneFinder::FIND_DEFAULT); ASSERT_EQ(ZoneFinder::SUCCESS, result2.code); EXPECT_EQ(2, result2.rrset->getRdataCount()); EXPECT_EQ(isc::dns::RRType::AAAA(), result2.rrset->getType()); + EXPECT_EQ(isc::dns::RRsetPtr(), result2.rrset->getRRsig()); ZoneFinder::FindResult result3 = finder->find(name, isc::dns::RRType::TXT(), NULL, ZoneFinder::FIND_DEFAULT); @@ -201,6 +225,7 @@ TEST_F(DatabaseClientTest, find) { ASSERT_EQ(ZoneFinder::CNAME, result4.code); EXPECT_EQ(1, result4.rrset->getRdataCount()); EXPECT_EQ(isc::dns::RRType::CNAME(), result4.rrset->getType()); + EXPECT_EQ(isc::dns::RRsetPtr(), result4.rrset->getRRsig()); ZoneFinder::FindResult result5 = finder->find(isc::dns::Name("doesnotexist.example.org."), isc::dns::RRType::A(), @@ -208,6 +233,30 @@ TEST_F(DatabaseClientTest, find) { ASSERT_EQ(ZoneFinder::NXDOMAIN, result5.code); EXPECT_EQ(isc::dns::ConstRRsetPtr(), result5.rrset); + ZoneFinder::FindResult result6 = finder->find(isc::dns::Name("signed1.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(ZoneFinder::SUCCESS, result6.code); + EXPECT_EQ(1, result6.rrset->getRdataCount()); + EXPECT_EQ(isc::dns::RRType::A(), result6.rrset->getType()); + EXPECT_NE(isc::dns::RRsetPtr(), result6.rrset->getRRsig()); + + ZoneFinder::FindResult result7 = finder->find(isc::dns::Name("signed1.example.org."), + isc::dns::RRType::AAAA(), + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(ZoneFinder::SUCCESS, result7.code); + EXPECT_EQ(2, result7.rrset->getRdataCount()); + EXPECT_EQ(isc::dns::RRType::AAAA(), result7.rrset->getType()); + EXPECT_NE(isc::dns::RRsetPtr(), result7.rrset->getRRsig()); + + ZoneFinder::FindResult result8 = finder->find(isc::dns::Name("signedcname.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(ZoneFinder::SUCCESS, result8.code); + EXPECT_EQ(1, result8.rrset->getRdataCount()); + EXPECT_EQ(isc::dns::RRType::CNAME(), result8.rrset->getType()); + EXPECT_NE(isc::dns::RRsetPtr(), result8.rrset->getRRsig()); + EXPECT_THROW(finder->find(isc::dns::Name("emptyvector.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc index 0c82406895..7d8c000119 100644 --- a/src/lib/dns/rdata/generic/rrsig_46.cc +++ b/src/lib/dns/rdata/generic/rrsig_46.cc @@ -243,5 +243,10 @@ RRSIG::compare(const Rdata& other) const { } } +const RRType& +RRSIG::typeCovered() { + return impl_->covered_; +} + // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/rrsig_46.h b/src/lib/dns/rdata/generic/rrsig_46.h index 19acc40c81..b8e630631e 100644 --- a/src/lib/dns/rdata/generic/rrsig_46.h +++ b/src/lib/dns/rdata/generic/rrsig_46.h @@ -38,6 +38,9 @@ public: // END_COMMON_MEMBERS RRSIG& operator=(const RRSIG& source); ~RRSIG(); + + // specialized methods + const RRType& typeCovered(); private: RRSIGImpl* impl_; }; From dba1e2c7884b5bc68f945fd5d2dd500f9a258c6b Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 21:57:16 +0200 Subject: [PATCH 357/974] [trac1062] refactor/cleanup of tests --- src/lib/datasrc/database.cc | 22 ++-- src/lib/datasrc/tests/database_unittest.cc | 129 ++++++++++++--------- 2 files changed, 83 insertions(+), 68 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index fb8b452467..29a871c231 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -74,7 +74,6 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, { bool records_found = false; connection_.searchForRecords(zone_id_, name.toText()); - isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; @@ -114,13 +113,16 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } else if (cur_type == isc::dns::RRType::CNAME()) { // There should be no other data, so cur_rrset should be empty, // except for signatures - if (result_rrset && result_rrset->getRdataCount() > 0) { - isc_throw(DataSourceError, "CNAME found but it is not the only record for " + name.toText()); + if (result_rrset) { + if (result_rrset->getRdataCount() > 0) { + isc_throw(DataSourceError, "CNAME found but it is not the only record for " + name.toText()); + } + } else { + result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, + getClass(), + cur_type, + cur_ttl)); } - result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, - getClass(), - cur_type, - cur_ttl)); result_rrset->addRdata(isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); @@ -139,9 +141,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // no data at all yet, assume the RRset data is coming, and // that the type covered will match result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, - getClass(), - type_covered, - cur_ttl)); + getClass(), + type_covered, + cur_ttl)); } result_rrset->addRRsig(cur_rrsig); } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 3ad7c6cc1d..eef8103b79 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -113,6 +113,9 @@ private: addRecord("AAAA", "3600", "", "2001:db8::2"); addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("signed1.example.org."); + addRecord("CNAME", "3600", "", "www.example.org."); + addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addCurName("signedcname1.example.org."); // let's pretend we have a database that is not careful // about the order in which it returns data @@ -122,10 +125,9 @@ private: addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addRecord("AAAA", "3600", "", "2001:db8::1"); addCurName("signed2.example.org."); - - addRecord("CNAME", "3600", "", "www.example.org."); addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("signedcname.example.org."); + addRecord("CNAME", "3600", "", "www.example.org."); + addCurName("signedcname2.example.org."); // also add some intentionally bad data cur_name.push_back(std::vector()); @@ -192,6 +194,33 @@ TEST_F(DatabaseClientTest, noConnException) { isc::InvalidParameter); } +namespace { +void +doFindTest(shared_ptr finder, + const isc::dns::Name& name, + const isc::dns::RRType& type, + const isc::dns::RRType& expected_type, + ZoneFinder::Result expected_result, + unsigned int expected_rdata_count, + unsigned int expected_signature_count) +{ + ZoneFinder::FindResult result = finder->find(name, type, + NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(expected_result, result.code) << name.toText() << " " << type.toText(); + if (expected_rdata_count > 0) { + EXPECT_EQ(expected_rdata_count, result.rrset->getRdataCount()); + EXPECT_EQ(expected_type, result.rrset->getType()); + if (expected_signature_count > 0) { + EXPECT_EQ(expected_signature_count, result.rrset->getRRsig()->getRdataCount()); + } else { + EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig()); + } + } else { + EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset); + } +} +} // end anonymous namespace + TEST_F(DatabaseClientTest, find) { DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); ASSERT_EQ(result::SUCCESS, zone.code); @@ -200,62 +229,46 @@ TEST_F(DatabaseClientTest, find) { EXPECT_EQ(42, finder->zone_id()); isc::dns::Name name("www.example.org."); - ZoneFinder::FindResult result1 = finder->find(name, isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(ZoneFinder::SUCCESS, result1.code); - EXPECT_EQ(1, result1.rrset->getRdataCount()); - EXPECT_EQ(isc::dns::RRType::A(), result1.rrset->getType()); - EXPECT_EQ(isc::dns::RRsetPtr(), result1.rrset->getRRsig()); + doFindTest(finder, isc::dns::Name("www.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + ZoneFinder::SUCCESS, 1, 0); + doFindTest(finder, isc::dns::Name("www.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + ZoneFinder::SUCCESS, 2, 0); + doFindTest(finder, isc::dns::Name("www.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + ZoneFinder::NXRRSET, 0, 0); + doFindTest(finder, isc::dns::Name("cname.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::CNAME(), + ZoneFinder::CNAME, 1, 0); + doFindTest(finder, isc::dns::Name("doesnotexist.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + ZoneFinder::NXDOMAIN, 0, 0); + doFindTest(finder, isc::dns::Name("signed1.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + ZoneFinder::SUCCESS, 1, 1); + doFindTest(finder, isc::dns::Name("signed1.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + ZoneFinder::SUCCESS, 2, 1); + doFindTest(finder, isc::dns::Name("signed1.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + ZoneFinder::NXRRSET, 0, 0); + doFindTest(finder, isc::dns::Name("signedcname1.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::CNAME(), + ZoneFinder::CNAME, 1, 1); - ZoneFinder::FindResult result2 = finder->find(name, isc::dns::RRType::AAAA(), - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(ZoneFinder::SUCCESS, result2.code); - EXPECT_EQ(2, result2.rrset->getRdataCount()); - EXPECT_EQ(isc::dns::RRType::AAAA(), result2.rrset->getType()); - EXPECT_EQ(isc::dns::RRsetPtr(), result2.rrset->getRRsig()); - - ZoneFinder::FindResult result3 = finder->find(name, isc::dns::RRType::TXT(), - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(ZoneFinder::NXRRSET, result3.code); - EXPECT_EQ(isc::dns::ConstRRsetPtr(), result3.rrset); - - ZoneFinder::FindResult result4 = finder->find(isc::dns::Name("cname.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(ZoneFinder::CNAME, result4.code); - EXPECT_EQ(1, result4.rrset->getRdataCount()); - EXPECT_EQ(isc::dns::RRType::CNAME(), result4.rrset->getType()); - EXPECT_EQ(isc::dns::RRsetPtr(), result4.rrset->getRRsig()); - - ZoneFinder::FindResult result5 = finder->find(isc::dns::Name("doesnotexist.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(ZoneFinder::NXDOMAIN, result5.code); - EXPECT_EQ(isc::dns::ConstRRsetPtr(), result5.rrset); - - ZoneFinder::FindResult result6 = finder->find(isc::dns::Name("signed1.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(ZoneFinder::SUCCESS, result6.code); - EXPECT_EQ(1, result6.rrset->getRdataCount()); - EXPECT_EQ(isc::dns::RRType::A(), result6.rrset->getType()); - EXPECT_NE(isc::dns::RRsetPtr(), result6.rrset->getRRsig()); - - ZoneFinder::FindResult result7 = finder->find(isc::dns::Name("signed1.example.org."), - isc::dns::RRType::AAAA(), - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(ZoneFinder::SUCCESS, result7.code); - EXPECT_EQ(2, result7.rrset->getRdataCount()); - EXPECT_EQ(isc::dns::RRType::AAAA(), result7.rrset->getType()); - EXPECT_NE(isc::dns::RRsetPtr(), result7.rrset->getRRsig()); - - ZoneFinder::FindResult result8 = finder->find(isc::dns::Name("signedcname.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(ZoneFinder::SUCCESS, result8.code); - EXPECT_EQ(1, result8.rrset->getRdataCount()); - EXPECT_EQ(isc::dns::RRType::CNAME(), result8.rrset->getType()); - EXPECT_NE(isc::dns::RRsetPtr(), result8.rrset->getRRsig()); + doFindTest(finder, isc::dns::Name("signed2.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + ZoneFinder::SUCCESS, 1, 1); + doFindTest(finder, isc::dns::Name("signed2.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + ZoneFinder::SUCCESS, 2, 1); + doFindTest(finder, isc::dns::Name("signed2.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + ZoneFinder::NXRRSET, 0, 0); + doFindTest(finder, isc::dns::Name("signedcname2.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::CNAME(), + ZoneFinder::CNAME, 1, 1); EXPECT_THROW(finder->find(isc::dns::Name("emptyvector.example.org."), isc::dns::RRType::A(), From ce0544bd0852415891cb31e0c1b7d0ba0b3d19f3 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 22:12:04 +0200 Subject: [PATCH 358/974] [trac1062] add some comments in the tests --- src/lib/datasrc/tests/database_unittest.cc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index eef8103b79..7e80b4304c 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -80,6 +80,9 @@ private: // fake data std::vector< std::vector > cur_name; + // Adds one record to the current name in the database + // The actual data will not be added to 'records' until + // addCurName() is called void addRecord(const std::string& name, const std::string& type, const std::string& sigtype, @@ -92,17 +95,32 @@ private: cur_name.push_back(columns); } + // Adds all records we just built with calls to addRecords + // to the actual fake database. This will clear cur_name, + // so we can immediately start adding new records. void addCurName(const std::string& name) { + ASSERT_EQ(0, records.count(name)); records[name] = cur_name; cur_name.clear(); } + // Fills the database with zone data. + // This method constructs a number of resource records (with addRecord), + // which will all be added for one domain name to the fake database + // (with addCurName). So for instance the first set of calls create + // data for the name 'www.example.org', which will consist of one A RRset + // of one record, and one AAAA RRset of two records. + // The order in which they are added is the order in which getNextRecord() + // will return them (so we can test whether find() etc. support data that + // might not come in 'normal' order) + // It shall immediately fail if you try to add the same name twice. void fillData() { // some plain data addRecord("A", "3600", "", "192.0.2.1"); addRecord("AAAA", "3600", "", "2001:db8::1"); addRecord("AAAA", "3600", "", "2001:db8::2"); addCurName("www.example.org."); + addRecord("CNAME", "3600", "", "www.example.org."); addCurName("cname.example.org."); @@ -135,7 +153,6 @@ private: addRecord("A", "3600", "", "192.0.2.1"); addRecord("CNAME", "3600", "", "www.example.org."); addCurName("badcname.example.org."); - } }; From ac9fd0a240cbfa8c448cb01bb69ac92313eb7e56 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 22:14:25 +0200 Subject: [PATCH 359/974] [trac1062] add comment --- src/lib/datasrc/database.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 29a871c231..28cf6d5acf 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -72,11 +72,14 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, isc::dns::RRsetList*, const FindOptions) const { + // This variable is used to determine the difference between + // NXDOMAIN and NXRRSET bool records_found = false; - connection_.searchForRecords(zone_id_, name.toText()); isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; + connection_.searchForRecords(zone_id_, name.toText()); + std::vector columns; while (connection_.getNextRecord(columns)) { if (!records_found) { From 15428e5a9c1bb01f5e7a04979c17ec5f1de9d1db Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Aug 2011 22:28:08 +0200 Subject: [PATCH 360/974] [trac1062] update for change in 1061 --- src/lib/datasrc/database.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 28cf6d5acf..da823a2600 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -78,10 +78,10 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; - connection_.searchForRecords(zone_id_, name.toText()); + connection_->searchForRecords(zone_id_, name.toText()); std::vector columns; - while (connection_.getNextRecord(columns)) { + while (connection_->getNextRecord(columns)) { if (!records_found) { records_found = true; } From 7e1e150e056d0dcf5a58b2a8036f47c2e5dac820 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Aug 2011 15:17:27 +0200 Subject: [PATCH 361/974] [trac1062] use shared_ptr instead of auto_ptr --- src/lib/datasrc/tests/sqlite3_connection_unittest.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 1c80eb5990..1279910114 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -11,6 +11,7 @@ // 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. +#include #include #include @@ -18,6 +19,7 @@ #include #include +#include using namespace isc::datasrc; using isc::data::ConstElementPtr; @@ -74,7 +76,7 @@ public: conn.reset(new SQLite3Connection(filename, rrclass)); } // The tested connection - std::auto_ptr conn; + boost::shared_ptr conn; }; // This zone exists in the data, so it should be found @@ -103,7 +105,7 @@ TEST_F(SQLite3Conn, noClass) { namespace { // Simple function to cound the number of records for // any name - size_t countRecords(std::auto_ptr& conn, + size_t countRecords(boost::shared_ptr& conn, int zone_id, const std::string& name) { conn->searchForRecords(zone_id, name); size_t count = 0; From cce2a00af57ef823abeaeff787eff35f43dfb093 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 5 Aug 2011 15:18:00 +0200 Subject: [PATCH 362/974] [1067] DatabaseClient part of iteration The implementation of the ZoneIterator for DatabaseClient (it isn't publicly visible, it is hidden in the .cc file) and tests for it. --- src/lib/datasrc/database.cc | 98 ++++++++- src/lib/datasrc/database.h | 3 +- src/lib/datasrc/tests/database_unittest.cc | 218 ++++++++++++++++++++- 3 files changed, 312 insertions(+), 7 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 5fe9f7ba8e..2b366b22da 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -13,11 +13,18 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include +#include #include #include +#include +#include -using isc::dns::Name; +#include + +using namespace isc::dns; +using std::string; namespace isc { namespace datasrc { @@ -81,5 +88,94 @@ DatabaseClient::Finder::getClass() const { return isc::dns::RRClass::IN(); } +namespace { + +/* + * This needs, beside of converting all data from textual representation, group + * together rdata of the same RRsets. To do this, we hold one row of data ahead + * of iteration. When we get a request to provide data, we create it from this + * data and load a new one. If it is to be put to the same rrset, we add it. + * Otherwise we just return what we have and keep the row as the one ahead + * for next time. + */ +class Iterator : public ZoneIterator { +public: + Iterator(const DatabaseConnection::IteratorContextPtr& context, + const RRClass& rrclass) : + context_(context), + class_(rrclass), + ready_(true) + { + // Prepare data for the next time + getData(); + } + virtual isc::dns::ConstRRsetPtr getNextRRset() { + if (!ready_) { + isc_throw(isc::Unexpected, "Iterating past the zone end"); + } + if (!data_ready_) { + // At the end of zone + ready_ = false; + return (ConstRRsetPtr()); + } + string nameStr(name_), rtypeStr(rtype_); + int ttl(ttl_); + Name name(nameStr); + RRType rtype(rtypeStr); + RRsetPtr rrset(new RRset(name, class_, rtype, RRTTL(ttl))); + while (data_ready_ && name_ == nameStr && rtypeStr == rtype_) { + if (ttl_ != ttl) { + isc_throw(DataSourceError, "TTLs in rrset " + nameStr + "/" + + rtypeStr + " differ"); + } + rrset->addRdata(rdata::createRdata(rtype, class_, rdata_)); + getData(); + } + return (rrset); + } +private: + // Load next row of data + void getData() { + data_ready_ = context_->getNext(name_, rtype_, ttl_, rdata_); + } + // The context + const DatabaseConnection::IteratorContextPtr context_; + // Class of the zone + RRClass class_; + // Status + bool ready_, data_ready_; + // Data of the next row + string name_, rtype_, rdata_; + int ttl_; +}; + +} + +ZoneIteratorPtr +DatabaseClient::getIterator(const isc::dns::Name& name) const { + // Get the zone + std::pair zone(connection_->getZone(name)); + if (!zone.first) { + // No such zone, can't continue + isc_throw(DataSourceError, "Zone " + name.toText() + + " can not be iterated, because it doesn't exist " + "in this data source"); + } + // Request the context + DatabaseConnection::IteratorContextPtr + context(connection_->getIteratorContext(name, zone.second)); + // It must not return NULL, that's a bug of the implementation + if (context == DatabaseConnection::IteratorContextPtr()) { + isc_throw(isc::Unexpected, "Iterator context null at " + + name.toText()); + } + // Create the iterator and return it + // TODO: Once #1062 is merged with this, we need to get the + // actual zone class from the connection, as the DatabaseClient + // doesn't know it and the iterator needs it (so it wouldn't query + // it each time) + return (ZoneIteratorPtr(new Iterator(context, RRClass::IN()))); +} + } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index b44211daab..fa4ce83880 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -271,7 +271,8 @@ public: * * \exception DataSourceError if the zone doesn't exist. * \exception isc::NotImplemented if the underlying DatabaseConnection - * doesn't implement iteration. + * doesn't implement iteration. But in case it is not implemented + * and the zone doesn't exist, DataSourceError is thrown. * \exception Anything else the underlying DatabaseConnection might * want to throw. * \param name The origin of the zone to iterate. diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 4a160d1b20..7e99a17f4a 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -15,36 +15,157 @@ #include #include +#include #include #include +#include +#include using namespace isc::datasrc; using namespace std; using namespace boost; -using isc::dns::Name; +using namespace isc::dns; namespace { /* - * A virtual database connection that pretends it contains single zone -- - * example.org. + * A connection with minimum implementation, keeping the original + * "NotImplemented" methods. */ -class MockConnection : public DatabaseConnection { +class NopConnection : public DatabaseConnection { public: virtual std::pair getZone(const Name& name) const { if (name == Name("example.org")) { return (std::pair(true, 42)); + } else if (name == Name("null.example.org")) { + return (std::pair(true, 13)); + } else if (name == Name("empty.example.org")) { + return (std::pair(true, 0)); + } else if (name == Name("bad.example.org")) { + return (std::pair(true, -1)); } else { return (std::pair(false, 0)); } } }; +/* + * A virtual database connection that pretends it contains single zone -- + * example.org. + * + * It has the same getZone method as NopConnection, but it provides + * implementation of the optional functionality. + */ +class MockConnection : public NopConnection { +private: + class MockIteratorContext : public IteratorContext { + private: + int step; + public: + MockIteratorContext() : + step(0) + { } + virtual bool getNext(string& name, string& rtype, int& ttl, + string& data) + { + switch (step ++) { + case 0: + name = "example.org"; + rtype = "SOA"; + ttl = 300; + data = "ns1.example.org. admin.example.org. " + "1234 3600 1800 2419200 7200"; + return (true); + case 1: + name = "x.example.org"; + rtype = "A"; + ttl = 300; + data = "192.0.2.1"; + return (true); + case 2: + name = "x.example.org"; + rtype = "A"; + ttl = 300; + data = "192.0.2.2"; + return (true); + case 3: + name = "x.example.org"; + rtype = "AAAA"; + ttl = 300; + data = "2001:db8::1"; + return (true); + case 4: + name = "x.example.org"; + rtype = "AAAA"; + ttl = 300; + data = "2001:db8::2"; + return (true); + default: + ADD_FAILURE() << + "Request past the end of iterator context"; + case 5: + return (false); + } + } + }; + class EmptyIteratorContext : public IteratorContext { + public: + virtual bool getNext(string&, string&, int&, string&) { + return (false); + } + }; + class BadIteratorContext : public IteratorContext { + private: + int step; + public: + BadIteratorContext() : + step(0) + { } + virtual bool getNext(string& name, string& rtype, int& ttl, + string& data) + { + switch (step ++) { + case 0: + name = "x.example.org"; + rtype = "A"; + ttl = 300; + data = "192.0.2.1"; + return (true); + case 1: + name = "x.example.org"; + rtype = "A"; + ttl = 301; + data = "192.0.2.2"; + return (true); + default: + ADD_FAILURE() << + "Request past the end of iterator context"; + case 2: + return (false); + } + } + }; +public: + virtual IteratorContextPtr getIteratorContext(const Name&, int id) const { + if (id == 42) { + return (IteratorContextPtr(new MockIteratorContext())); + } else if (id == 13) { + return (IteratorContextPtr()); + } else if (id == 0) { + return (IteratorContextPtr(new EmptyIteratorContext())); + } else if (id == -1) { + return (IteratorContextPtr(new BadIteratorContext())); + } else { + isc_throw(isc::Unexpected, "Unknown zone ID"); + } + } +}; + // This tests the default getIteratorContext behaviour, throwing NotImplemented TEST(DatabaseConnectionTest, getIteratorContext) { // The parameters don't matter - EXPECT_THROW(MockConnection().getIteratorContext(Name("."), 1), + EXPECT_THROW(NopConnection().getIteratorContext(Name("."), 1), isc::NotImplemented); } @@ -103,4 +224,91 @@ TEST_F(DatabaseClientTest, noConnException) { isc::InvalidParameter); } +// If the zone doesn't exist, exception is thrown +TEST_F(DatabaseClientTest, noZoneIterator) { + EXPECT_THROW(client_->getIterator(Name("example.com")), DataSourceError); +} + +// If the zone doesn't exist and iteration is not implemented, it still throws +// the exception it doesn't exist +TEST_F(DatabaseClientTest, noZoneNotImplementedIterator) { + EXPECT_THROW(DatabaseClient(auto_ptr( + new NopConnection())).getIterator(Name("example.com")), + DataSourceError); +} + +TEST_F(DatabaseClientTest, notImplementedIterator) { + EXPECT_THROW(DatabaseClient(auto_ptr( + new NopConnection())).getIterator(Name("example.org")), + isc::NotImplemented); +} + +// Pretend a bug in the connection and pass NULL as the context +// Should not crash, but gracefully throw +TEST_F(DatabaseClientTest, nullIteratorContext) { + EXPECT_THROW(client_->getIterator(Name("null.example.org")), + isc::Unexpected); +} + +// It doesn't crash or anything if the zone is completely empty +TEST_F(DatabaseClientTest, emptyIterator) { + ZoneIteratorPtr it(client_->getIterator(Name("empty.example.org"))); + EXPECT_EQ(ConstRRsetPtr(), it->getNextRRset()); + // This is past the end, it should throw + EXPECT_THROW(it->getNextRRset(), isc::Unexpected); +} + +// Iterate trough a zone +TEST_F(DatabaseClientTest, iterator) { + ZoneIteratorPtr it(client_->getIterator(Name("example.org"))); + ConstRRsetPtr rrset(it->getNextRRset()); + ASSERT_NE(ConstRRsetPtr(), rrset); + EXPECT_EQ(Name("example.org"), rrset->getName()); + EXPECT_EQ(RRClass::IN(), rrset->getClass()); + EXPECT_EQ(RRType::SOA(), rrset->getType()); + EXPECT_EQ(RRTTL(300), rrset->getTTL()); + RdataIteratorPtr rit(rrset->getRdataIterator()); + ASSERT_FALSE(rit->isLast()); + rit->next(); + EXPECT_TRUE(rit->isLast()); + + rrset = it->getNextRRset(); + ASSERT_NE(ConstRRsetPtr(), rrset); + EXPECT_EQ(Name("x.example.org"), rrset->getName()); + EXPECT_EQ(RRClass::IN(), rrset->getClass()); + EXPECT_EQ(RRType::A(), rrset->getType()); + EXPECT_EQ(RRTTL(300), rrset->getTTL()); + rit = rrset->getRdataIterator(); + ASSERT_FALSE(rit->isLast()); + EXPECT_EQ("192.0.2.1", rit->getCurrent().toText()); + rit->next(); + ASSERT_FALSE(rit->isLast()); + EXPECT_EQ("192.0.2.2", rit->getCurrent().toText()); + rit->next(); + EXPECT_TRUE(rit->isLast()); + + rrset = it->getNextRRset(); + ASSERT_NE(ConstRRsetPtr(), rrset); + EXPECT_EQ(Name("x.example.org"), rrset->getName()); + EXPECT_EQ(RRClass::IN(), rrset->getClass()); + EXPECT_EQ(RRType::AAAA(), rrset->getType()); + EXPECT_EQ(RRTTL(300), rrset->getTTL()); + EXPECT_EQ(ConstRRsetPtr(), it->getNextRRset()); + rit = rrset->getRdataIterator(); + ASSERT_FALSE(rit->isLast()); + EXPECT_EQ("2001:db8::1", rit->getCurrent().toText()); + rit->next(); + ASSERT_FALSE(rit->isLast()); + EXPECT_EQ("2001:db8::2", rit->getCurrent().toText()); + rit->next(); + EXPECT_TRUE(rit->isLast()); +} + +// This has inconsistent TTL in the set (the rest, like nonsense in +// the data is handled in rdata itself). +TEST_F(DatabaseClientTest, badIterator) { + ZoneIteratorPtr it(client_->getIterator(Name("bad.example.org"))); + EXPECT_THROW(it->getNextRRset(), DataSourceError); +} + } From 9a98be99edd71e540bd65631dcbd3d766f93056e Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Fri, 5 Aug 2011 10:42:49 -0500 Subject: [PATCH 363/974] [master] minor grammar improvements --- src/lib/asiodns/asiodns_messages.mes | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/asiodns/asiodns_messages.mes b/src/lib/asiodns/asiodns_messages.mes index 3e11ede159..feb75d44fc 100644 --- a/src/lib/asiodns/asiodns_messages.mes +++ b/src/lib/asiodns/asiodns_messages.mes @@ -26,13 +26,13 @@ enabled. % ASIODNS_OPEN_SOCKET error %1 opening %2 socket to %3(%4) The asynchronous I/O code encountered an error when trying to open a socket of the specified protocol in order to send a message to the target address. -The number of the system error that cause the problem is given in the +The number of the system error that caused the problem is given in the message. % ASIODNS_READ_DATA error %1 reading %2 data from %3(%4) The asynchronous I/O code encountered an error when trying to read data from the specified address on the given protocol. The number of the system -error that cause the problem is given in the message. +error that caused the problem is given in the message. % ASIODNS_READ_TIMEOUT receive timeout while waiting for data from %1(%2) An upstream fetch from the specified address timed out. This may happen for @@ -41,9 +41,9 @@ or a problem on the network. The message will only appear if debug is enabled. % ASIODNS_SEND_DATA error %1 sending data using %2 to %3(%4) -The asynchronous I/O code encountered an error when trying send data to -the specified address on the given protocol. The the number of the system -error that cause the problem is given in the message. +The asynchronous I/O code encountered an error when trying to send data to +the specified address on the given protocol. The number of the system +error that caused the problem is given in the message. % ASIODNS_UNKNOWN_ORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3) An internal consistency check on the origin of a message from the From 5f13949918d125f851bd2ba8ab092c301835d3ac Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Aug 2011 18:02:33 +0200 Subject: [PATCH 364/974] [trac1062] refactor and fixes - check for bad data in the db - work with data in 'bad' order (sigs before data for example) --- src/lib/datasrc/database.cc | 185 ++++++++++++++------- src/lib/datasrc/tests/database_unittest.cc | 50 +++++- 2 files changed, 172 insertions(+), 63 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index da823a2600..69a46108e8 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -22,6 +22,8 @@ #include +#include + using isc::dns::Name; namespace isc { @@ -66,6 +68,88 @@ DatabaseClient::Finder::Finder(boost::shared_ptr zone_id_(zone_id) { } +namespace { + // Adds the given Rdata to the given RRset + // If the rrset does not exist, one is created + // adds the given rdata to the set + void addOrCreate(isc::dns::RRsetPtr& rrset, + const isc::dns::Name& name, + const isc::dns::RRClass& cls, + const isc::dns::RRType& type, + const isc::dns::RRTTL& ttl, + const std::string& rdata_str) + { + if (!rrset) { + rrset.reset(new isc::dns::RRset(name, cls, type, ttl)); + } else { + if (ttl < rrset->getTTL()) { + rrset->setTTL(ttl); + } + // make sure the type is correct + if (type != rrset->getType()) { + isc_throw(DataSourceError, + "attempt to add multiple types to RRset in find()"); + } + } + if (rdata_str != "") { + try { + rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str)); + } catch (const isc::dns::rdata::InvalidRdataText& ivrt) { + // at this point, rrset may have been initialised for no reason, + // and won't be used. But the caller would drop the shared_ptr + // on such an error anyway, so we don't care. + isc_throw(DataSourceError, + "bad rdata in database for " << name.toText() << " " + << type.toText() << " " << ivrt.what()); + } + } + } + + // This class keeps a short-lived store of RRSIG records encountered + // during a call to find(). If the backend happens to return signatures + // before the actual data, we might not know which signatures we will need + // So if they may be relevant, we store the in this class. + // + // (If this class seems useful in other places, we might want to move + // it to util. That would also provide an opportunity to add unit tests) + class RRsigStore { + public: + // add the given signature Rdata to the store + // The signature MUST be of the RRSIG type (the caller + // must make sure of this) + void addSig(isc::dns::rdata::RdataPtr sig_rdata) { + const isc::dns::RRType& type_covered = + static_cast( + sig_rdata.get())->typeCovered(); + if (!haveSigsFor(type_covered)) { + sigs[type_covered] = std::vector(); + } + sigs.find(type_covered)->second.push_back(sig_rdata); + } + + // Returns true if this store contains signatures covering the + // given type + bool haveSigsFor(isc::dns::RRType type) { + return (sigs.count(type) > 0); + } + + // If the store contains signatures for the type of the given + // rrset, they are appended to it. + void appendSignatures(isc::dns::RRsetPtr& rrset) { + if (haveSigsFor(rrset->getType())) { + BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, + sigs.find(rrset->getType())->second) { + rrset->addRRsig(sig); + } + } + } + + private: + std::map > sigs; + }; +} + + ZoneFinder::FindResult DatabaseClient::Finder::find(const isc::dns::Name& name, const isc::dns::RRType& type, @@ -77,6 +161,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, bool records_found = false; isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; + RRsigStore sig_store; connection_->searchForRecords(zone_id_, name.toText()); @@ -91,75 +176,53 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, "Datasource backend did not return 4 columns in getNextRecord()"); } - const isc::dns::RRType cur_type(columns[0]); - const isc::dns::RRTTL cur_ttl(columns[1]); - //cur_sigtype(columns[2]); + try { + const isc::dns::RRType cur_type(columns[0]); + const isc::dns::RRTTL cur_ttl(columns[1]); + //cur_sigtype(columns[2]); - if (cur_type == type) { - if (!result_rrset) { - result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, - getClass(), - cur_type, - cur_ttl)); - result_status = SUCCESS; - } else { - // We have existing data from earlier calls, do some checks - // and updates if necessary - if (cur_ttl < result_rrset->getTTL()) { - result_rrset->setTTL(cur_ttl); + if (cur_type == type) { + addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]); + //isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); + } else if (cur_type == isc::dns::RRType::CNAME()) { + // There should be no other data, so cur_rrset should be empty, + // except for signatures, of course + if (result_rrset) { + if (result_rrset->getRdataCount() > 0) { + isc_throw(DataSourceError, "CNAME found but it is not the only record for " + name.toText()); + } } + addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]); + //isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); + result_status = CNAME; + } else if (cur_type == isc::dns::RRType::RRSIG()) { + // If we get signatures before we get the actual data, we can't know + // which ones to keep and which to drop... + // So we keep a separate store of any signature that may be relevant + // and add them to the final RRset when we are done. + isc::dns::rdata::RdataPtr cur_rrsig( + isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); + sig_store.addSig(cur_rrsig); } - - result_rrset->addRdata(isc::dns::rdata::createRdata(cur_type, - getClass(), - columns[3])); - } else if (cur_type == isc::dns::RRType::CNAME()) { - // There should be no other data, so cur_rrset should be empty, - // except for signatures - if (result_rrset) { - if (result_rrset->getRdataCount() > 0) { - isc_throw(DataSourceError, "CNAME found but it is not the only record for " + name.toText()); - } - } else { - result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, - getClass(), - cur_type, - cur_ttl)); - } - result_rrset->addRdata(isc::dns::rdata::createRdata(cur_type, - getClass(), - columns[3])); - result_status = CNAME; - } else if (cur_type == isc::dns::RRType::RRSIG()) { - isc::dns::rdata::RdataPtr cur_rrsig( - isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); - const isc::dns::RRType& type_covered = - static_cast( - cur_rrsig.get())->typeCovered(); - // Ignore the RRSIG data we got if it does not cover the type - // that was requested or CNAME - // see if we have RRset data yet, and whether it has an RRsig yet - if (type_covered == type || type_covered == isc::dns::RRType::CNAME()) { - if (!result_rrset) { - // no data at all yet, assume the RRset data is coming, and - // that the type covered will match - result_rrset = isc::dns::RRsetPtr(new isc::dns::RRset(name, - getClass(), - type_covered, - cur_ttl)); - } - result_rrset->addRRsig(cur_rrsig); - } + } catch (const isc::dns::InvalidRRType& irt) { + isc_throw(DataSourceError, + "Invalid RRType in database for " << name << ": " << columns[0]); + } catch (const isc::dns::InvalidRRTTL& irttl) { + isc_throw(DataSourceError, + "Invalid TTL in database for " << name << ": " << columns[1]); } } - if (result_rrset) { - return (FindResult(result_status, result_rrset)); - } else if (records_found) { - return (FindResult(NXRRSET, isc::dns::ConstRRsetPtr())); + if (!result_rrset) { + if (records_found) { + result_status = NXRRSET; + } else { + result_status = NXDOMAIN; + } } else { - return (FindResult(NXDOMAIN, isc::dns::ConstRRsetPtr())); + sig_store.appendSignatures(result_rrset); } + return (FindResult(result_status, result_rrset)); } Name diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 7e80b4304c..76c801c702 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -127,6 +127,7 @@ private: // some DNSSEC-'signed' data addRecord("A", "3600", "", "192.0.2.1"); addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); addRecord("AAAA", "3600", "", "2001:db8::1"); addRecord("AAAA", "3600", "", "2001:db8::2"); addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); @@ -134,11 +135,18 @@ private: addRecord("CNAME", "3600", "", "www.example.org."); addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("signedcname1.example.org."); + // special case might fail; sig is for cname, which isn't there (should be ignored) + // (ignoring of 'normal' other type is done above by www.) + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addCurName("acnamesig1.example.org."); // let's pretend we have a database that is not careful // about the order in which it returns data addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addRecord("AAAA", "3600", "", "2001:db8::2"); + addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); addRecord("A", "3600", "", "192.0.2.1"); addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addRecord("AAAA", "3600", "", "2001:db8::1"); @@ -147,12 +155,28 @@ private: addRecord("CNAME", "3600", "", "www.example.org."); addCurName("signedcname2.example.org."); + addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addCurName("acnamesig2.example.org."); + + addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addRecord("A", "3600", "", "192.0.2.1"); + addCurName("acnamesig3.example.org."); + // also add some intentionally bad data cur_name.push_back(std::vector()); addCurName("emptyvector.example.org."); addRecord("A", "3600", "", "192.0.2.1"); addRecord("CNAME", "3600", "", "www.example.org."); addCurName("badcname.example.org."); + addRecord("A", "3600", "", "bad"); + addCurName("badrdata.example.org."); + addRecord("BAD_TYPE", "3600", "", "192.0.2.1"); + addCurName("badtype.example.org."); + addRecord("A", "badttl", "", "192.0.2.1"); + addCurName("badttl.example.org."); } }; @@ -263,7 +287,7 @@ TEST_F(DatabaseClientTest, find) { ZoneFinder::NXDOMAIN, 0, 0); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), - ZoneFinder::SUCCESS, 1, 1); + ZoneFinder::SUCCESS, 1, 2); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), ZoneFinder::SUCCESS, 2, 1); @@ -276,7 +300,7 @@ TEST_F(DatabaseClientTest, find) { doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), - ZoneFinder::SUCCESS, 1, 1); + ZoneFinder::SUCCESS, 1, 2); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), ZoneFinder::SUCCESS, 2, 1); @@ -287,6 +311,16 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRType::A(), isc::dns::RRType::CNAME(), ZoneFinder::CNAME, 1, 1); + doFindTest(finder, isc::dns::Name("acnamesig1.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + ZoneFinder::SUCCESS, 1, 1); + doFindTest(finder, isc::dns::Name("acnamesig2.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + ZoneFinder::SUCCESS, 1, 1); + doFindTest(finder, isc::dns::Name("acnamesig3.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + ZoneFinder::SUCCESS, 1, 1); + EXPECT_THROW(finder->find(isc::dns::Name("emptyvector.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), @@ -295,6 +329,18 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); + EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); } From 86257c05755c8adbb19ce684546b718dd48a5ef8 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Aug 2011 18:24:20 +0200 Subject: [PATCH 365/974] [trac1062] minor style cleanup --- src/lib/datasrc/database.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 69a46108e8..dc688325fa 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -183,23 +183,25 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, if (cur_type == type) { addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]); - //isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); } else if (cur_type == isc::dns::RRType::CNAME()) { // There should be no other data, so cur_rrset should be empty, // except for signatures, of course if (result_rrset) { if (result_rrset->getRdataCount() > 0) { - isc_throw(DataSourceError, "CNAME found but it is not the only record for " + name.toText()); + isc_throw(DataSourceError, + "CNAME found but it is not the only record for " + + name.toText()); } } addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]); - //isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); result_status = CNAME; } else if (cur_type == isc::dns::RRType::RRSIG()) { // If we get signatures before we get the actual data, we can't know // which ones to keep and which to drop... // So we keep a separate store of any signature that may be relevant // and add them to the final RRset when we are done. + // A possible optimization here is to not store them for types we + // are certain we don't need isc::dns::rdata::RdataPtr cur_rrsig( isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); sig_store.addSig(cur_rrsig); From fc33ec0a47dce3e94fa7179d4d28d7fd050a258d Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Fri, 5 Aug 2011 11:41:37 -0500 Subject: [PATCH 366/974] [master] various minor fixes: grammar, spelling, punctuation, case --- src/bin/auth/auth_messages.mes | 4 ++-- src/bin/bind10/bind10_messages.mes | 2 +- src/bin/cmdctl/cmdctl_messages.mes | 2 +- src/bin/resolver/resolver_messages.mes | 4 ++-- src/bin/stats/stats_httpd_messages.mes | 12 ++++++------ src/bin/xfrout/xfrout_messages.mes | 2 +- src/bin/zonemgr/zonemgr_messages.mes | 2 +- src/lib/cc/cc_messages.mes | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes index 2bb402cb20..527d100db4 100644 --- a/src/bin/auth/auth_messages.mes +++ b/src/bin/auth/auth_messages.mes @@ -141,8 +141,8 @@ encountered an internal error whilst processing a received packet: the cause of the error is included in the message. The server will return a SERVFAIL error code to the sender of the packet. -However, this message indicates a potential error in the server. -Please open a bug ticket for this issue. +This message indicates a potential error in the server. Please open a +bug ticket for this issue. % AUTH_RECEIVED_COMMAND command '%1' received This is a debug message issued when the authoritative server has received diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index e10bc7c7b7..4bac069098 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -122,7 +122,7 @@ The boss requested a socket from the creator, but the answer is unknown. This looks like a programmer error. % BIND10_SOCKCREATOR_CRASHED the socket creator crashed -The socket creator terminated unexpectadly. It is not possible to restart it +The socket creator terminated unexpectedly. It is not possible to restart it (because the boss already gave up root privileges), so the system is going to terminate. diff --git a/src/bin/cmdctl/cmdctl_messages.mes b/src/bin/cmdctl/cmdctl_messages.mes index 55b941f1a1..e00729676d 100644 --- a/src/bin/cmdctl/cmdctl_messages.mes +++ b/src/bin/cmdctl/cmdctl_messages.mes @@ -69,7 +69,7 @@ There was a keyboard interrupt signal to stop the cmdctl daemon. The daemon will now shut down. % CMDCTL_UNCAUGHT_EXCEPTION uncaught exception: %1 -The b10-cdmctl daemon encountered an uncaught exception and +The b10-cmdctl daemon encountered an uncaught exception and will now shut down. This is indicative of a programming error and should not happen under normal circumstances. The exception message is printed. diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes index b44115ae91..7930c52a84 100644 --- a/src/bin/resolver/resolver_messages.mes +++ b/src/bin/resolver/resolver_messages.mes @@ -78,7 +78,7 @@ specified, it will appear once for each address. % RESOLVER_FORWARD_QUERY processing forward query This is a debug message indicating that a query received by the resolver has passed a set of checks (message is well-formed, it is allowed by the -ACL, it is a supported opcode etc.) and is being forwarded to upstream +ACL, it is a supported opcode, etc.) and is being forwarded to upstream servers. % RESOLVER_HEADER_ERROR message received, exception when processing header: %1 @@ -116,7 +116,7 @@ so is returning a REFUSED response to the sender. % RESOLVER_NORMAL_QUERY processing normal query This is a debug message indicating that the query received by the resolver has passed a set of checks (message is well-formed, it is allowed by the -ACL, it is a supported opcode etc.) and is being processed the resolver. +ACL, it is a supported opcode, etc.) and is being processed by the resolver. % RESOLVER_NOTIFY_RECEIVED NOTIFY arrived but server is not authoritative The resolver has received a NOTIFY message. As the server is not diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes index d0f7e2cb3c..0e984dc27f 100644 --- a/src/bin/stats/stats_httpd_messages.mes +++ b/src/bin/stats/stats_httpd_messages.mes @@ -49,14 +49,14 @@ An unknown command has been sent to the stats-httpd module. The stats-httpd module will respond with an error, and the command will be ignored. -% STATHTTPD_SERVER_ERROR http server error: %1 -An internal error occurred while handling an http request. A HTTP 500 +% STATHTTPD_SERVER_ERROR HTTP server error: %1 +An internal error occurred while handling an HTTP request. An HTTP 500 response will be sent back, and the specific error is printed. This is an error condition that likely points to a module that is not responding correctly to statistic requests. -% STATHTTPD_SERVER_INIT_ERROR http server initialization error: %1 -There was a problem initializing the http server in the stats-httpd +% STATHTTPD_SERVER_INIT_ERROR HTTP server initialization error: %1 +There was a problem initializing the HTTP server in the stats-httpd module upon receiving its configuration data. The most likely cause is a port binding problem or a bad configuration value. The specific error is printed in the message. The new configuration is ignored, @@ -65,8 +65,8 @@ and an error is sent back. % STATHTTPD_SHUTDOWN shutting down The stats-httpd daemon is shutting down. -% STATHTTPD_START_SERVER_INIT_ERROR http server initialization error: %1 -There was a problem initializing the http server in the stats-httpd +% STATHTTPD_START_SERVER_INIT_ERROR HTTP server initialization error: %1 +There was a problem initializing the HTTP server in the stats-httpd module upon startup. The most likely cause is that it was not able to bind to the listening port. The specific error is printed, and the module will shut down. diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes index 19b104ee7c..121b2adf4b 100644 --- a/src/bin/xfrout/xfrout_messages.mes +++ b/src/bin/xfrout/xfrout_messages.mes @@ -48,7 +48,7 @@ There was a problem reading from the command and control channel. The most likely cause is that the msgq daemon is not running. % XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response -There was a problem reading a response from antoher module over the +There was a problem reading a response from another module over the command and control channel. The most likely cause is that the configuration manager b10-cfgmgr is not running. diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes index d15ce05251..8abec5d802 100644 --- a/src/bin/zonemgr/zonemgr_messages.mes +++ b/src/bin/zonemgr/zonemgr_messages.mes @@ -64,7 +64,7 @@ This is a debug message indicating that the zone manager has received a NOTIFY command over the command channel. The command is sent by the Auth process when it is acting as a slave server for the zone and causes the zone manager to record the master server for the zone and start a timer; -when the time rexpires, the master will be polled to see if it contains +when the timer expires, the master will be polled to see if it contains new data. % ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command diff --git a/src/lib/cc/cc_messages.mes b/src/lib/cc/cc_messages.mes index 8c62ea101b..8370cdd03c 100644 --- a/src/lib/cc/cc_messages.mes +++ b/src/lib/cc/cc_messages.mes @@ -53,11 +53,11 @@ Debug message, we're about to send a message over the command channel. This happens when garbage comes over the command channel or some kind of confusion happens in the program. The data received from the socket make no sense if we interpret it as lengths of message. The first one is total length -of message, the second length of the header. The header and it's length -(2 bytes) is counted in the total length. +of the message; the second is the length of the header. The header +and its length (2 bytes) is counted in the total length. % CC_LENGTH_NOT_READY length not ready -There should be data representing length of message on the socket, but it +There should be data representing the length of message on the socket, but it is not there. % CC_NO_MESSAGE no message ready to be received yet From 3027ed2010e5e27ef6e8ba519b789269100f442e Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Fri, 5 Aug 2011 11:46:51 -0500 Subject: [PATCH 367/974] [master] spelling fixes --- src/bin/auth/auth_messages.mes | 6 +++--- src/lib/python/isc/notify/notify_out_messages.mes | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes index 527d100db4..9f04b76264 100644 --- a/src/bin/auth/auth_messages.mes +++ b/src/bin/auth/auth_messages.mes @@ -63,7 +63,7 @@ datebase data source, listing the file that is being accessed. % AUTH_DNS_SERVICES_CREATED DNS services created This is a debug message indicating that the component that will handling -incoming queries for the authoritiative server (DNSServices) has been +incoming queries for the authoritative server (DNSServices) has been successfully created. It is issued during server startup is an indication that the initialization is proceeding normally. @@ -74,7 +74,7 @@ reason for the failure is given in the message.) The server will drop the packet. % AUTH_LOAD_TSIG loading TSIG keys -This is a debug message indicating that the authoritiative server +This is a debug message indicating that the authoritative server has requested the keyring holding TSIG keys from the configuration database. It is issued during server startup is an indication that the initialization is proceeding normally. @@ -209,7 +209,7 @@ channel. It is issued during server startup is an indication that the initialization is proceeding normally. % AUTH_STATS_COMMS communication error in sending statistics data: %1 -An error was encountered when the authoritiative server tried to send data +An error was encountered when the authoritative server tried to send data to the statistics daemon. The message includes additional information describing the reason for the failure. diff --git a/src/lib/python/isc/notify/notify_out_messages.mes b/src/lib/python/isc/notify/notify_out_messages.mes index f9de744b00..570f51e0e9 100644 --- a/src/lib/python/isc/notify/notify_out_messages.mes +++ b/src/lib/python/isc/notify/notify_out_messages.mes @@ -78,6 +78,6 @@ message, either in the message parser, or while trying to extract data from the parsed message. The error is printed, and notify_out will treat the response as a bad message, but this does point to a programming error, since all exceptions should have been caught -explicitely. Please file a bug report. Since there was a response, +explicitly. Please file a bug report. Since there was a response, no more notifies will be sent to this server for this notification event. From 69642fb8f55cb4741f977d3fbaacd5d12d742625 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Aug 2011 19:09:36 +0200 Subject: [PATCH 368/974] [trac1062] some comment updates --- src/lib/datasrc/database.cc | 27 ++++++++++++++++----------- src/lib/datasrc/database.h | 24 +++++++++++++++++++++--- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index dc688325fa..3002056338 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -70,8 +70,16 @@ DatabaseClient::Finder::Finder(boost::shared_ptr namespace { // Adds the given Rdata to the given RRset - // If the rrset does not exist, one is created - // adds the given rdata to the set + // If the rrset is an empty pointer, a new one is + // created with the given name, class, type and ttl + // The type is checked if the rrset exists, but the + // name is not. + // + // Then adds the given rdata to the set + // + // Raises a DataSourceError if the type does not + // match, or if the given rdata string does not + // parse correctly for the given type and class void addOrCreate(isc::dns::RRsetPtr& rrset, const isc::dns::Name& name, const isc::dns::RRClass& cls, @@ -114,9 +122,9 @@ namespace { // it to util. That would also provide an opportunity to add unit tests) class RRsigStore { public: - // add the given signature Rdata to the store - // The signature MUST be of the RRSIG type (the caller - // must make sure of this) + // Adds the given signature Rdata to the store + // The signature rdata MUST be of the RRSIG rdata type + // (the caller must make sure of this) void addSig(isc::dns::rdata::RdataPtr sig_rdata) { const isc::dns::RRType& type_covered = static_cast( @@ -185,13 +193,10 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]); } else if (cur_type == isc::dns::RRType::CNAME()) { // There should be no other data, so cur_rrset should be empty, - // except for signatures, of course if (result_rrset) { - if (result_rrset->getRdataCount() > 0) { - isc_throw(DataSourceError, - "CNAME found but it is not the only record for " + - name.toText()); - } + isc_throw(DataSourceError, + "CNAME found but it is not the only record for " + + name.toText()); } addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]); result_status = CNAME; diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 047db3d090..5498694687 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -158,16 +158,34 @@ public: /** * \brief Find an RRset in the datasource * - * target is unused at this point, it was used in the original - * API to store the results for ANY queries, and we may reuse it - * for that, but we might choose a different approach. + * Searches the datasource for an RRset of the given name and + * type. If there is a CNAME at the given name, the CNAME rrset + * is returned. + * (this implementation is not complete, and currently only + * does full matches, CNAMES, and the signatures for matches and + * CNAMEs) + * \note target was used in the original design to handle ANY + * queries. This is not implemented yet, and may use + * target again for that, but it might also use something + * different. It is left in for compatibility at the moment. + * \note options are ignored at this moment * + * \exception DataSourceError when there is a problem reading + * the data from the dabase backend. + * This can be a connection, code, or + * data (parse) error. + * + * \param name The name to find + * \param type The RRType to find + * \param target Unused at this moment + * \param options Unused at this moment */ virtual FindResult find(const isc::dns::Name& name, const isc::dns::RRType& type, isc::dns::RRsetList* target = NULL, const FindOptions options = FIND_DEFAULT) const; + /** * \brief The zone ID * From cc6d6b14603924a4ef2d86dfaf758447cca6a7ff Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Aug 2011 20:30:22 +0200 Subject: [PATCH 369/974] [trac1062] test for rrsig typeCovered() --- src/lib/dns/rdata/generic/rrsig_46.cc | 2 +- src/lib/dns/tests/rdata_rrsig_unittest.cc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc index 7d8c000119..fc8e3400c9 100644 --- a/src/lib/dns/rdata/generic/rrsig_46.cc +++ b/src/lib/dns/rdata/generic/rrsig_46.cc @@ -245,7 +245,7 @@ RRSIG::compare(const Rdata& other) const { const RRType& RRSIG::typeCovered() { - return impl_->covered_; + return (impl_->covered_); } // END_RDATA_NAMESPACE diff --git a/src/lib/dns/tests/rdata_rrsig_unittest.cc b/src/lib/dns/tests/rdata_rrsig_unittest.cc index 903021fb5e..ad49f76caf 100644 --- a/src/lib/dns/tests/rdata_rrsig_unittest.cc +++ b/src/lib/dns/tests/rdata_rrsig_unittest.cc @@ -47,6 +47,7 @@ TEST_F(Rdata_RRSIG_Test, fromText) { "f49t+sXKPzbipN9g+s1ZPiIyofc="); generic::RRSIG rdata_rrsig(rrsig_txt); EXPECT_EQ(rrsig_txt, rdata_rrsig.toText()); + EXPECT_EQ(isc::dns::RRType::A(), rdata_rrsig.typeCovered()); } From 2b6bcb84a17fc98ea0ea87df65e6a77829857ecd Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Aug 2011 20:56:19 +0200 Subject: [PATCH 370/974] [trac1062] doxygen update --- src/lib/datasrc/database.cc | 2 +- src/lib/datasrc/database.h | 8 +++++++- src/lib/datasrc/sqlite3_connection.cc | 9 +++++---- src/lib/datasrc/sqlite3_connection.h | 23 +++++++++++++++++++++++ src/lib/dns/tests/rdata_rrsig_unittest.cc | 1 - 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 3002056338..c3f67cd786 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -74,7 +74,7 @@ namespace { // created with the given name, class, type and ttl // The type is checked if the rrset exists, but the // name is not. - // + // // Then adds the given rdata to the set // // Raises a DataSourceError if the type does not diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 5498694687..d82c86f771 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -75,6 +75,12 @@ public: /** * \brief Starts a new search for records of the given name in the given zone * + * The data searched by this call can be retrieved with subsequent calls to + * getNextRecord(). + * + * \exception DataSourceError if there is a problem connecting to the + * backend database + * * \param zone_id The zone to search in, as returned by getZone() * \param name The name of the records to find */ @@ -169,7 +175,7 @@ public: * target again for that, but it might also use something * different. It is left in for compatibility at the moment. * \note options are ignored at this moment - * + * * \exception DataSourceError when there is a problem reading * the data from the dabase backend. * This can be a connection, code, or diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index fa5f8310d2..70adde4f6f 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -351,19 +351,20 @@ SQLite3Connection::getNextRecord(std::vector& columns) { columns.push_back(convertToPlainChar(sqlite3_column_text( current_stmt, column))); } - return true; + return (true); } else if (rc == SQLITE_DONE) { // reached the end of matching rows sqlite3_reset(current_stmt); sqlite3_clear_bindings(current_stmt); - return false; + return (false); } sqlite3_reset(current_stmt); sqlite3_clear_bindings(current_stmt); - isc_throw(DataSourceError, "Unexpected failure in sqlite3_step"); + isc_throw(DataSourceError, + "Unexpected failure in sqlite3_step (sqlite result code " << rc << ")"); // Compilers might not realize isc_throw always throws - return false; + return (false); } } diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index ca41a0621c..ffb2470b8e 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -88,7 +88,30 @@ public: * element and the zone id in the second if it was. */ virtual std::pair getZone(const isc::dns::Name& name) const; + + /** + * \brief Start a new search for the given name in the given zone. + * + * This implements the searchForRecords from DatabaseConnection. + * This particular implementation does not raise DataSourceError. + * + * \param zone_id The zone to seach in, as returned by getZone() + * \param name The name to find records for + */ virtual void searchForRecords(int zone_id, const std::string& name); + + /** + * \brief Retrieve the next record from the search started with + * searchForRecords + * + * This implements the getNextRecord from DatabaseConnection. + * See the documentation there for more information. + * + * \param columns This vector will be cleared, and the fields of the record will + * be appended here as strings (in the order rdtype, ttl, sigtype, + * and rdata). If there was no data, the vector is untouched. + * \return true if there was a next record, false if there was not + */ virtual bool getNextRecord(std::vector& columns); private: /// \brief Private database data diff --git a/src/lib/dns/tests/rdata_rrsig_unittest.cc b/src/lib/dns/tests/rdata_rrsig_unittest.cc index ad49f76caf..3324b99de1 100644 --- a/src/lib/dns/tests/rdata_rrsig_unittest.cc +++ b/src/lib/dns/tests/rdata_rrsig_unittest.cc @@ -48,7 +48,6 @@ TEST_F(Rdata_RRSIG_Test, fromText) { generic::RRSIG rdata_rrsig(rrsig_txt); EXPECT_EQ(rrsig_txt, rdata_rrsig.toText()); EXPECT_EQ(isc::dns::RRType::A(), rdata_rrsig.typeCovered()); - } TEST_F(Rdata_RRSIG_Test, badText) { From c9d7e29600f7a80094bcda2c3bd87d8f07d813e9 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Aug 2011 11:57:03 +0200 Subject: [PATCH 371/974] [801] Editorial fixes --- src/bin/bind10/creatorapi.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/creatorapi.txt b/src/bin/bind10/creatorapi.txt index a55099e01b..6100f39a1d 100644 --- a/src/bin/bind10/creatorapi.txt +++ b/src/bin/bind10/creatorapi.txt @@ -7,7 +7,7 @@ ports for now, but we should have some function where we can abstract it later. Goals ----- -* Be able to request a socket of any combination IP/IPv6 UDP/TCP bound to given +* Be able to request a socket of any combination IPv4/IPv6 UDP/TCP bound to given port and address (sockets that are not bound to anything can be created without privileges, therefore are not requested from the socket creator). * Allow to provide the same socket to multiple modules (eg. multiple running @@ -52,7 +52,7 @@ started by boss. There are two possibilities: * Let the msgq send messages about disconnected clients (eg. group message to some name). This one is better if we want to migrate to dbus, since dbus - already has this capability as well as sending the sockets inbound (at last it + already has this capability as well as sending the sockets inbound (at least it seems so on unix) and we could get rid of the unix-domain socket completely. * Keep the unix-domain connections open forever. Boss can remember which socket was sent to which connection and when the connection closes (because the From 56083614ae0e8c5177786528e85d348686bf9bc2 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Aug 2011 12:26:36 +0200 Subject: [PATCH 372/974] [1061] Rename DatabaseConnection to DatabaseAbstraction As database connection might be confusing, use something better. Also renamed SQLiteConnection to SQLiteDatabase --- src/lib/datasrc/Makefile.am | 2 +- src/lib/datasrc/database.cc | 24 ++++---- src/lib/datasrc/database.h | 55 ++++++++++--------- ...ite3_connection.cc => sqlite3_database.cc} | 12 ++-- ...qlite3_connection.h => sqlite3_database.h} | 16 +++--- src/lib/datasrc/tests/Makefile.am | 2 +- src/lib/datasrc/tests/database_unittest.cc | 16 +++--- ...ittest.cc => sqlite3_database_unittest.cc} | 34 ++++++------ 8 files changed, 81 insertions(+), 80 deletions(-) rename src/lib/datasrc/{sqlite3_connection.cc => sqlite3_database.cc} (97%) rename src/lib/datasrc/{sqlite3_connection.h => sqlite3_database.h} (86%) rename src/lib/datasrc/tests/{sqlite3_connection_unittest.cc => sqlite3_database_unittest.cc} (73%) diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index e6bff58fea..6792365ccd 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -23,7 +23,7 @@ libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc libdatasrc_la_SOURCES += client.h libdatasrc_la_SOURCES += database.h database.cc -libdatasrc_la_SOURCES += sqlite3_connection.h sqlite3_connection.cc +libdatasrc_la_SOURCES += sqlite3_database.h sqlite3_database.cc nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 2264f2c7ab..2d30ba28c6 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -22,32 +22,32 @@ using isc::dns::Name; namespace isc { namespace datasrc { -DatabaseClient::DatabaseClient(boost::shared_ptr - connection) : - connection_(connection) +DatabaseClient::DatabaseClient(boost::shared_ptr + database) : + database_(database) { - if (connection_.get() == NULL) { + if (database_.get() == NULL) { isc_throw(isc::InvalidParameter, - "No connection provided to DatabaseClient"); + "No database provided to DatabaseClient"); } } DataSourceClient::FindResult DatabaseClient::findZone(const Name& name) const { - std::pair zone(connection_->getZone(name)); + std::pair zone(database_->getZone(name)); // Try exact first if (zone.first) { return (FindResult(result::SUCCESS, - ZoneFinderPtr(new Finder(connection_, + ZoneFinderPtr(new Finder(database_, zone.second)))); } // Than super domains // Start from 1, as 0 is covered above for (size_t i(1); i < name.getLabelCount(); ++i) { - zone = connection_->getZone(name.split(i)); + zone = database_->getZone(name.split(i)); if (zone.first) { return (FindResult(result::PARTIALMATCH, - ZoneFinderPtr(new Finder(connection_, + ZoneFinderPtr(new Finder(database_, zone.second)))); } } @@ -55,9 +55,9 @@ DatabaseClient::findZone(const Name& name) const { return (FindResult(result::NOTFOUND, ZoneFinderPtr())); } -DatabaseClient::Finder::Finder(boost::shared_ptr - connection, int zone_id) : - connection_(connection), +DatabaseClient::Finder::Finder(boost::shared_ptr + database, int zone_id) : + database_(database), zone_id_(zone_id) { } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 8e5c1564e5..aed4fcc05c 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -21,7 +21,7 @@ namespace isc { namespace datasrc { /** - * \brief Abstract connection to database with DNS data + * \brief Abstraction of lowlevel database with DNS data * * This class is defines interface to databases. Each supported database * will provide methods for accessing the data stored there in a generic @@ -39,10 +39,11 @@ namespace datasrc { * be better for that than copy constructor. * * \note The same application may create multiple connections to the same - * database. If the database allows having multiple open queries at one - * connection, the connection class may share it. + * database, having multiple instances of this class. If the database + * allows having multiple open queries at one connection, the connection + * class may share it. */ -class DatabaseConnection : boost::noncopyable { +class DatabaseAbstraction : boost::noncopyable { public: /** * \brief Destructor @@ -50,7 +51,7 @@ public: * It is empty, but needs a virtual one, since we will use the derived * classes in polymorphic way. */ - virtual ~DatabaseConnection() { } + virtual ~DatabaseAbstraction() { } /** * \brief Retrieve a zone identifier * @@ -67,7 +68,7 @@ public: * was found. In case it was, the second part is internal zone ID. * This one will be passed to methods finding data in the zone. * It is not required to keep them, in which case whatever might - * be returned - the ID is only passed back to the connection as + * be returned - the ID is only passed back to the database as * an opaque handle. */ virtual std::pair getZone(const isc::dns::Name& name) const = 0; @@ -78,36 +79,36 @@ public: * * This class (together with corresponding versions of ZoneFinder, * ZoneIterator, etc.) translates high-level data source queries to - * low-level calls on DatabaseConnection. It calls multiple queries + * low-level calls on DatabaseAbstraction. It calls multiple queries * if necessary and validates data from the database, allowing the - * DatabaseConnection to be just simple translation to SQL/other + * DatabaseAbstraction to be just simple translation to SQL/other * queries to database. * * While it is possible to subclass it for specific database in case * of special needs, it is not expected to be needed. This should just - * work as it is with whatever DatabaseConnection. + * work as it is with whatever DatabaseAbstraction. */ class DatabaseClient : public DataSourceClient { public: /** * \brief Constructor * - * It initializes the client with a connection. + * It initializes the client with a database. * - * \exception isc::InvalidParameter if connection is NULL. It might throw + * \exception isc::InvalidParameter if database is NULL. It might throw * standard allocation exception as well, but doesn't throw anything else. * - * \param connection The connection to use to get data. As the parameter - * suggests, the client takes ownership of the connection and will + * \param database The database to use to get data. As the parameter + * suggests, the client takes ownership of the database and will * delete it when itself deleted. */ - DatabaseClient(boost::shared_ptr connection); + DatabaseClient(boost::shared_ptr database); /** * \brief Corresponding ZoneFinder implementation * * The zone finder implementation for database data sources. Similarly * to the DatabaseClient, it translates the queries to methods of the - * connection. + * database. * * Application should not come directly in contact with this class * (it should handle it trough generic ZoneFinder pointer), therefore @@ -122,13 +123,13 @@ public: /** * \brief Constructor * - * \param connection The connection (shared with DatabaseClient) to + * \param database The database (shared with DatabaseClient) to * be used for queries (the one asked for ID before). * \param zone_id The zone ID which was returned from - * DatabaseConnection::getZone and which will be passed to further - * calls to the connection. + * DatabaseAbstraction::getZone and which will be passed to further + * calls to the database. */ - Finder(boost::shared_ptr connection, int zone_id); + Finder(boost::shared_ptr database, int zone_id); virtual isc::dns::Name getOrigin() const; virtual isc::dns::RRClass getClass() const; virtual FindResult find(const isc::dns::Name& name, @@ -145,23 +146,23 @@ public: */ int zone_id() const { return (zone_id_); } /** - * \brief The database connection. + * \brief The database. * - * This function provides the database connection stored inside as + * This function provides the database stored inside as * passed to the constructor. This is meant for testing purposes and * normal applications shouldn't need it. */ - const DatabaseConnection& connection() const { - return (*connection_); + const DatabaseAbstraction& database() const { + return (*database_); } private: - boost::shared_ptr connection_; + boost::shared_ptr database_; const int zone_id_; }; /** * \brief Find a zone in the database * - * This queries connection's getZone to find the best matching zone. + * This queries database's getZone to find the best matching zone. * It will propagate whatever exceptions are thrown from that method * (which is not restricted in any way). * @@ -172,8 +173,8 @@ public: */ virtual FindResult findZone(const isc::dns::Name& name) const; private: - /// \brief Our connection. - const boost::shared_ptr connection_; + /// \brief Our database. + const boost::shared_ptr database_; }; } diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_database.cc similarity index 97% rename from src/lib/datasrc/sqlite3_connection.cc rename to src/lib/datasrc/sqlite3_database.cc index 35db44620d..2fdd240cbe 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_database.cc @@ -14,7 +14,7 @@ #include -#include +#include #include #include @@ -44,7 +44,7 @@ struct SQLite3Parameters { */ }; -SQLite3Connection::SQLite3Connection(const std::string& filename, +SQLite3Database::SQLite3Database(const std::string& filename, const isc::dns::RRClass& rrclass) : dbparameters_(new SQLite3Parameters), class_(rrclass.toText()) @@ -215,7 +215,7 @@ checkAndSetupSchema(Initializer* initializer) { } void -SQLite3Connection::open(const std::string& name) { +SQLite3Database::open(const std::string& name) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNOPEN).arg(name); if (dbparameters_->db_ != NULL) { // There shouldn't be a way to trigger this anyway @@ -232,7 +232,7 @@ SQLite3Connection::open(const std::string& name) { initializer.move(dbparameters_); } -SQLite3Connection::~SQLite3Connection() { +SQLite3Database::~SQLite3Database() { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DROPCONN); if (dbparameters_->db_ != NULL) { close(); @@ -241,7 +241,7 @@ SQLite3Connection::~SQLite3Connection() { } void -SQLite3Connection::close(void) { +SQLite3Database::close(void) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNCLOSE); if (dbparameters_->db_ == NULL) { isc_throw(DataSourceError, @@ -283,7 +283,7 @@ SQLite3Connection::close(void) { } std::pair -SQLite3Connection::getZone(const isc::dns::Name& name) const { +SQLite3Database::getZone(const isc::dns::Name& name) const { int rc; // Take the statement (simple SELECT id FROM zones WHERE...) diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_database.h similarity index 86% rename from src/lib/datasrc/sqlite3_connection.h rename to src/lib/datasrc/sqlite3_database.h index 484571599e..9607e691b6 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_database.h @@ -45,13 +45,13 @@ public: struct SQLite3Parameters; /** - * \brief Concrete implementation of DatabaseConnection for SQLite3 databases + * \brief Concrete implementation of DatabaseAbstraction for SQLite3 databases * * This opens one database file with our schema and serves data from there. * According to the design, it doesn't interpret the data in any way, it just * provides unified access to the DB. */ -class SQLite3Connection : public DatabaseConnection { +class SQLite3Database : public DatabaseAbstraction { public: /** * \brief Constructor @@ -63,21 +63,21 @@ public: * * \param filename The database file to be used. * \param rrclass Which class of data it should serve (while the database - * can contain multiple classes of data, single connection can provide - * only one class). + * file can contain multiple classes of data, single database can + * provide only one class). */ - SQLite3Connection(const std::string& filename, - const isc::dns::RRClass& rrclass); + SQLite3Database(const std::string& filename, + const isc::dns::RRClass& rrclass); /** * \brief Destructor * * Closes the database. */ - ~SQLite3Connection(); + ~SQLite3Database(); /** * \brief Look up a zone * - * This implements the getZone from DatabaseConnection and looks up a zone + * This implements the getZone from DatabaseAbstraction and looks up a zone * in the data. It looks for a zone with the exact given origin and class * passed to the constructor. * diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index c2e2b5caad..3667306f9a 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -29,7 +29,7 @@ run_unittests_SOURCES += zonetable_unittest.cc run_unittests_SOURCES += memory_datasrc_unittest.cc run_unittests_SOURCES += logger_unittest.cc run_unittests_SOURCES += database_unittest.cc -run_unittests_SOURCES += sqlite3_connection_unittest.cc +run_unittests_SOURCES += sqlite3_database_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index c271a76dc8..7de3e8098d 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -30,7 +30,7 @@ namespace { * A virtual database connection that pretends it contains single zone -- * example.org. */ -class MockConnection : public DatabaseConnection { +class MockAbstraction : public DatabaseAbstraction { public: virtual std::pair getZone(const Name& name) const { if (name == Name("example.org")) { @@ -51,16 +51,16 @@ public: * times per test. */ void createClient() { - current_connection_ = new MockConnection(); - client_.reset(new DatabaseClient(shared_ptr( - current_connection_))); + current_database_ = new MockAbstraction(); + client_.reset(new DatabaseClient(shared_ptr( + current_database_))); } // Will be deleted by client_, just keep the current value for comparison. - MockConnection* current_connection_; + MockAbstraction* current_database_; shared_ptr client_; /** * Check the zone finder is a valid one and references the zone ID and - * connection available here. + * database available here. */ void checkZoneFinder(const DataSourceClient::FindResult& zone) { ASSERT_NE(ZoneFinderPtr(), zone.zone_finder) << "No zone finder"; @@ -69,7 +69,7 @@ public: ASSERT_NE(shared_ptr(), finder) << "Wrong type of finder"; EXPECT_EQ(42, finder->zone_id()); - EXPECT_EQ(current_connection_, &finder->connection()); + EXPECT_EQ(current_database_, &finder->database()); } }; @@ -92,7 +92,7 @@ TEST_F(DatabaseClientTest, superZone) { } TEST_F(DatabaseClientTest, noConnException) { - EXPECT_THROW(DatabaseClient(shared_ptr()), + EXPECT_THROW(DatabaseClient(shared_ptr()), isc::InvalidParameter); } diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_database_unittest.cc similarity index 73% rename from src/lib/datasrc/tests/sqlite3_connection_unittest.cc rename to src/lib/datasrc/tests/sqlite3_database_unittest.cc index 1bdbe90206..cf82d190b5 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_database_unittest.cc @@ -12,7 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include +#include #include #include @@ -41,29 +41,29 @@ std::string SQLITE_DBFILE_NOTEXIST = TEST_DATA_DIR "/nodir/notexist"; // Opening works (the content is tested in different tests) TEST(SQLite3Open, common) { - EXPECT_NO_THROW(SQLite3Connection conn(SQLITE_DBFILE_EXAMPLE, - RRClass::IN())); + EXPECT_NO_THROW(SQLite3Database db(SQLITE_DBFILE_EXAMPLE, + RRClass::IN())); } // The file can't be opened TEST(SQLite3Open, notExist) { - EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_NOTEXIST, - RRClass::IN()), SQLite3Error); + EXPECT_THROW(SQLite3Database db(SQLITE_DBFILE_NOTEXIST, + RRClass::IN()), SQLite3Error); } // It rejects broken DB TEST(SQLite3Open, brokenDB) { - EXPECT_THROW(SQLite3Connection conn(SQLITE_DBFILE_BROKENDB, - RRClass::IN()), SQLite3Error); + EXPECT_THROW(SQLite3Database db(SQLITE_DBFILE_BROKENDB, + RRClass::IN()), SQLite3Error); } // Test we can create the schema on the fly TEST(SQLite3Open, memoryDB) { - EXPECT_NO_THROW(SQLite3Connection conn(SQLITE_DBFILE_MEMORY, - RRClass::IN())); + EXPECT_NO_THROW(SQLite3Database db(SQLITE_DBFILE_MEMORY, + RRClass::IN())); } -// Test fixture for querying the connection +// Test fixture for querying the db class SQLite3Conn : public ::testing::Test { public: SQLite3Conn() { @@ -71,33 +71,33 @@ public: } // So it can be re-created with different data void initConn(const std::string& filename, const RRClass& rrclass) { - conn.reset(new SQLite3Connection(filename, rrclass)); + db.reset(new SQLite3Database(filename, rrclass)); } - // The tested connection - std::auto_ptr conn; + // The tested db + std::auto_ptr db; }; // This zone exists in the data, so it should be found TEST_F(SQLite3Conn, getZone) { - std::pair result(conn->getZone(Name("example.com"))); + std::pair result(db->getZone(Name("example.com"))); EXPECT_TRUE(result.first); EXPECT_EQ(1, result.second); } // But it should find only the zone, nothing below it TEST_F(SQLite3Conn, subZone) { - EXPECT_FALSE(conn->getZone(Name("sub.example.com")).first); + EXPECT_FALSE(db->getZone(Name("sub.example.com")).first); } // This zone is not there at all TEST_F(SQLite3Conn, noZone) { - EXPECT_FALSE(conn->getZone(Name("example.org")).first); + EXPECT_FALSE(db->getZone(Name("example.org")).first); } // This zone is there, but in different class TEST_F(SQLite3Conn, noClass) { initConn(SQLITE_DBFILE_EXAMPLE, RRClass::CH()); - EXPECT_FALSE(conn->getZone(Name("example.com")).first); + EXPECT_FALSE(db->getZone(Name("example.com")).first); } } From 97153d16eb9ecb7281ed9dc76783091964e769dd Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Aug 2011 12:33:33 +0200 Subject: [PATCH 373/974] [1061] Few comments --- src/lib/datasrc/database.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index aed4fcc05c..7a6cd6bc46 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -130,6 +130,8 @@ public: * calls to the database. */ Finder(boost::shared_ptr database, int zone_id); + // The following three methods are just implementations of inherited + // ZoneFinder's pure virtual methods. virtual isc::dns::Name getOrigin() const; virtual isc::dns::RRClass getClass() const; virtual FindResult find(const isc::dns::Name& name, @@ -167,9 +169,11 @@ public: * (which is not restricted in any way). * * \param name Name of the zone or data contained there. - * \return Result containing the code and instance of Finder, if anything - * is found. Applications should not rely on the specific class being - * returned, though. + * \return FindResult containing the code and an instance of Finder, if + * anything is found. However, application should not rely on the + * ZoneFinder being instance of Finder (possible subclass of this class + * may return something else and it may change in future versions), it + * should use it as a ZoneFinder only. */ virtual FindResult findZone(const isc::dns::Name& name) const; private: From 52357dbe51bd015119a798a4f8e7244a3e1efda4 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Mon, 8 Aug 2011 11:48:15 +0800 Subject: [PATCH 374/974] [trac1130] Add more unit tests --- src/lib/dns/tests/rdata_in_naptr_unittest.cc | 56 ++++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/src/lib/dns/tests/rdata_in_naptr_unittest.cc b/src/lib/dns/tests/rdata_in_naptr_unittest.cc index aae80ebbfd..c63cd2c512 100644 --- a/src/lib/dns/tests/rdata_in_naptr_unittest.cc +++ b/src/lib/dns/tests/rdata_in_naptr_unittest.cc @@ -41,18 +41,32 @@ static uint8_t naptr_rdata[] = {0x00,0x0a,0x00,0x64,0x01,0x53,0x07,0x53,0x49, 0x50,0x2b,0x44,0x32,0x55,0x00,0x04,0x5f,0x73,0x69,0x70,0x04,0x5f,0x75,0x64, 0x70,0x07,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x03,0x63,0x6f,0x6d,0x00}; -static const char *naptr_str = "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small1 = "9 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small2 = "10 90 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small3 = "10 100 \"R\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small4 = "10 100 \"S\" \"SIP+C2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small5 = "10 100 \"S\" \"SIP+D2U\" \"\" _rip._udp.example.com."; +static const char *naptr_str = + "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str2 = + "10 100 S SIP+D2U \"\" _sip._udp.example.com."; -static const char *naptr_str_large1 = "11 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_large2 = "10 110 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_large3 = "10 100 \"T\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_large4 = "10 100 \"S\" \"SIP+E2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_large5 = "10 100 \"S\" \"SIP+D2U\" \"\" _tip._udp.example.com."; +static const char *naptr_str_small1 = + "9 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small2 = + "10 90 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small3 = + "10 100 \"R\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small4 = + "10 100 \"S\" \"SIP+C2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small5 = + "10 100 \"S\" \"SIP+D2U\" \"\" _rip._udp.example.com."; + +static const char *naptr_str_large1 = + "11 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large2 = + "10 110 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large3 = + "10 100 \"T\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large4 = + "10 100 \"S\" \"SIP+E2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large5 = + "10 100 \"S\" \"SIP+D2U\" \"\" _tip._udp.example.com."; TEST_F(Rdata_IN_NAPTR_Test, createFromText) { NAPTR naptr(naptr_str); @@ -62,6 +76,26 @@ TEST_F(Rdata_IN_NAPTR_Test, createFromText) { EXPECT_EQ(string("SIP+D2U"), naptr.getServices()); EXPECT_EQ(string(""), naptr.getRegexp()); EXPECT_EQ(Name("_sip._udp.example.com."), naptr.getReplacement()); + + // Test that separated by space + NAPTR naptr2(naptr_str2); + EXPECT_EQ(string("S"), naptr2.getFlags()); + EXPECT_EQ(string("SIP+D2U"), naptr2.getServices()); +} + +TEST_F(Rdata_IN_NAPTR_Test, badText) { + // Order number cannot exceed 65535 + EXPECT_THROW(const NAPTR naptr("65536 10 S SIP \"\" _sip._udp.example.com."), + InvalidRdataText); + // Preference number cannot exceed 65535 + EXPECT_THROW(const NAPTR naptr("100 65536 S SIP \"\" _sip._udp.example.com."), + InvalidRdataText); + // No regexp given + EXPECT_THROW(const NAPTR naptr("100 10 S SIP _sip._udp.example.com."), + InvalidRdataText); + // The double quotes seperator must match + EXPECT_THROW(const NAPTR naptr("100 10 \"S SIP \"\" _sip._udp.example.com."), + InvalidRdataText); } TEST_F(Rdata_IN_NAPTR_Test, createFromWire) { From 46b961d69aff3a2e4d1cb7f3d0910bfcc66d1e19 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Aug 2011 01:41:39 -0700 Subject: [PATCH 375/974] [1062] various types of editorial/trivial fixes. --- src/lib/datasrc/database.cc | 47 ++++++++++--------- src/lib/datasrc/tests/database_unittest.cc | 6 +-- .../tests/sqlite3_connection_unittest.cc | 11 +++-- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index c3f67cd786..ee257887da 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -12,6 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + #include #include @@ -101,7 +103,8 @@ namespace { } if (rdata_str != "") { try { - rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str)); + rrset->addRdata(isc::dns::rdata::createRdata(type, cls, + rdata_str)); } catch (const isc::dns::rdata::InvalidRdataText& ivrt) { // at this point, rrset may have been initialised for no reason, // and won't be used. But the caller would drop the shared_ptr @@ -137,13 +140,13 @@ namespace { // Returns true if this store contains signatures covering the // given type - bool haveSigsFor(isc::dns::RRType type) { + bool haveSigsFor(isc::dns::RRType type) const { return (sigs.count(type) > 0); } // If the store contains signatures for the type of the given // rrset, they are appended to it. - void appendSignatures(isc::dns::RRsetPtr& rrset) { + void appendSignatures(isc::dns::RRsetPtr& rrset) const { if (haveSigsFor(rrset->getType())) { BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, sigs.find(rrset->getType())->second) { @@ -180,8 +183,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } if (columns.size() != 4) { - isc_throw(DataSourceError, - "Datasource backend did not return 4 columns in getNextRecord()"); + isc_throw(DataSourceError, "Datasource backend did not return 4 " + "columns in getNextRecord()"); } try { @@ -190,33 +193,35 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, //cur_sigtype(columns[2]); if (cur_type == type) { - addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]); + addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, + columns[3]); } else if (cur_type == isc::dns::RRType::CNAME()) { // There should be no other data, so cur_rrset should be empty, if (result_rrset) { - isc_throw(DataSourceError, - "CNAME found but it is not the only record for " + - name.toText()); + isc_throw(DataSourceError, "CNAME found but it is not " + "the only record for " + name.toText()); } - addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]); + addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, + columns[3]); result_status = CNAME; } else if (cur_type == isc::dns::RRType::RRSIG()) { - // If we get signatures before we get the actual data, we can't know - // which ones to keep and which to drop... - // So we keep a separate store of any signature that may be relevant - // and add them to the final RRset when we are done. - // A possible optimization here is to not store them for types we - // are certain we don't need + // If we get signatures before we get the actual data, we + // can't know which ones to keep and which to drop... + // So we keep a separate store of any signature that may be + // relevant and add them to the final RRset when we are done. + // A possible optimization here is to not store them for types + // we are certain we don't need isc::dns::rdata::RdataPtr cur_rrsig( - isc::dns::rdata::createRdata(cur_type, getClass(), columns[3])); + isc::dns::rdata::createRdata(cur_type, getClass(), + columns[3])); sig_store.addSig(cur_rrsig); } } catch (const isc::dns::InvalidRRType& irt) { - isc_throw(DataSourceError, - "Invalid RRType in database for " << name << ": " << columns[0]); + isc_throw(DataSourceError, "Invalid RRType in database for " << + name << ": " << columns[0]); } catch (const isc::dns::InvalidRRTTL& irttl) { - isc_throw(DataSourceError, - "Invalid TTL in database for " << name << ": " << columns[1]); + isc_throw(DataSourceError, "Invalid TTL in database for " << + name << ": " << columns[1]); } } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 76c801c702..c31593217f 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -65,9 +65,9 @@ public: virtual bool getNextRecord(std::vector& columns) { if (cur_record < cur_name.size()) { columns = cur_name[cur_record++]; - return true; + return (true); } else { - return false; + return (false); } }; @@ -268,7 +268,7 @@ TEST_F(DatabaseClientTest, find) { shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); EXPECT_EQ(42, finder->zone_id()); - isc::dns::Name name("www.example.org."); + const isc::dns::Name name("www.example.org."); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 1279910114..0d0b8c35f6 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -103,10 +103,11 @@ TEST_F(SQLite3Conn, noClass) { } namespace { - // Simple function to cound the number of records for + // Simple function to count the number of records for // any name size_t countRecords(boost::shared_ptr& conn, - int zone_id, const std::string& name) { + int zone_id, const std::string& name) + { conn->searchForRecords(zone_id, name); size_t count = 0; std::vector columns; @@ -114,16 +115,16 @@ namespace { EXPECT_EQ(4, columns.size()); ++count; } - return count; + return (count); } } } TEST_F(SQLite3Conn, getRecords) { - std::pair zone_info(conn->getZone(Name("example.com"))); + const std::pair zone_info(conn->getZone(Name("example.com"))); ASSERT_TRUE(zone_info.first); - int zone_id = zone_info.second; + const int zone_id = zone_info.second; ASSERT_EQ(1, zone_id); // without search, getNext() should return false From 848cfc635084c5baccb275ed4995032d3ada2d59 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 8 Aug 2011 11:08:50 +0200 Subject: [PATCH 376/974] [1067] Tests for SQLite3 iterator context Just loads and iterates single-record zone. --- src/lib/datasrc/sqlite3_connection.cc | 5 ++++ src/lib/datasrc/sqlite3_connection.h | 2 ++ .../tests/sqlite3_connection_unittest.cc | 24 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index e850db4250..d9d873e532 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -318,5 +318,10 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const { return (result); } +DatabaseConnection::IteratorContextPtr +SQLite3Connection::getIteratorContext(const isc::dns::Name&, int) const { + return IteratorContextPtr(); +} + } } diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index 266dd05ea6..66c40e724e 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -94,6 +94,8 @@ public: * element and the zone id in the second if it was. */ virtual std::pair getZone(const isc::dns::Name& name) const; + virtual IteratorContextPtr getIteratorContext(const isc::dns::Name&, + int id) const; private: /// \brief Private database data SQLite3Parameters* dbparameters_; diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 3065dfe9fb..f0bceeafbf 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -113,4 +113,28 @@ TEST_F(SQLite3Conn, noClass) { EXPECT_FALSE(conn->getZone(Name("example.com")).first); } +// This tests the iterator context +TEST_F(SQLite3Conn, iterator) { + // Our test zone is conveniently small, but not empty + initConn(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); + + // Get the iterator context + DatabaseConnection::IteratorContextPtr + context(conn->getIteratorContext(Name("example2.com"), 1)); + ASSERT_NE(DatabaseConnection::IteratorContextPtr(), + context); + + std::string name, rtype, data; + int ttl; + // Get and check the first and only record + EXPECT_TRUE(context->getNext(name, rtype, ttl, data)); + EXPECT_EQ("example2.com.", name); + EXPECT_EQ("SOA", rtype); + EXPECT_EQ("master.example2.com. admin.example2.com. " + "1234 3600 1800 2419200 7200", data); + EXPECT_EQ(3600, ttl); + // Check there's no other + EXPECT_FALSE(context->getNext(name, rtype, ttl, data)); +} + } From b5347a6b22c2d82ffa57c8302c81ee0f25b413a1 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 8 Aug 2011 12:14:02 +0200 Subject: [PATCH 377/974] [1067] Iterator context for sqlite3 It holds the statement that is used to get the data. --- src/lib/datasrc/sqlite3_connection.cc | 54 ++++++++++++++++++- src/lib/datasrc/sqlite3_connection.h | 6 ++- .../tests/sqlite3_connection_unittest.cc | 2 +- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index d9d873e532..b51259e7e2 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -137,6 +137,10 @@ const char* const SCHEMA_LIST[] = { const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"; +const char* const q_iterate_str = "SELECT name, rdtype, ttl, rdata FROM records " + "WHERE zone_id = ?1 " + "ORDER BY name, rdtype"; + /* TODO: Prune the statements, not everything will be needed maybe? const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata " "FROM records WHERE zone_id=?1 AND name=?2 AND " @@ -318,9 +322,55 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const { return (result); } +namespace { + +std::string +getstr(const unsigned char* str) { + return + (std::string(static_cast(static_cast(str)))); +} + +} + +class SQLite3Connection::Context : public DatabaseConnection::IteratorContext { +public: + Context(boost::shared_ptr connection, int id) : + connection_(connection), + statement(NULL) + { + // We create the statement now and then just keep getting data from it + statement = prepare(connection->dbparameters_->db_, q_iterate_str); + if (sqlite3_bind_int(statement, 1, id) != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not bind " << id << + " to SQL statement (iterate)"); + } + } + bool getNext(std::string& name, std::string& rtype, int& ttl, + std::string& data) + { + // If there's another row, get it + if (sqlite3_step(statement) == SQLITE_ROW) { + name = getstr(sqlite3_column_text(statement, 0)); + rtype = getstr(sqlite3_column_text(statement, 1)); + ttl = sqlite3_column_int(statement, 2); + data = getstr(sqlite3_column_text(statement, 3)); + return (true); + } + return (false); + } + virtual ~Context() { + if (statement) + sqlite3_finalize(statement); + } + +private: + boost::shared_ptr connection_; + sqlite3_stmt *statement; +}; + DatabaseConnection::IteratorContextPtr -SQLite3Connection::getIteratorContext(const isc::dns::Name&, int) const { - return IteratorContextPtr(); +SQLite3Connection::getIteratorContext(const isc::dns::Name&, int id) const { + return (IteratorContextPtr(new Context(shared_from_this(), id))); } } diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index 66c40e724e..901c0253b8 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -21,6 +21,7 @@ #include #include +#include #include namespace isc { @@ -52,7 +53,8 @@ struct SQLite3Parameters; * According to the design, it doesn't interpret the data in any way, it just * provides unified access to the DB. */ -class SQLite3Connection : public DatabaseConnection { +class SQLite3Connection : public DatabaseConnection, + public boost::enable_shared_from_this { public: /** * \brief Constructor @@ -105,6 +107,8 @@ private: void open(const std::string& filename); /// \brief Closes the database void close(); + class Context; + friend class Context; }; } diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index f0bceeafbf..0ff120a030 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -87,7 +87,7 @@ public: conn.reset(new SQLite3Connection(config, rrclass)); } // The tested connection - std::auto_ptr conn; + boost::shared_ptr conn; }; // This zone exists in the data, so it should be found From de06b256b36f6428c5d914266c4e91c25c69ded5 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 8 Aug 2011 13:18:36 +0200 Subject: [PATCH 378/974] [1067] Doxygen --- src/lib/datasrc/sqlite3_database.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/datasrc/sqlite3_database.h b/src/lib/datasrc/sqlite3_database.h index a152177742..9922d844d7 100644 --- a/src/lib/datasrc/sqlite3_database.h +++ b/src/lib/datasrc/sqlite3_database.h @@ -90,6 +90,7 @@ public: * element and the zone id in the second if it was. */ virtual std::pair getZone(const isc::dns::Name& name) const; + /// \brief Implementation of DatabaseAbstraction::getIteratorContext virtual IteratorContextPtr getIteratorContext(const isc::dns::Name&, int id) const; private: @@ -101,6 +102,7 @@ private: void open(const std::string& filename); /// \brief Closes the database void close(); + /// \brief SQLite3 implementation of IteratorContext class Context; friend class Context; }; From 9351dbcc88ccdd6aa83d72f432f19a76c031124b Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Mon, 8 Aug 2011 10:14:06 -0500 Subject: [PATCH 379/974] [1011] little more docbook formatting --- doc/guide/bind10-guide.xml | 148 ++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 86 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 3024467822..5c8840b324 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1704,37 +1704,27 @@ then change those defaults with config set Resolver/forward_addresses[0]/address severity):
- - - - FATAL - - + + + FATAL + - - - ERROR - - + + ERROR + - - - WARN - - + + WARN + - - - INFO - - + + INFO + - - - DEBUG - - - + + DEBUG + + @@ -1828,37 +1818,27 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
destination (string) - + - The destination is the type of output. It can be one of: + The destination is the type of output. It can be one of: - + - + - + + console + - - - console - - - - - - file - - - - - - syslog - - - - + + file + + + syslog + +
@@ -1878,27 +1858,30 @@ then change those defaults with config set Resolver/forward_addresses[0]/address destination is 'console' - 'output' must be one of 'stdout' (messages printed to standard output) or 'stderr' (messages printed to standard error). + The value of output must be one of 'stdout' + (messages printed to standard output) or 'stderr' + (messages printed to standard error). - destination is 'file' - The value of output is interpreted as a file name; log messages will be appended to this file. + The value of output is interpreted as a file name; + log messages will be appended to this file. - destination is 'syslog' - The value of output is interpreted as the syslog facility (e.g. 'local0') that should be used for log messages. + The value of output is interpreted as the syslog + facility (e.g. 'local0') that should be used for + log messages. @@ -1911,57 +1894,50 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
-
-
flush (true of false) - - - Flush buffers after each log message. Doing this will - reduce performance but will ensure that if the program - terminates abnormally, all messages up to the point of - termination are output. - - + + Flush buffers after each log message. Doing this will + reduce performance but will ensure that if the program + terminates abnormally, all messages up to the point of + termination are output. +
maxsize (integer) - + + Only relevant when destination is file, this is maximum + file size of output files in bytes. When the maximum + size is reached, the file is renamed (a ".1" is appended + to the name - if a ".1" file exists, it is renamed ".2" + etc.) and a new file opened. + - Only relevant when destination is file, this is maximum - file size of output files in bytes. When the maximum size - is reached, the file is renamed (a ".1" is appended to - the name - if a ".1" file exists, it is renamed ".2" - etc.) and a new file opened. - - - - - - If this is 0, no maximum file size is used. - - + + If this is 0, no maximum file size is used. +
maxver (integer) - + + Maximum number of old log files to keep around when + rolling the output file. Only relevant when destination + if 'file'. + - Maximum number of old log files to keep around when - rolling the output file. Only relevant when destination - if 'file'. - -
+
+
Example session From 10b192574ca253331298bbc4b05ef70d2cb927d1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Aug 2011 10:25:28 -0700 Subject: [PATCH 380/974] [904] missing Makefile.am --- src/lib/util/python/Makefile.am | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/lib/util/python/Makefile.am diff --git a/src/lib/util/python/Makefile.am b/src/lib/util/python/Makefile.am new file mode 100644 index 0000000000..81d528c5c2 --- /dev/null +++ b/src/lib/util/python/Makefile.am @@ -0,0 +1 @@ +noinst_SCRIPTS = gen_wiredata.py mkpywrapper.py From 2808941eebec54dc7c4981f5a2a0e149d452b8ca Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Tue, 9 Aug 2011 11:11:33 +0800 Subject: [PATCH 381/974] [trac1113] commit minfo rdata --- src/lib/dns/Makefile.am | 2 + src/lib/dns/rdata/generic/minfo_14.cc | 152 ++++++++++++++++++ src/lib/dns/rdata/generic/minfo_14.h | 88 ++++++++++ src/lib/dns/rdata/in_1/srv_33.h | 4 +- src/lib/dns/tests/Makefile.am | 1 + src/lib/dns/tests/testdata/Makefile.am | 1 + .../dns/tests/testdata/rdata_minfo_fromWire | 35 ++++ 7 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 src/lib/dns/rdata/generic/minfo_14.cc create mode 100644 src/lib/dns/rdata/generic/minfo_14.h create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 4a0173cb17..8ffd162076 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -51,6 +51,8 @@ EXTRA_DIST += rdata/generic/soa_6.cc EXTRA_DIST += rdata/generic/soa_6.h EXTRA_DIST += rdata/generic/txt_16.cc EXTRA_DIST += rdata/generic/txt_16.h +EXTRA_DIST += rdata/generic/minfo_14.cc +EXTRA_DIST += rdata/generic/minfo_14.h EXTRA_DIST += rdata/hs_4/a_1.cc EXTRA_DIST += rdata/hs_4/a_1.h EXTRA_DIST += rdata/in_1/a_1.cc diff --git a/src/lib/dns/rdata/generic/minfo_14.cc b/src/lib/dns/rdata/generic/minfo_14.cc new file mode 100644 index 0000000000..d40a833a10 --- /dev/null +++ b/src/lib/dns/rdata/generic/minfo_14.cc @@ -0,0 +1,152 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace isc::dns; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// \c minfo_str must be formatted as follows: +/// \code +/// \endcode +/// where both fields must represent a valid domain name. +/// +/// An example of valid string is: +/// \code "rmail.example.com. email.example.com." \endcode +/// +/// Exceptions +/// +/// If or if not a valid domain name, a +/// corresponding exception from the \c Name class will be thrown; +/// If the number of RDATA fields (must be 2) is incorrect, an exception of +/// class \c InvalidRdataText will be thrown. +/// This constructor internally involves resource allocation, and if it +/// fails a corresponding standard exception std::bad_alloc will be throw. +MINFO::MINFO(const std::string& minfo_str) : + // We cannot construct both names in the initialization list due to the + // necessary text processing, so we have to initialize them with a dummy + // name and replace them later. + rmailbox_(Name::ROOT_NAME()), emailbox_(Name::ROOT_NAME()) +{ + istringstream iss(minfo_str); + string rmailbox_str, emailbox_str; + iss >> rmailbox_str >> emailbox_str; + + // Validation: A valid MINFO RR must have exactly two fields. + if (iss.bad() || iss.fail()) { + isc_throw(InvalidRdataText, "Invalid MINFO text: " << minfo_str); + } + if (!iss.eof()) { + isc_throw(InvalidRdataText, "Invalid MINFO text (redundant field): " + << minfo_str); + } + + rmailbox_ = Name(rmailbox_str); + emailbox_ = Name(emailbox_str); +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \exception std::bad_alloc Memory allocation for names fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +MINFO::MINFO(InputBuffer& buffer, size_t) : rmailbox_(buffer), emailbox_(buffer) { +} + +/// \brief Copy constructor. +/// +/// \exception std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +MINFO::MINFO(const MINFO& other) : + Rdata(), rmailbox_(other.rmailbox_), emailbox_(other.emailbox_) +{} + +/// \brief Convert the \c MINFO to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c MINFO(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c MINFO object. +std::string +MINFO::toText() const { + return (rmailbox_.toText() + " " + emailbox_.toText()); +} + +/// \brief Render the \c MINFO in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding standard +/// exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +MINFO::toWire(OutputBuffer& buffer) const { + rmailbox_.toWire(buffer); + emailbox_.toWire(buffer); +} + +/// \brief Render the \c MINFO in the wire format with taking into account +/// compression. +/// +/// As specified in RFC1035, the rmailbox and emailbox fields (domain names) +/// will be compressed. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +MINFO::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(rmailbox_); + renderer.writeName(emailbox_); +} + +/// \brief Compare two instances of \c MINFO RDATA. +/// +/// See documentation in \c Rdata. +int +MINFO::compare(const Rdata& other) const { + const MINFO& other_minfo = dynamic_cast(other); + + const int cmp = compareNames(rmailbox_, other_minfo.rmailbox_); + if (cmp != 0) { + return (cmp); + } + return (compareNames(emailbox_, other_minfo.emailbox_)); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/minfo_14.h b/src/lib/dns/rdata/generic/minfo_14.h new file mode 100644 index 0000000000..9a1120a737 --- /dev/null +++ b/src/lib/dns/rdata/generic/minfo_14.h @@ -0,0 +1,88 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_HEADER_GUARD + +#include + +#include +#include + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +/// \brief \c rdata::generic::MINFO class represents the MINFO RDATA as +/// defined in RFC1035. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// MINFO RDATA. +class MINFO : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// We use the default copy constructor and assignment operator. + + /// \brief Constructor from RDATA field parameters. + /// + /// The parameters are a straightforward mapping of %MINFO RDATA + /// fields as defined in RFC1035. + MINFO(const Name& rmailbox, const Name& emailbox) : + rmailbox_(rmailbox), emailbox_(emailbox) + {} + + /// \brief Return the value of the rmailbox field. + /// + /// This method normally does not throw an exception, but if resource + /// allocation for the returned \c Name object fails, a corresponding + /// standard exception will be thrown. + /// + /// \note + /// Unlike the case of some other RDATA classes (such as + /// \c NS::getNSName()), this method constructs a new \c Name object + /// and returns it, instead of returning a reference to a \c Name object + /// internally maintained in the class (which is a private member). + /// This is based on the observation that this method will be rarely used + /// and even when it's used it will not be in a performance context + /// (for example, a recursive resolver won't need this field in its + /// resolution process). By returning a new object we have flexibility of + /// changing the internal representation without the risk of changing + /// the interface or method property. + /// The same note applies to the \c getEmailbox() method. + Name getRmailbox() const { return (rmailbox_); } + + /// \brief Return the value of the emailbox field. + /// + /// This method normally does not throw an exception, but if resource + /// allocation for the returned \c Name object fails, a corresponding + /// standard exception will be thrown. + Name getEmailbox() const { return (emailbox_); } + +private: + Name rmailbox_; + Name emailbox_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/in_1/srv_33.h b/src/lib/dns/rdata/in_1/srv_33.h index d067021939..32b7dc07b8 100644 --- a/src/lib/dns/rdata/in_1/srv_33.h +++ b/src/lib/dns/rdata/in_1/srv_33.h @@ -12,13 +12,13 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +// BEGIN_HEADER_GUARD + #include #include #include -// BEGIN_HEADER_GUARD - // BEGIN_ISC_NAMESPACE // BEGIN_COMMON_DECLARATIONS diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index bd6fbe2a6e..3921f27131 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -42,6 +42,7 @@ run_unittests_SOURCES += rdata_nsec3param_unittest.cc run_unittests_SOURCES += rdata_rrsig_unittest.cc run_unittests_SOURCES += rdata_rp_unittest.cc run_unittests_SOURCES += rdata_srv_unittest.cc +run_unittests_SOURCES += rdata_minfo_unittest.cc run_unittests_SOURCES += rdata_tsig_unittest.cc run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc run_unittests_SOURCES += question_unittest.cc diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 60735e90bd..344231a6e2 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -102,6 +102,7 @@ EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec EXTRA_DIST += rdata_srv_fromWire +EXTRA_DIST += rdata_minfo_fromWire EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire new file mode 100644 index 0000000000..b440505508 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire @@ -0,0 +1,35 @@ +# +# various kinds of MINFO RDATA stored in an input buffer +# +# RDLENGHT=27 bytes +# 0 1 + 00 1c +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2(bytes) + 04 72 6f 6f 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 65 6d +# 3 4 5 6 7 8 9 + 61 69 6c 62 78 c0 07 +# +# compressed name +# RDLENGHT=04 bytes +#30 1 + 00 04 +# 2 3 4 5(bytes) + c0 02 c0 14 +# +# length too short +# 6 7 + 00 03 +# 8 9 40 1(bytes) + c0 02 c0 14 +# +# length too long +# 2 3 + 00 19 +# 4 5 6 7(bytes) + c0 02 c0 14 +# +# incomplete target name +# 8 9 + 00 13 +#50 1 2 3 4 5 6 7 8 9 60 1 2 3 4 5 6 7 8 (bytes) + 04 72 6f 6f 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 From 87090907f39983b744749017cdac3fb957d8d0c0 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Tue, 9 Aug 2011 11:22:23 +0800 Subject: [PATCH 382/974] [trac1113] commit unittest source file --- src/lib/dns/tests/rdata_minfo_unittest.cc | 165 ++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/lib/dns/tests/rdata_minfo_unittest.cc diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc new file mode 100644 index 0000000000..0f5ff13829 --- /dev/null +++ b/src/lib/dns/tests/rdata_minfo_unittest.cc @@ -0,0 +1,165 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for generic +// 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using isc::UnitTestUtil; +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { +class Rdata_MINFO_Test : public RdataTest { + // there's nothing to specialize +}; + +// minfo text +string minfo_txt("root.example.com. emailbx.example.com."); +string minfo_txt2("rmailbx.example.com. emailbx.example.com."); +string too_long_label("012345678901234567890123456789" + "0123456789012345678901234567890123"); + +// root.example.com. emailbx.example.com. +const uint8_t uncompressed_wiredata_minfo[] = { + 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x07, 0x65, 0x6d, 0x61, + 0x69, 0x6c, 0x62, 0x78, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; +// rmailbx.example.com. emailbx.example.com. +const uint8_t uncompressed_wiredata_minfo2[] = { + 0x07, 0x72, 0x6d, 0x61, 0x69, 0x6c, 0x62, 0x78, 0x07, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x07, + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x62, 0x78, 0x07, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; + +// root.example.com. emailbx.example.com. +const uint8_t compressed_wiredata_minfo[] = { + 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x07, 0x65, 0x6d, 0x61, + 0x69, 0x6c, 0x62, 0x78, 0xc0, 0x05}; +// rmailbx.example.com. emailbx.example.com. +const uint8_t compressed_wiredata_minfo2[] = { + 0x07, 0x72, 0x6d, 0x61, 0x69, 0x6c, 0x62, 0x78, 0x07, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x07, + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x62, 0x78, 0xc0, 0x08}; + +const generic::MINFO rdata_minfo(minfo_txt); +const generic::MINFO rdata_minfo2(minfo_txt2); + +TEST_F(Rdata_MINFO_Test, createFromText) { + EXPECT_EQ(Name("root.example.com."), rdata_minfo.getRmailbox()); + EXPECT_EQ(Name("emailbx.example.com."), rdata_minfo.getEmailbox()); +} + +TEST_F(Rdata_MINFO_Test, badText) { + // incomplete text + EXPECT_THROW(generic::MINFO("root.example.com."), + InvalidRdataText); + // bad name + EXPECT_THROW(generic::MINFO("root.example.com. emailbx.example.com." + too_long_label), + TooLongLabel); +} + +TEST_F(Rdata_MINFO_Test, createFromWire) { + // compressed emailbx name + EXPECT_EQ(0, rdata_minfo.compare( + *rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), + "rdata_minfo_fromWire"))); + // compressed rmailbx and emailbx name + EXPECT_EQ(0, rdata_minfo.compare( + *rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), + "rdata_minfo_fromWire", 30))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), + "rdata_minfo_fromWire", 36), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), + "rdata_minfo_fromWire", 42), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), + "rdata_cname_fromWire", 48), + DNSMessageFORMERR); +} + +TEST_F(Rdata_MINFO_Test, toWireBuffer) { + rdata_minfo.toWire(obuffer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + uncompressed_wiredata_minfo, + sizeof(uncompressed_wiredata_minfo)); + obuffer.clear(); + rdata_minfo2.toWire(obuffer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + uncompressed_wiredata_minfo2, + sizeof(uncompressed_wiredata_minfo2)); +} + +TEST_F(Rdata_MINFO_Test, toWireRenderer) { + rdata_minfo.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + compressed_wiredata_minfo, + sizeof(compressed_wiredata_minfo)); + renderer.clear(); + rdata_minfo2.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + compressed_wiredata_minfo2, + sizeof(compressed_wiredata_minfo2)); +} + +TEST_F(Rdata_MINFO_Test, toText) { + EXPECT_EQ(minfo_txt, rdata_minfo.toText()); + EXPECT_EQ(minfo_txt2, rdata_minfo2.toText()); +} + +TEST_F(Rdata_MINFO_Test, compare) { + // check reflexivity + EXPECT_EQ(0, rdata_minfo.compare(rdata_minfo)); + + // names must be compared in case-insensitive manner + EXPECT_EQ(0, rdata_minfo.compare(generic::MINFO("ROOT.example.com. " + "emailbx.EXAMPLE.com."))); + + // another MINFO whose rmailbox name is larger than that of rdata_minfo. + const generic::MINFO large1_minfo("zzzz.example.com. " + "emailbox.example.com."); + EXPECT_GT(0, rdata_minfo.compare(large1_minfo)); + EXPECT_LT(0, large1_minfo.compare(rdata_minfo)); + + // another MINFO whose emailbox name is larger than that of rdata_minfo. + const generic::MINFO large2_minfo("root.example.com. " + "zzzzzzz.example.com."); + EXPECT_GT(0, rdata_minfo.compare(large2_minfo)); + EXPECT_LT(0, large2_minfo.compare(rdata_minfo)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_minfo.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} From 19c8c07e6e1601180f85f7aad145f00112f3f8a4 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Tue, 9 Aug 2011 11:27:09 +0800 Subject: [PATCH 383/974] [trac1113] fix testdata comments --- src/lib/dns/tests/testdata/rdata_minfo_fromWire | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire index b440505508..c50f7f6324 100644 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire @@ -1,7 +1,7 @@ # # various kinds of MINFO RDATA stored in an input buffer # -# RDLENGHT=27 bytes +# RDLENGHT=28 bytes # 0 1 00 1c # 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2(bytes) @@ -17,18 +17,21 @@ c0 02 c0 14 # # length too short +# RDLENGHT=03 bytes # 6 7 00 03 # 8 9 40 1(bytes) c0 02 c0 14 # # length too long +# RDLENGHT=25 bytes # 2 3 00 19 # 4 5 6 7(bytes) c0 02 c0 14 # # incomplete target name +# RDLENGHT=19 bytes # 8 9 00 13 #50 1 2 3 4 5 6 7 8 9 60 1 2 3 4 5 6 7 8 (bytes) From 65e4595c21bf9c01fb0b7da61577ae8a79d29c30 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 9 Aug 2011 09:02:03 +0200 Subject: [PATCH 384/974] [1061] Yet another renaming This time to DatabaseAccessor, Abstraction might be misleading in other ways. --- src/lib/datasrc/Makefile.am | 2 +- src/lib/datasrc/database.cc | 4 ++-- src/lib/datasrc/database.h | 22 +++++++++---------- ...qlite3_database.cc => sqlite3_accessor.cc} | 2 +- ...{sqlite3_database.h => sqlite3_accessor.h} | 10 ++++----- src/lib/datasrc/tests/Makefile.am | 2 +- src/lib/datasrc/tests/database_unittest.cc | 10 ++++----- ...ittest.cc => sqlite3_accessor_unittest.cc} | 2 +- 8 files changed, 27 insertions(+), 27 deletions(-) rename src/lib/datasrc/{sqlite3_database.cc => sqlite3_accessor.cc} (99%) rename src/lib/datasrc/{sqlite3_database.h => sqlite3_accessor.h} (91%) rename src/lib/datasrc/tests/{sqlite3_database_unittest.cc => sqlite3_accessor_unittest.cc} (98%) diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index 6792365ccd..db67781917 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -23,7 +23,7 @@ libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc libdatasrc_la_SOURCES += client.h libdatasrc_la_SOURCES += database.h database.cc -libdatasrc_la_SOURCES += sqlite3_database.h sqlite3_database.cc +libdatasrc_la_SOURCES += sqlite3_accessor.h sqlite3_accessor.cc nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 2d30ba28c6..0e1418dfbd 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -22,7 +22,7 @@ using isc::dns::Name; namespace isc { namespace datasrc { -DatabaseClient::DatabaseClient(boost::shared_ptr +DatabaseClient::DatabaseClient(boost::shared_ptr database) : database_(database) { @@ -55,7 +55,7 @@ DatabaseClient::findZone(const Name& name) const { return (FindResult(result::NOTFOUND, ZoneFinderPtr())); } -DatabaseClient::Finder::Finder(boost::shared_ptr +DatabaseClient::Finder::Finder(boost::shared_ptr database, int zone_id) : database_(database), zone_id_(zone_id) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 7a6cd6bc46..1f6bd229ea 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -43,7 +43,7 @@ namespace datasrc { * allows having multiple open queries at one connection, the connection * class may share it. */ -class DatabaseAbstraction : boost::noncopyable { +class DatabaseAccessor : boost::noncopyable { public: /** * \brief Destructor @@ -51,7 +51,7 @@ public: * It is empty, but needs a virtual one, since we will use the derived * classes in polymorphic way. */ - virtual ~DatabaseAbstraction() { } + virtual ~DatabaseAccessor() { } /** * \brief Retrieve a zone identifier * @@ -79,14 +79,14 @@ public: * * This class (together with corresponding versions of ZoneFinder, * ZoneIterator, etc.) translates high-level data source queries to - * low-level calls on DatabaseAbstraction. It calls multiple queries + * low-level calls on DatabaseAccessor. It calls multiple queries * if necessary and validates data from the database, allowing the - * DatabaseAbstraction to be just simple translation to SQL/other + * DatabaseAccessor to be just simple translation to SQL/other * queries to database. * * While it is possible to subclass it for specific database in case * of special needs, it is not expected to be needed. This should just - * work as it is with whatever DatabaseAbstraction. + * work as it is with whatever DatabaseAccessor. */ class DatabaseClient : public DataSourceClient { public: @@ -102,7 +102,7 @@ public: * suggests, the client takes ownership of the database and will * delete it when itself deleted. */ - DatabaseClient(boost::shared_ptr database); + DatabaseClient(boost::shared_ptr database); /** * \brief Corresponding ZoneFinder implementation * @@ -126,10 +126,10 @@ public: * \param database The database (shared with DatabaseClient) to * be used for queries (the one asked for ID before). * \param zone_id The zone ID which was returned from - * DatabaseAbstraction::getZone and which will be passed to further + * DatabaseAccessor::getZone and which will be passed to further * calls to the database. */ - Finder(boost::shared_ptr database, int zone_id); + Finder(boost::shared_ptr database, int zone_id); // The following three methods are just implementations of inherited // ZoneFinder's pure virtual methods. virtual isc::dns::Name getOrigin() const; @@ -154,11 +154,11 @@ public: * passed to the constructor. This is meant for testing purposes and * normal applications shouldn't need it. */ - const DatabaseAbstraction& database() const { + const DatabaseAccessor& database() const { return (*database_); } private: - boost::shared_ptr database_; + boost::shared_ptr database_; const int zone_id_; }; /** @@ -178,7 +178,7 @@ public: virtual FindResult findZone(const isc::dns::Name& name) const; private: /// \brief Our database. - const boost::shared_ptr database_; + const boost::shared_ptr database_; }; } diff --git a/src/lib/datasrc/sqlite3_database.cc b/src/lib/datasrc/sqlite3_accessor.cc similarity index 99% rename from src/lib/datasrc/sqlite3_database.cc rename to src/lib/datasrc/sqlite3_accessor.cc index 2fdd240cbe..352768de68 100644 --- a/src/lib/datasrc/sqlite3_database.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -14,7 +14,7 @@ #include -#include +#include #include #include diff --git a/src/lib/datasrc/sqlite3_database.h b/src/lib/datasrc/sqlite3_accessor.h similarity index 91% rename from src/lib/datasrc/sqlite3_database.h rename to src/lib/datasrc/sqlite3_accessor.h index 9607e691b6..0d7ddeebc7 100644 --- a/src/lib/datasrc/sqlite3_database.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -13,8 +13,8 @@ // PERFORMANCE OF THIS SOFTWARE. -#ifndef __DATASRC_SQLITE3_CONNECTION_H -#define __DATASRC_SQLITE3_CONNECTION_H +#ifndef __DATASRC_SQLITE3_ACCESSOR_H +#define __DATASRC_SQLITE3_ACCESSOR_H #include @@ -45,13 +45,13 @@ public: struct SQLite3Parameters; /** - * \brief Concrete implementation of DatabaseAbstraction for SQLite3 databases + * \brief Concrete implementation of DatabaseAccessor for SQLite3 databases * * This opens one database file with our schema and serves data from there. * According to the design, it doesn't interpret the data in any way, it just * provides unified access to the DB. */ -class SQLite3Database : public DatabaseAbstraction { +class SQLite3Database : public DatabaseAccessor { public: /** * \brief Constructor @@ -77,7 +77,7 @@ public: /** * \brief Look up a zone * - * This implements the getZone from DatabaseAbstraction and looks up a zone + * This implements the getZone from DatabaseAccessor and looks up a zone * in the data. It looks for a zone with the exact given origin and class * passed to the constructor. * diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index 3667306f9a..4a7f322b24 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -29,7 +29,7 @@ run_unittests_SOURCES += zonetable_unittest.cc run_unittests_SOURCES += memory_datasrc_unittest.cc run_unittests_SOURCES += logger_unittest.cc run_unittests_SOURCES += database_unittest.cc -run_unittests_SOURCES += sqlite3_database_unittest.cc +run_unittests_SOURCES += sqlite3_accessor_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 7de3e8098d..ab4423ec53 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -30,7 +30,7 @@ namespace { * A virtual database connection that pretends it contains single zone -- * example.org. */ -class MockAbstraction : public DatabaseAbstraction { +class MockAccessor : public DatabaseAccessor { public: virtual std::pair getZone(const Name& name) const { if (name == Name("example.org")) { @@ -51,12 +51,12 @@ public: * times per test. */ void createClient() { - current_database_ = new MockAbstraction(); - client_.reset(new DatabaseClient(shared_ptr( + current_database_ = new MockAccessor(); + client_.reset(new DatabaseClient(shared_ptr( current_database_))); } // Will be deleted by client_, just keep the current value for comparison. - MockAbstraction* current_database_; + MockAccessor* current_database_; shared_ptr client_; /** * Check the zone finder is a valid one and references the zone ID and @@ -92,7 +92,7 @@ TEST_F(DatabaseClientTest, superZone) { } TEST_F(DatabaseClientTest, noConnException) { - EXPECT_THROW(DatabaseClient(shared_ptr()), + EXPECT_THROW(DatabaseClient(shared_ptr()), isc::InvalidParameter); } diff --git a/src/lib/datasrc/tests/sqlite3_database_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc similarity index 98% rename from src/lib/datasrc/tests/sqlite3_database_unittest.cc rename to src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index cf82d190b5..101c02b420 100644 --- a/src/lib/datasrc/tests/sqlite3_database_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -12,7 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include +#include #include #include From 8975d286a6de827a02b073de32570602cd9cffbc Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 9 Aug 2011 05:23:41 -0400 Subject: [PATCH 385/974] [1144] fixes to Makefile.am --- src/lib/dns/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 887ac09fee..09c6e1961c 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -23,6 +23,7 @@ EXTRA_DIST += rdata/generic/cname_5.cc EXTRA_DIST += rdata/generic/cname_5.h EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h +EXTRA_DIST += rdata/generic/detail/ds_like.h EXTRA_DIST += rdata/generic/dname_39.cc EXTRA_DIST += rdata/generic/dname_39.h EXTRA_DIST += rdata/generic/dnskey_48.cc @@ -90,6 +91,7 @@ libdns___la_SOURCES += tsigkey.h tsigkey.cc libdns___la_SOURCES += tsigrecord.h tsigrecord.cc libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc +libdns___la_SOURCES += rdata/generic/detail/ds_like.h libdns___la_CPPFLAGS = $(AM_CPPFLAGS) # Most applications of libdns++ will only implicitly rely on libcryptolink, From 20483389cb90e4f46486be925b896c8a0438191c Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 9 Aug 2011 09:13:38 -0500 Subject: [PATCH 386/974] [master] update version in configure.ac (date stamp is used for now) --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4ff0d07975..0c8150efd3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.59]) -AC_INIT(bind10-devel, 20110519, bind10-dev@isc.org) +AC_INIT(bind10-devel, 20110809, bind10-dev@isc.org) AC_CONFIG_SRCDIR(README) AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([config.h]) From f6a1807c25d85a0ca762bfa276ebac4a3430e7c7 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 9 Aug 2011 09:18:56 -0500 Subject: [PATCH 387/974] [1011] small formatting change This change makes no difference. I am only doing this to test my pre-receive hook in a trac branch. --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index a6509da2d2..8d495e0ca3 100644 --- a/README +++ b/README @@ -67,8 +67,8 @@ e.g., Operating-System specific tips: - FreeBSD - You may need to install a python binding for sqlite3 by hand. A - sample procedure is as follows: + You may need to install a python binding for sqlite3 by hand. + A sample procedure is as follows: - add the following to /etc/make.conf PYTHON_VERSION=3.1 - build and install the python binding from ports, assuming the top From eb5023d2a38e0862e2d9a5f1ca4a3788fc131405 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 9 Aug 2011 15:54:42 -0400 Subject: [PATCH 388/974] [1144] fixes to address jinmei's review notes --- src/lib/dns/rdata/generic/detail/ds_like.h | 23 +++++++++----- src/lib/dns/rdata/generic/dlv_32769.cc | 32 +++++++++++++++++++ src/lib/dns/rdata/generic/dlv_32769.h | 37 +--------------------- src/lib/dns/rdata/generic/ds_43.cc | 32 +++++++++++++++++++ src/lib/dns/rdata/generic/ds_43.h | 37 +--------------------- 5 files changed, 81 insertions(+), 80 deletions(-) diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h index 20cf09469d..8d4c5b2492 100644 --- a/src/lib/dns/rdata/generic/detail/ds_like.h +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -20,9 +20,6 @@ #include #include -using namespace std; -using namespace isc::util; - struct DSImpl { // straightforward representation of DS RDATA fields DSImpl(uint16_t tag, uint8_t algorithm, uint8_t digest_type, @@ -48,16 +45,24 @@ public: iss >> tag >> algorithm >> digest_type >> &digestbuf; if (iss.bad() || iss.fail()) { - isc_throw(InvalidRdataText, "Invalid DS text"); + isc_throw(InvalidRdataText, "Invalid " + + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " text"); } if (tag > 0xffff) { - isc_throw(InvalidRdataText, "DS tag out of range"); + isc_throw(InvalidRdataText, + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " tag out of range"); } if (algorithm > 0xff) { - isc_throw(InvalidRdataText, "DS algorithm out of range"); + isc_throw(InvalidRdataText, + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " algorithm out of range"); } if (digest_type > 0xff) { - isc_throw(InvalidRdataText, "DS digest type out of range"); + isc_throw(InvalidRdataText, + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " digest type out of range"); } vector digest; @@ -68,7 +73,9 @@ public: DS_LIKE(InputBuffer& buffer, size_t rdata_len) { if (rdata_len < 4) { - isc_throw(InvalidRdataLength, "DS too short"); + isc_throw(InvalidRdataLength, + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " too short"); } uint16_t tag = buffer.readUint16(); diff --git a/src/lib/dns/rdata/generic/dlv_32769.cc b/src/lib/dns/rdata/generic/dlv_32769.cc index cf72d76882..d00282f739 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.cc +++ b/src/lib/dns/rdata/generic/dlv_32769.cc @@ -15,5 +15,37 @@ // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE +DLV::DLV(const std::string& type_str) : + DS_LIKE(type_str) +{} + +DLV::DLV(isc::util::InputBuffer& buffer, size_t rdata_len) : + DS_LIKE(buffer, rdata_len) +{} + +DLV::DLV(const DLV& other) : + DS_LIKE(other) +{} + +std::string DLV::toText() const +{ + return DS_LIKE::toText(); +} + +void DLV::toWire(isc::util::OutputBuffer& buffer) const +{ + DS_LIKE::toWire(buffer); +} + +void DLV::toWire(AbstractMessageRenderer& renderer) const +{ + DS_LIKE::toWire(renderer); +} + +int DLV::compare(const Rdata& other) const +{ + return DS_LIKE::compare(other); +} + // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/dlv_32769.h b/src/lib/dns/rdata/generic/dlv_32769.h index 1a8e7a3766..cf86234347 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.h +++ b/src/lib/dns/rdata/generic/dlv_32769.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -46,48 +47,12 @@ using namespace isc::util::encode; #include class DLV : public DS_LIKE { - friend class DS_LIKE; - static string const id; - public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS }; -/// explicit DLV(const std::string& type_str); -inline DLV::DLV(const std::string& type_str) : DS_LIKE(type_str) {} - -/// DLV(isc::util::InputBuffer& buffer, size_t rdata_len); -inline DLV::DLV(isc::util::InputBuffer& buffer, size_t rdata_len) : DS_LIKE(buffer, rdata_len) {} - -/// DLV(const DLV& other); -inline DLV::DLV(const DLV& other) : DS_LIKE(other) {} - -/// virtual std::string toText() const; -inline std::string DLV::toText() const -{ - return DS_LIKE::toText(); -} - -/// virtual void toWire(isc::util::OutputBuffer& buffer) const; -inline void DLV::toWire(isc::util::OutputBuffer& buffer) const -{ - DS_LIKE::toWire(buffer); -} - -/// virtual void toWire(AbstractMessageRenderer& renderer) const; -inline void DLV::toWire(AbstractMessageRenderer& renderer) const -{ - DS_LIKE::toWire(renderer); -} - -/// virtual int compare(const Rdata& other) const; -inline int DLV::compare(const Rdata& other) const -{ - return DS_LIKE::compare(other); -} - // END_RDATA_NAMESPACE // END_ISC_NAMESPACE // END_HEADER_GUARD diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc index cf72d76882..fe73e52630 100644 --- a/src/lib/dns/rdata/generic/ds_43.cc +++ b/src/lib/dns/rdata/generic/ds_43.cc @@ -15,5 +15,37 @@ // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE +DS::DS(const std::string& type_str) : + DS_LIKE(type_str) +{} + +DS::DS(isc::util::InputBuffer& buffer, size_t rdata_len) : + DS_LIKE(buffer, rdata_len) +{} + +DS::DS(const DS& other) : + DS_LIKE(other) +{} + +std::string DS::toText() const +{ + return DS_LIKE::toText(); +} + +void DS::toWire(isc::util::OutputBuffer& buffer) const +{ + DS_LIKE::toWire(buffer); +} + +void DS::toWire(AbstractMessageRenderer& renderer) const +{ + DS_LIKE::toWire(renderer); +} + +int DS::compare(const Rdata& other) const +{ + return DS_LIKE::compare(other); +} + // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/ds_43.h b/src/lib/dns/rdata/generic/ds_43.h index 0bb813587e..24715d997f 100644 --- a/src/lib/dns/rdata/generic/ds_43.h +++ b/src/lib/dns/rdata/generic/ds_43.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -46,48 +47,12 @@ using namespace isc::util::encode; #include class DS : public DS_LIKE { - friend class DS_LIKE; - static string const id; - public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS }; -/// explicit DS(const std::string& type_str); -inline DS::DS(const std::string& type_str) : DS_LIKE(type_str) {} - -/// DS(isc::util::InputBuffer& buffer, size_t rdata_len); -inline DS::DS(isc::util::InputBuffer& buffer, size_t rdata_len) : DS_LIKE(buffer, rdata_len) {} - -/// DS(const DS& other); -inline DS::DS(const DS& other) : DS_LIKE(other) {} - -/// virtual std::string toText() const; -inline std::string DS::toText() const -{ - return DS_LIKE::toText(); -} - -/// virtual void toWire(isc::util::OutputBuffer& buffer) const; -inline void DS::toWire(isc::util::OutputBuffer& buffer) const -{ - DS_LIKE::toWire(buffer); -} - -/// virtual void toWire(AbstractMessageRenderer& renderer) const; -inline void DS::toWire(AbstractMessageRenderer& renderer) const -{ - DS_LIKE::toWire(renderer); -} - -/// virtual int compare(const Rdata& other) const; -inline int DS::compare(const Rdata& other) const -{ - return DS_LIKE::compare(other); -} - // END_RDATA_NAMESPACE // END_ISC_NAMESPACE // END_HEADER_GUARD From 5d6fde4aa0d2a93945276dd722be48e05da72faf Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Aug 2011 18:25:26 -0700 Subject: [PATCH 389/974] [1113] trivial editorial fixes: folded long lines --- src/lib/dns/rdata/generic/minfo_14.cc | 4 +++- src/lib/dns/tests/rdata_minfo_unittest.cc | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/rdata/generic/minfo_14.cc b/src/lib/dns/rdata/generic/minfo_14.cc index d40a833a10..d7a8c13139 100644 --- a/src/lib/dns/rdata/generic/minfo_14.cc +++ b/src/lib/dns/rdata/generic/minfo_14.cc @@ -78,7 +78,9 @@ MINFO::MINFO(const std::string& minfo_str) : /// \exception std::bad_alloc Memory allocation for names fails. /// \exception Other The constructor of the \c Name class will throw if the /// names in the wire is invalid. -MINFO::MINFO(InputBuffer& buffer, size_t) : rmailbox_(buffer), emailbox_(buffer) { +MINFO::MINFO(InputBuffer& buffer, size_t) : + rmailbox_(buffer), emailbox_(buffer) +{ } /// \brief Copy constructor. diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc index 0f5ff13829..99945c6ed1 100644 --- a/src/lib/dns/tests/rdata_minfo_unittest.cc +++ b/src/lib/dns/tests/rdata_minfo_unittest.cc @@ -79,7 +79,8 @@ TEST_F(Rdata_MINFO_Test, badText) { EXPECT_THROW(generic::MINFO("root.example.com."), InvalidRdataText); // bad name - EXPECT_THROW(generic::MINFO("root.example.com. emailbx.example.com." + too_long_label), + EXPECT_THROW(generic::MINFO("root.example.com. emailbx.example.com." + + too_long_label), TooLongLabel); } From a03d7d9aae8ac258d266c66c62c63e03ff5d2558 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 9 Aug 2011 21:26:24 -0400 Subject: [PATCH 390/974] [1144] DS and DLV cleaned up --- src/lib/dns/Makefile.am | 2 +- src/lib/dns/rdata/generic/detail/ds_like.h | 4 +- src/lib/dns/rdata/generic/dlv_32769.cc | 51 ---------------------- src/lib/dns/rdata/generic/dlv_32769.h | 7 +-- src/lib/dns/rdata/generic/ds_43.cc | 51 ---------------------- src/lib/dns/rdata/generic/ds_43.h | 7 +-- 6 files changed, 5 insertions(+), 117 deletions(-) delete mode 100644 src/lib/dns/rdata/generic/dlv_32769.cc delete mode 100644 src/lib/dns/rdata/generic/ds_43.cc diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 88242bc707..a8bcb72f48 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -24,11 +24,11 @@ EXTRA_DIST += rdata/generic/cname_5.h EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h EXTRA_DIST += rdata/generic/detail/ds_like.h +EXTRA_DIST += rdata/generic/dlv_32769.h EXTRA_DIST += rdata/generic/dname_39.cc EXTRA_DIST += rdata/generic/dname_39.h EXTRA_DIST += rdata/generic/dnskey_48.cc EXTRA_DIST += rdata/generic/dnskey_48.h -EXTRA_DIST += rdata/generic/ds_43.cc EXTRA_DIST += rdata/generic/ds_43.h EXTRA_DIST += rdata/generic/mx_15.cc EXTRA_DIST += rdata/generic/mx_15.h diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h index 8d4c5b2492..3ed190f3d2 100644 --- a/src/lib/dns/rdata/generic/detail/ds_like.h +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -34,7 +34,7 @@ struct DSImpl { const vector digest_; }; -templateclass DS_LIKE : public Rdata { +templateclass DS_LIKE : public Rdata { public: DS_LIKE(const string& ds_str) : impl_(NULL) @@ -137,7 +137,7 @@ public: int compare(const Rdata& other) const { - const RTYPE& other_ds = dynamic_cast(other); + const DS_LIKE& other_ds = dynamic_cast(other); if (impl_->tag_ != other_ds.impl_->tag_) { return (impl_->tag_ < other_ds.impl_->tag_ ? -1 : 1); diff --git a/src/lib/dns/rdata/generic/dlv_32769.cc b/src/lib/dns/rdata/generic/dlv_32769.cc deleted file mode 100644 index d00282f739..0000000000 --- a/src/lib/dns/rdata/generic/dlv_32769.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC 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. - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -DLV::DLV(const std::string& type_str) : - DS_LIKE(type_str) -{} - -DLV::DLV(isc::util::InputBuffer& buffer, size_t rdata_len) : - DS_LIKE(buffer, rdata_len) -{} - -DLV::DLV(const DLV& other) : - DS_LIKE(other) -{} - -std::string DLV::toText() const -{ - return DS_LIKE::toText(); -} - -void DLV::toWire(isc::util::OutputBuffer& buffer) const -{ - DS_LIKE::toWire(buffer); -} - -void DLV::toWire(AbstractMessageRenderer& renderer) const -{ - DS_LIKE::toWire(renderer); -} - -int DLV::compare(const Rdata& other) const -{ - return DS_LIKE::compare(other); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/dlv_32769.h b/src/lib/dns/rdata/generic/dlv_32769.h index cf86234347..f1d27618de 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.h +++ b/src/lib/dns/rdata/generic/dlv_32769.h @@ -46,12 +46,7 @@ using namespace isc::util::encode; #include -class DLV : public DS_LIKE { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - -}; +typedef DS_LIKE<32769> DLV; // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc deleted file mode 100644 index fe73e52630..0000000000 --- a/src/lib/dns/rdata/generic/ds_43.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC 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. - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -DS::DS(const std::string& type_str) : - DS_LIKE(type_str) -{} - -DS::DS(isc::util::InputBuffer& buffer, size_t rdata_len) : - DS_LIKE(buffer, rdata_len) -{} - -DS::DS(const DS& other) : - DS_LIKE(other) -{} - -std::string DS::toText() const -{ - return DS_LIKE::toText(); -} - -void DS::toWire(isc::util::OutputBuffer& buffer) const -{ - DS_LIKE::toWire(buffer); -} - -void DS::toWire(AbstractMessageRenderer& renderer) const -{ - DS_LIKE::toWire(renderer); -} - -int DS::compare(const Rdata& other) const -{ - return DS_LIKE::compare(other); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/ds_43.h b/src/lib/dns/rdata/generic/ds_43.h index 24715d997f..eae22cbd41 100644 --- a/src/lib/dns/rdata/generic/ds_43.h +++ b/src/lib/dns/rdata/generic/ds_43.h @@ -46,12 +46,7 @@ using namespace isc::util::encode; #include -class DS : public DS_LIKE { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - -}; +typedef DS_LIKE<43> DS; // END_RDATA_NAMESPACE // END_ISC_NAMESPACE From 7990857c32cbb49f4bedf805f86c1b718b3a70d0 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 10 Aug 2011 14:30:15 +0800 Subject: [PATCH 391/974] [trac1113] fix comments --- src/lib/dns/rdata/generic/minfo_14.cc | 26 +++++++++-------------- src/lib/dns/rdata/generic/minfo_14.h | 10 --------- src/lib/dns/tests/rdata_minfo_unittest.cc | 2 +- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/src/lib/dns/rdata/generic/minfo_14.cc b/src/lib/dns/rdata/generic/minfo_14.cc index d7a8c13139..072c767f48 100644 --- a/src/lib/dns/rdata/generic/minfo_14.cc +++ b/src/lib/dns/rdata/generic/minfo_14.cc @@ -40,12 +40,11 @@ using namespace isc::dns; /// /// Exceptions /// -/// If or if not a valid domain name, a -/// corresponding exception from the \c Name class will be thrown; -/// If the number of RDATA fields (must be 2) is incorrect, an exception of -/// class \c InvalidRdataText will be thrown. -/// This constructor internally involves resource allocation, and if it -/// fails a corresponding standard exception std::bad_alloc will be throw. +/// \exception InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \exception std::bad_alloc Memory allocation for names fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the string is invalid. MINFO::MINFO(const std::string& minfo_str) : // We cannot construct both names in the initialization list due to the // necessary text processing, so we have to initialize them with a dummy @@ -96,8 +95,7 @@ MINFO::MINFO(const MINFO& other) : /// The output of this method is formatted as described in the "from string" /// constructor (\c MINFO(const std::string&))). /// -/// If internal resource allocation fails, a corresponding -/// standard exception will be thrown. +/// \exception std::bad_alloc Internal resource allocation fails. /// /// \return A \c string object that represents the \c MINFO object. std::string @@ -107,9 +105,7 @@ MINFO::toText() const { /// \brief Render the \c MINFO in the wire format without name compression. /// -/// If internal resource allocation fails, a corresponding standard -/// exception will be thrown. -/// This method never throws an exception otherwise. +/// \exception std::bad_alloc Internal resource allocation fails. /// /// \param buffer An output buffer to store the wire data. void @@ -121,12 +117,10 @@ MINFO::toWire(OutputBuffer& buffer) const { /// \brief Render the \c MINFO in the wire format with taking into account /// compression. /// -/// As specified in RFC1035, the rmailbox and emailbox fields (domain names) -/// will be compressed. +/// As specified in RFC3597, TYPE MINFO is "well-known", the rmailbox and +/// emailbox fields (domain names) will be compressed. /// -/// If internal resource allocation fails, a corresponding -/// standard exception will be thrown. -/// This method never throws an exception otherwise. +/// \exception std::bad_alloc Internal resource allocation fails. /// /// \param renderer DNS message rendering context that encapsulates the /// output buffer and name compression information. diff --git a/src/lib/dns/rdata/generic/minfo_14.h b/src/lib/dns/rdata/generic/minfo_14.h index 9a1120a737..0f3f87bb21 100644 --- a/src/lib/dns/rdata/generic/minfo_14.h +++ b/src/lib/dns/rdata/generic/minfo_14.h @@ -37,16 +37,6 @@ public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS - /// We use the default copy constructor and assignment operator. - - /// \brief Constructor from RDATA field parameters. - /// - /// The parameters are a straightforward mapping of %MINFO RDATA - /// fields as defined in RFC1035. - MINFO(const Name& rmailbox, const Name& emailbox) : - rmailbox_(rmailbox), emailbox_(emailbox) - {} - /// \brief Return the value of the rmailbox field. /// /// This method normally does not throw an exception, but if resource diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc index 99945c6ed1..1f6129a46d 100644 --- a/src/lib/dns/tests/rdata_minfo_unittest.cc +++ b/src/lib/dns/tests/rdata_minfo_unittest.cc @@ -103,7 +103,7 @@ TEST_F(Rdata_MINFO_Test, createFromWire) { InvalidRdataLength); // incomplete name. the error should be detected in the name constructor EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), - "rdata_cname_fromWire", 48), + "rdata_minfo_fromWire", 48), DNSMessageFORMERR); } From a53c7d7c450de09ceb04b47cb59450225827bd51 Mon Sep 17 00:00:00 2001 From: zhanglikun Date: Wed, 10 Aug 2011 17:51:36 +0800 Subject: [PATCH 392/974] [519] 1. Make stats module get boss's start time from the answer of command 'getstats'. 2. Make boss and stats receive msgq message(the call to group_recvmsg) in block mode. --- src/bin/bind10/bind10_src.py.in | 14 ++++++++++---- src/bin/stats/stats.py.in | 24 ++++++++++++++++-------- src/bin/stats/tests/b10-stats_test.py | 3 ++- src/bin/stats/tests/isc/cc/session.py | 10 +++++++++- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index b497f7c922..5da78bfd93 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -307,6 +307,11 @@ class BoB: process_list.append([pid, self.processes[pid].name]) return process_list + def _get_stats_data(self): + return { "stats_data": { + 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + }} + def command_handler(self, command, args): logger.debug(DBG_COMMANDS, BIND10_RECEIVED_COMMAND, command) answer = isc.config.ccsession.create_answer(1, "command not implemented") @@ -316,14 +321,15 @@ class BoB: if command == "shutdown": self.runnable = False answer = isc.config.ccsession.create_answer(0) + elif command == "getstats": + answer = isc.config.ccsession.create_answer(0, self._get_stats_data()) elif command == "sendstats": # send statistics data to the stats daemon immediately cmd = isc.config.ccsession.create_command( - 'set', { "stats_data": { - 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) - }}) + 'set', self._get_stats_data()) seq = self.cc_session.group_sendmsg(cmd, 'Stats') - self.cc_session.group_recvmsg(True, seq) + # Consume the answer, in case it becomes a orphan message. + self.cc_session.group_recvmsg(False, seq) answer = isc.config.ccsession.create_answer(0) elif command == "ping": answer = isc.config.ccsession.create_answer(0, "pong") diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index ce3d9f4612..022900a344 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -213,6 +213,12 @@ class CCSessionListener(Listener): except AttributeError as ae: logger.error(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) + def _update_stats_data(self, args): + # 'args' must be dictionary type + self.stats_data.update(args['stats_data']) + # overwrite "stats.LastUpdateTime" + self.stats_data['stats.last_update_time'] = get_datetime() + def start(self): """ start the cc chanel @@ -225,9 +231,16 @@ class CCSessionListener(Listener): self.cc_session.start() # request Bob to send statistics data logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) - cmd = isc.config.ccsession.create_command("sendstats", None) + cmd = isc.config.ccsession.create_command("getstats", None) seq = self.session.group_sendmsg(cmd, 'Boss') - self.session.group_recvmsg(True, seq) + try: + answer, env = self.session.group_recvmsg(False, seq) + if answer: + rcode, arg = isc.config.ccsession.parse_answer(answer) + if rcode == 0: + self._update_stats_data(arg) + except isc.cc.session.SessionTimeout: + pass def stop(self): """ @@ -276,12 +289,7 @@ class CCSessionListener(Listener): """ handle set command """ - # 'args' must be dictionary type - self.stats_data.update(args['stats_data']) - - # overwrite "stats.LastUpdateTime" - self.stats_data['stats.last_update_time'] = get_datetime() - + self._update_stats_data(args) return create_answer(0) def command_remove(self, args, stats_item_name=''): diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index a42c81d136..2fb4ab5e50 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -59,6 +59,7 @@ class TestStats(unittest.TestCase): # check starting self.assertFalse(self.subject.running) self.subject.start() + self.assertEqual(len(self.session.old_message_queue), 1) self.assertTrue(self.subject.running) self.assertEqual(len(self.session.message_queue), 0) self.assertEqual(self.module_name, 'Stats') @@ -509,7 +510,7 @@ class TestStats(unittest.TestCase): def test_for_boss(self): last_queue = self.session.old_message_queue.pop() self.assertEqual( - last_queue.msg, {'command': ['sendstats']}) + last_queue.msg, {'command': ['getstats']}) self.assertEqual( last_queue.env['group'], 'Boss') diff --git a/src/bin/stats/tests/isc/cc/session.py b/src/bin/stats/tests/isc/cc/session.py index e16d6a9abc..e18a695200 100644 --- a/src/bin/stats/tests/isc/cc/session.py +++ b/src/bin/stats/tests/isc/cc/session.py @@ -115,8 +115,16 @@ class Session: def group_recvmsg(self, nonblock=True, seq=0): que = self.dequeue() + if que.msg != None: + cmd = que.msg.get("command") + if cmd and cmd[0] == 'getstats': + # Create answer for command 'getstats' + retdata = { "stats_data": { + 'bind10.boot_time' : "1970-01-01T00:00:00Z" + }} + return {'result': [0, retdata]}, que.env return que.msg, que.env - + def group_reply(self, routing, msg): return self.enqueue(msg=msg, env={ "type": "send", From f82dc7b09f470f79ed2bf099216fa64c76528d3b Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 9 Aug 2011 11:19:13 +0200 Subject: [PATCH 393/974] [1062] addressed review comments --- src/lib/datasrc/database.cc | 270 +++++++++--------- src/lib/datasrc/database.h | 53 +++- src/lib/datasrc/sqlite3_connection.cc | 76 +++-- src/lib/datasrc/sqlite3_connection.h | 26 +- src/lib/datasrc/tests/database_unittest.cc | 215 +++++++++++++- .../tests/sqlite3_connection_unittest.cc | 148 ++++++++-- 6 files changed, 588 insertions(+), 200 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index ee257887da..8f13f525a4 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -71,93 +71,87 @@ DatabaseClient::Finder::Finder(boost::shared_ptr { } namespace { - // Adds the given Rdata to the given RRset - // If the rrset is an empty pointer, a new one is - // created with the given name, class, type and ttl - // The type is checked if the rrset exists, but the - // name is not. - // - // Then adds the given rdata to the set - // - // Raises a DataSourceError if the type does not - // match, or if the given rdata string does not - // parse correctly for the given type and class - void addOrCreate(isc::dns::RRsetPtr& rrset, - const isc::dns::Name& name, - const isc::dns::RRClass& cls, - const isc::dns::RRType& type, - const isc::dns::RRTTL& ttl, - const std::string& rdata_str) - { - if (!rrset) { - rrset.reset(new isc::dns::RRset(name, cls, type, ttl)); - } else { - if (ttl < rrset->getTTL()) { - rrset->setTTL(ttl); - } - // make sure the type is correct - if (type != rrset->getType()) { - isc_throw(DataSourceError, - "attempt to add multiple types to RRset in find()"); - } +// Adds the given Rdata to the given RRset +// If the rrset is an empty pointer, a new one is +// created with the given name, class, type and ttl +// The type is checked if the rrset exists, but the +// name is not. +// +// Then adds the given rdata to the set +// +// Raises a DataSourceError if the type does not +// match, or if the given rdata string does not +// parse correctly for the given type and class +void addOrCreate(isc::dns::RRsetPtr& rrset, + const isc::dns::Name& name, + const isc::dns::RRClass& cls, + const isc::dns::RRType& type, + const isc::dns::RRTTL& ttl, + const std::string& rdata_str) +{ + if (!rrset) { + rrset.reset(new isc::dns::RRset(name, cls, type, ttl)); + } else { + if (ttl < rrset->getTTL()) { + rrset->setTTL(ttl); } - if (rdata_str != "") { - try { - rrset->addRdata(isc::dns::rdata::createRdata(type, cls, - rdata_str)); - } catch (const isc::dns::rdata::InvalidRdataText& ivrt) { - // at this point, rrset may have been initialised for no reason, - // and won't be used. But the caller would drop the shared_ptr - // on such an error anyway, so we don't care. - isc_throw(DataSourceError, - "bad rdata in database for " << name.toText() << " " - << type.toText() << " " << ivrt.what()); + // make sure the type is correct + // TODO Assert? + if (type != rrset->getType()) { + isc_throw(DataSourceError, + "attempt to add multiple types to RRset in find()"); + } + } + try { + rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str)); + } catch (const isc::dns::rdata::InvalidRdataText& ivrt) { + // at this point, rrset may have been initialised for no reason, + // and won't be used. But the caller would drop the shared_ptr + // on such an error anyway, so we don't care. + isc_throw(DataSourceError, + "bad rdata in database for " << name << " " + << type << ": " << ivrt.what()); + } +} + +// This class keeps a short-lived store of RRSIG records encountered +// during a call to find(). If the backend happens to return signatures +// before the actual data, we might not know which signatures we will need +// So if they may be relevant, we store the in this class. +// +// (If this class seems useful in other places, we might want to move +// it to util. That would also provide an opportunity to add unit tests) +class RRsigStore { +public: + // Adds the given signature Rdata to the store + // The signature rdata MUST be of the RRSIG rdata type + // (the caller must make sure of this). + // NOTE: if we move this class to a public namespace, + // we should add a type_covered argument, so as not + // to have to do this cast here. + void addSig(isc::dns::rdata::RdataPtr sig_rdata) { + const isc::dns::RRType& type_covered = + static_cast( + sig_rdata.get())->typeCovered(); + sigs[type_covered].push_back(sig_rdata); + } + + // If the store contains signatures for the type of the given + // rrset, they are appended to it. + void appendSignatures(isc::dns::RRsetPtr& rrset) const { + std::map >::const_iterator + found = sigs.find(rrset->getType()); + if (found != sigs.end()) { + BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) { + rrset->addRRsig(sig); } } } - // This class keeps a short-lived store of RRSIG records encountered - // during a call to find(). If the backend happens to return signatures - // before the actual data, we might not know which signatures we will need - // So if they may be relevant, we store the in this class. - // - // (If this class seems useful in other places, we might want to move - // it to util. That would also provide an opportunity to add unit tests) - class RRsigStore { - public: - // Adds the given signature Rdata to the store - // The signature rdata MUST be of the RRSIG rdata type - // (the caller must make sure of this) - void addSig(isc::dns::rdata::RdataPtr sig_rdata) { - const isc::dns::RRType& type_covered = - static_cast( - sig_rdata.get())->typeCovered(); - if (!haveSigsFor(type_covered)) { - sigs[type_covered] = std::vector(); - } - sigs.find(type_covered)->second.push_back(sig_rdata); - } - - // Returns true if this store contains signatures covering the - // given type - bool haveSigsFor(isc::dns::RRType type) const { - return (sigs.count(type) > 0); - } - - // If the store contains signatures for the type of the given - // rrset, they are appended to it. - void appendSignatures(isc::dns::RRsetPtr& rrset) const { - if (haveSigsFor(rrset->getType())) { - BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, - sigs.find(rrset->getType())->second) { - rrset->addRRsig(sig); - } - } - } - - private: - std::map > sigs; - }; +private: + std::map > sigs; +}; } @@ -174,55 +168,75 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, ZoneFinder::Result result_status = SUCCESS; RRsigStore sig_store; - connection_->searchForRecords(zone_id_, name.toText()); + try { + connection_->searchForRecords(zone_id_, name.toText()); - std::vector columns; - while (connection_->getNextRecord(columns)) { - if (!records_found) { - records_found = true; - } - - if (columns.size() != 4) { - isc_throw(DataSourceError, "Datasource backend did not return 4 " - "columns in getNextRecord()"); - } - - try { - const isc::dns::RRType cur_type(columns[0]); - const isc::dns::RRTTL cur_ttl(columns[1]); - //cur_sigtype(columns[2]); - - if (cur_type == type) { - addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, - columns[3]); - } else if (cur_type == isc::dns::RRType::CNAME()) { - // There should be no other data, so cur_rrset should be empty, - if (result_rrset) { - isc_throw(DataSourceError, "CNAME found but it is not " - "the only record for " + name.toText()); - } - addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, - columns[3]); - result_status = CNAME; - } else if (cur_type == isc::dns::RRType::RRSIG()) { - // If we get signatures before we get the actual data, we - // can't know which ones to keep and which to drop... - // So we keep a separate store of any signature that may be - // relevant and add them to the final RRset when we are done. - // A possible optimization here is to not store them for types - // we are certain we don't need - isc::dns::rdata::RdataPtr cur_rrsig( - isc::dns::rdata::createRdata(cur_type, getClass(), - columns[3])); - sig_store.addSig(cur_rrsig); + std::string columns[DatabaseConnection::RecordColumnCount]; + while (connection_->getNextRecord(columns, + DatabaseConnection::RecordColumnCount)) { + if (!records_found) { + records_found = true; + } + + try { + const isc::dns::RRType cur_type(columns[DatabaseConnection::TYPE_COLUMN]); + const isc::dns::RRTTL cur_ttl(columns[DatabaseConnection::TTL_COLUMN]); + // Ths sigtype column was an optimization for finding the relevant + // RRSIG RRs for a lookup. Currently this column is not used in this + // revised datasource implementation. We should either start using it + // again, or remove it from use completely (i.e. also remove it from + // the schema and the backend implementation). + // Note that because we don't use it now, we also won't notice it if + // the value is wrong (i.e. if the sigtype column contains an rrtype + // that is different from the actual value of the 'type covered' field + // in the RRSIG Rdata). + //cur_sigtype(columns[SIGTYPE_COLUMN]); + + if (cur_type == type) { + addOrCreate(result_rrset, name, getClass(), cur_type, + cur_ttl, columns[DatabaseConnection::RDATA_COLUMN]); + } else if (cur_type == isc::dns::RRType::CNAME()) { + // There should be no other data, so result_rrset should be empty. + if (result_rrset) { + isc_throw(DataSourceError, "CNAME found but it is not " + "the only record for " + name.toText()); + } + addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, + columns[DatabaseConnection::RDATA_COLUMN]); + result_status = CNAME; + } else if (cur_type == isc::dns::RRType::RRSIG()) { + // If we get signatures before we get the actual data, we + // can't know which ones to keep and which to drop... + // So we keep a separate store of any signature that may be + // relevant and add them to the final RRset when we are done. + // A possible optimization here is to not store them for types + // we are certain we don't need + sig_store.addSig(isc::dns::rdata::createRdata(cur_type, + getClass(), + columns[DatabaseConnection::RDATA_COLUMN])); + } + } catch (const isc::dns::InvalidRRType& irt) { + isc_throw(DataSourceError, "Invalid RRType in database for " << + name << ": " << columns[DatabaseConnection::TYPE_COLUMN]); + } catch (const isc::dns::InvalidRRTTL& irttl) { + isc_throw(DataSourceError, "Invalid TTL in database for " << + name << ": " << columns[DatabaseConnection::TTL_COLUMN]); + } catch (const isc::dns::rdata::InvalidRdataText& ird) { + isc_throw(DataSourceError, "Invalid rdata in database for " << + name << ": " << columns[DatabaseConnection::RDATA_COLUMN]); } - } catch (const isc::dns::InvalidRRType& irt) { - isc_throw(DataSourceError, "Invalid RRType in database for " << - name << ": " << columns[0]); - } catch (const isc::dns::InvalidRRTTL& irttl) { - isc_throw(DataSourceError, "Invalid TTL in database for " << - name << ": " << columns[1]); } + } catch (const DataSourceError& dse) { + // call cleanup and rethrow + connection_->resetSearch(); + throw; + } catch (const isc::Exception& isce) { +// // cleanup, change it to a DataSourceError and rethrow + connection_->resetSearch(); + isc_throw(DataSourceError, isce.what()); + } catch (const std::exception& ex) { + connection_->resetSearch(); + throw; } if (!result_rrset) { diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index d82c86f771..0632f64321 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -92,14 +92,59 @@ public: * Returns a boolean specifying whether or not there was more data to read. * In the case of a database error, a DatasourceError is thrown. * + * The columns passed is an array of std::strings consisting of + * DatabaseConnection::RecordColumnCount elements, the elements of which + * are defined in DatabaseConnection::RecordColumns, in their basic + * string representation. + * + * If you are implementing a derived database connection class, you + * should have this method check the column_count value, and fill the + * array with strings conforming to their description in RecordColumn. + * * \exception DatasourceError if there was an error reading from the database * - * \param columns This vector will be cleared, and the fields of the record will - * be appended here as strings (in the order rdtype, ttl, sigtype, - * and rdata). If there was no data, the vector is untouched. + * \param columns The elements of this array will be filled with the data + * for one record as defined by RecordColumns + * If there was no data, the array is untouched. * \return true if there was a next record, false if there was not */ - virtual bool getNextRecord(std::vector& columns) = 0; + virtual bool getNextRecord(std::string columns[], size_t column_count) = 0; + + /** + * \brief Resets the current search initiated with searchForRecords() + * + * This method will be called when the called of searchForRecords() and + * getNextRecord() finds bad data, and aborts the current search. + * It should clean up whatever handlers searchForRecords() created, and + * any other state modified or needed by getNextRecord() + * + * Of course, the implementation of getNextRecord may also use it when + * it is done with a search. If it does, the implementation of this + * method should make sure it can handle being called multiple times. + * + * The implementation for this method should make sure it never throws. + */ + virtual void resetSearch() = 0; + + /** + * Definitions of the fields as they are required to be filled in + * by getNextRecord() + * + * When implementing getNextRecord(), the columns array should + * be filled with the values as described in this enumeration, + * in this order. + */ + enum RecordColumns { + TYPE_COLUMN = 0, ///< The RRType of the record (A/NS/TXT etc.) + TTL_COLUMN = 1, ///< The TTL of the record (a + SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPE + ///< the RRSIG covers. In the current implementation, + ///< this field is ignored. + RDATA_COLUMN = 3 ///< Full text representation of the record's RDATA + }; + + /// The number of fields the columns array passed to getNextRecord should have + static const size_t RecordColumnCount = 4; }; /** diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index 70adde4f6f..750a62cf4c 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -321,15 +321,30 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const { void SQLite3Connection::searchForRecords(int zone_id, const std::string& name) { - sqlite3_reset(dbparameters_->q_any_); - sqlite3_clear_bindings(dbparameters_->q_any_); - sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id); + resetSearch(); + int result; + result = sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id); + if (result != SQLITE_OK) { + isc_throw(DataSourceError, + "Error in sqlite3_bind_int() for zone_id " << + zone_id << ", sqlite3 result code: " << result); + } // use transient since name is a ref and may disappear - sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, - SQLITE_TRANSIENT); + result = sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, + SQLITE_TRANSIENT); + if (result != SQLITE_OK) { + isc_throw(DataSourceError, + "Error in sqlite3_bind_text() for name " << + name << ", sqlite3 result code: " << result); + } }; namespace { +// This helper function converts from the unsigned char* type (used by +// sqlite3) to char* (wanted by std::string). Technically these types +// might not be directly convertable +// In case sqlite3_column_text() returns NULL, we just make it an +// empty string. const char* convertToPlainChar(const unsigned char* ucp) { if (ucp == NULL) { @@ -341,31 +356,44 @@ convertToPlainChar(const unsigned char* ucp) { } bool -SQLite3Connection::getNextRecord(std::vector& columns) { - sqlite3_stmt* current_stmt = dbparameters_->q_any_; - const int rc = sqlite3_step(current_stmt); +SQLite3Connection::getNextRecord(std::string columns[], size_t column_count) { + try { + sqlite3_stmt* current_stmt = dbparameters_->q_any_; + const int rc = sqlite3_step(current_stmt); - if (rc == SQLITE_ROW) { - columns.clear(); - for (int column = 0; column < 4; ++column) { - columns.push_back(convertToPlainChar(sqlite3_column_text( - current_stmt, column))); + if (column_count != RecordColumnCount) { + isc_throw(DataSourceError, + "Datasource backend caller did not pass a column array " + "of size " << RecordColumnCount << + " to getNextRecord()"); } - return (true); - } else if (rc == SQLITE_DONE) { - // reached the end of matching rows - sqlite3_reset(current_stmt); - sqlite3_clear_bindings(current_stmt); - return (false); - } - sqlite3_reset(current_stmt); - sqlite3_clear_bindings(current_stmt); - isc_throw(DataSourceError, - "Unexpected failure in sqlite3_step (sqlite result code " << rc << ")"); + if (rc == SQLITE_ROW) { + for (int column = 0; column < column_count; ++column) { + columns[column] = convertToPlainChar(sqlite3_column_text( + current_stmt, column)); + } + return (true); + } else if (rc == SQLITE_DONE) { + // reached the end of matching rows + resetSearch(); + return (false); + } + resetSearch(); + isc_throw(DataSourceError, + "Unexpected failure in sqlite3_step (sqlite result code " << rc << ")"); + } catch (std::bad_alloc) { + isc_throw(DataSourceError, "bad_alloc in Sqlite3Connection::getNextRecord"); + } // Compilers might not realize isc_throw always throws return (false); } +void +SQLite3Connection::resetSearch() { + sqlite3_reset(dbparameters_->q_any_); + sqlite3_clear_bindings(dbparameters_->q_any_); +} + } } diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index ffb2470b8e..c1968c4a34 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -95,6 +95,9 @@ public: * This implements the searchForRecords from DatabaseConnection. * This particular implementation does not raise DataSourceError. * + * \exception DataSourceError when sqlite3_bind_int() or + * sqlite3_bind_text() fails + * * \param zone_id The zone to seach in, as returned by getZone() * \param name The name to find records for */ @@ -107,12 +110,31 @@ public: * This implements the getNextRecord from DatabaseConnection. * See the documentation there for more information. * + * If this method raises an exception, the contents of columns are undefined. + * + * \exception DataSourceError if there is an error returned by sqlite_step() + * When this exception is raised, the current + * search as initialized by searchForRecords() is + * NOT reset, and the caller is expected to take + * care of that. * \param columns This vector will be cleared, and the fields of the record will * be appended here as strings (in the order rdtype, ttl, sigtype, - * and rdata). If there was no data, the vector is untouched. + * and rdata). If there was no data (i.e. if this call returns + * false), the vector is untouched. * \return true if there was a next record, false if there was not */ - virtual bool getNextRecord(std::vector& columns); + virtual bool getNextRecord(std::string columns[], size_t column_count); + + /** + * \brief Resets any state created by searchForRecords + * + * This implements the resetSearch from DatabaseConnection. + * See the documentation there for more information. + * + * This function never throws. + */ + virtual void resetSearch(); + private: /// \brief Private database data SQLite3Parameters* dbparameters_; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index c31593217f..69678f0047 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -36,7 +37,7 @@ namespace { */ class MockConnection : public DatabaseConnection { public: - MockConnection() { fillData(); } + MockConnection() : search_running_(false) { fillData(); } virtual std::pair getZone(const Name& name) const { if (name == Name("example.org")) { @@ -47,10 +48,23 @@ public: } virtual void searchForRecords(int zone_id, const std::string& name) { + search_running_ = true; + + // 'hardcoded' name to trigger exceptions (for testing + // the error handling of find() (the other on is below in + // if the name is "exceptiononsearch" it'll raise an exception here + if (name == "dsexception.in.search.") { + isc_throw(DataSourceError, "datasource exception on search"); + } else if (name == "iscexception.in.search.") { + isc_throw(isc::Exception, "isc exception on search"); + } else if (name == "basicexception.in.search.") { + throw std::exception(); + } + searched_name_ = name; + // we're not aiming for efficiency in this test, simply // copy the relevant vector from records cur_record = 0; - if (zone_id == 42) { if (records.count(name) > 0) { cur_name = records.find(name)->second; @@ -62,15 +76,38 @@ public: } }; - virtual bool getNextRecord(std::vector& columns) { + virtual bool getNextRecord(std::string columns[], size_t column_count) { + if (searched_name_ == "dsexception.in.getnext.") { + isc_throw(DataSourceError, "datasource exception on getnextrecord"); + } else if (searched_name_ == "iscexception.in.getnext.") { + isc_throw(isc::Exception, "isc exception on getnextrecord"); + } else if (searched_name_ == "basicexception.in.getnext.") { + throw std::exception(); + } + + if (column_count != DatabaseConnection::RecordColumnCount) { + isc_throw(DataSourceError, "Wrong column count in getNextRecord"); + } if (cur_record < cur_name.size()) { - columns = cur_name[cur_record++]; + for (size_t i = 0; i < column_count; ++i) { + columns[i] = cur_name[cur_record][i]; + } + cur_record++; return (true); } else { + resetSearch(); return (false); } }; + virtual void resetSearch() { + search_running_ = false; + }; + + bool searchRunning() const { + return (search_running_); + } + private: std::map > > records; // used as internal index for getNextRecord() @@ -80,6 +117,14 @@ private: // fake data std::vector< std::vector > cur_name; + // This boolean is used to make sure find() calls resetSearch + // when it encounters an error + bool search_running_; + + // We store the name passed to searchForRecords, so we can + // hardcode some exceptions into getNextRecord + std::string searched_name_; + // Adds one record to the current name in the database // The actual data will not be added to 'records' until // addCurName() is called @@ -121,6 +166,11 @@ private: addRecord("AAAA", "3600", "", "2001:db8::2"); addCurName("www.example.org."); + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("AAAA", "3600", "", "2001:db8::1"); + addRecord("A", "3600", "", "192.0.2.2"); + addCurName("www2.example.org."); + addRecord("CNAME", "3600", "", "www.example.org."); addCurName("cname.example.org."); @@ -165,18 +215,42 @@ private: addRecord("A", "3600", "", "192.0.2.1"); addCurName("acnamesig3.example.org."); + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("A", "360", "", "192.0.2.2"); + addCurName("ttldiff1.example.org."); + addRecord("A", "360", "", "192.0.2.1"); + addRecord("A", "3600", "", "192.0.2.2"); + addCurName("ttldiff2.example.org."); + // also add some intentionally bad data - cur_name.push_back(std::vector()); - addCurName("emptyvector.example.org."); addRecord("A", "3600", "", "192.0.2.1"); addRecord("CNAME", "3600", "", "www.example.org."); - addCurName("badcname.example.org."); + addCurName("badcname1.example.org."); + + addRecord("CNAME", "3600", "", "www.example.org."); + addRecord("A", "3600", "", "192.0.2.1"); + addCurName("badcname2.example.org."); + + addRecord("CNAME", "3600", "", "www.example.org."); + addRecord("CNAME", "3600", "", "www.example2.org."); + addCurName("badcname3.example.org."); + addRecord("A", "3600", "", "bad"); addCurName("badrdata.example.org."); + addRecord("BAD_TYPE", "3600", "", "192.0.2.1"); addCurName("badtype.example.org."); + addRecord("A", "badttl", "", "192.0.2.1"); addCurName("badttl.example.org."); + + addRecord("A", "badttl", "", "192.0.2.1"); + addRecord("RRSIG", "3600", "", "A 5 3 3600 somebaddata 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addCurName("badsig.example.org."); + + addRecord("A", "3600", "", "192.0.2.1"); + addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + addCurName("badsigtype.example.org."); } }; @@ -241,18 +315,21 @@ doFindTest(shared_ptr finder, const isc::dns::Name& name, const isc::dns::RRType& type, const isc::dns::RRType& expected_type, + const isc::dns::RRTTL expected_ttl, ZoneFinder::Result expected_result, unsigned int expected_rdata_count, unsigned int expected_signature_count) { - ZoneFinder::FindResult result = finder->find(name, type, - NULL, ZoneFinder::FIND_DEFAULT); - ASSERT_EQ(expected_result, result.code) << name.toText() << " " << type.toText(); + ZoneFinder::FindResult result = + finder->find(name, type, NULL, ZoneFinder::FIND_DEFAULT); + ASSERT_EQ(expected_result, result.code) << name << " " << type; if (expected_rdata_count > 0) { EXPECT_EQ(expected_rdata_count, result.rrset->getRdataCount()); + EXPECT_EQ(expected_ttl, result.rrset->getTTL()); EXPECT_EQ(expected_type, result.rrset->getType()); if (expected_signature_count > 0) { - EXPECT_EQ(expected_signature_count, result.rrset->getRRsig()->getRdataCount()); + EXPECT_EQ(expected_signature_count, + result.rrset->getRRsig()->getRdataCount()); } else { EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig()); } @@ -268,79 +345,189 @@ TEST_F(DatabaseClientTest, find) { shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); EXPECT_EQ(42, finder->zone_id()); - const isc::dns::Name name("www.example.org."); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 1, 0); + EXPECT_FALSE(current_connection_->searchRunning()); + doFindTest(finder, isc::dns::Name("www2.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), + ZoneFinder::SUCCESS, 2, 0); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 2, 0); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, 0, 0); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), + isc::dns::RRTTL(3600), ZoneFinder::CNAME, 1, 0); + EXPECT_FALSE(current_connection_->searchRunning()); + doFindTest(finder, isc::dns::Name("cname.example.org."), + isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(), + isc::dns::RRTTL(3600), + ZoneFinder::SUCCESS, 1, 0); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("doesnotexist.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, 0, 0); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 1, 2); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 2, 1); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, 0, 0); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("signedcname1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), + isc::dns::RRTTL(3600), ZoneFinder::CNAME, 1, 1); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 1, 2); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 2, 1); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, 0, 0); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("signedcname2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), + isc::dns::RRTTL(3600), ZoneFinder::CNAME, 1, 1); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("acnamesig1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 1, 1); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("acnamesig2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 1, 1); + EXPECT_FALSE(current_connection_->searchRunning()); doFindTest(finder, isc::dns::Name("acnamesig3.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, 1, 1); + EXPECT_FALSE(current_connection_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("emptyvector.example.org."), + doFindTest(finder, isc::dns::Name("ttldiff1.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(360), + ZoneFinder::SUCCESS, 2, 0); + EXPECT_FALSE(current_connection_->searchRunning()); + doFindTest(finder, isc::dns::Name("ttldiff2.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(360), + ZoneFinder::SUCCESS, 2, 0); + EXPECT_FALSE(current_connection_->searchRunning()); + + EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_THROW(finder->find(isc::dns::Name("badcname.example.org."), + EXPECT_FALSE(current_connection_->searchRunning()); + EXPECT_THROW(finder->find(isc::dns::Name("badcname2.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); + EXPECT_THROW(finder->find(isc::dns::Name("badcname3.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); + EXPECT_THROW(finder->find(isc::dns::Name("badsig.example.org."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); + + // Trigger the hardcoded exceptions and see if find() has cleaned up + /* + EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); + EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); + EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + std::exception); + EXPECT_FALSE(current_connection_->searchRunning()); + */ + EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); + EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(current_connection_->searchRunning()); + EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."), + isc::dns::RRType::A(), + NULL, ZoneFinder::FIND_DEFAULT), + std::exception); + EXPECT_FALSE(current_connection_->searchRunning()); + + // This RRSIG has the wrong sigtype field, which should be + // an error if we decide to keep using that field + // Right now the field is ignored, so it does not error + doFindTest(finder, isc::dns::Name("badsigtype.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), + ZoneFinder::SUCCESS, 1, 1); + EXPECT_FALSE(current_connection_->searchRunning()); } diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 0d0b8c35f6..7f7032238d 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -11,8 +11,6 @@ // 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. -#include - #include #include @@ -20,6 +18,7 @@ #include #include +#include using namespace isc::datasrc; using isc::data::ConstElementPtr; @@ -76,7 +75,7 @@ public: conn.reset(new SQLite3Connection(filename, rrclass)); } // The tested connection - boost::shared_ptr conn; + boost::scoped_ptr conn; }; // This zone exists in the data, so it should be found @@ -102,22 +101,19 @@ TEST_F(SQLite3Conn, noClass) { EXPECT_FALSE(conn->getZone(Name("example.com")).first); } -namespace { - // Simple function to count the number of records for - // any name - size_t countRecords(boost::shared_ptr& conn, - int zone_id, const std::string& name) - { - conn->searchForRecords(zone_id, name); - size_t count = 0; - std::vector columns; - while (conn->getNextRecord(columns)) { - EXPECT_EQ(4, columns.size()); - ++count; - } - return (count); - } -} +// Simple function to cound the number of records for +// any name +void +checkRecordRow(const std::string columns[], + const std::string& field0, + const std::string& field1, + const std::string& field2, + const std::string& field3) +{ + EXPECT_EQ(field0, columns[0]); + EXPECT_EQ(field1, columns[1]); + EXPECT_EQ(field2, columns[2]); + EXPECT_EQ(field3, columns[3]); } TEST_F(SQLite3Conn, getRecords) { @@ -127,16 +123,112 @@ TEST_F(SQLite3Conn, getRecords) { const int zone_id = zone_info.second; ASSERT_EQ(1, zone_id); + const size_t column_count = DatabaseConnection::RecordColumnCount; + std::string columns[column_count]; + // without search, getNext() should return false - std::vector columns; - EXPECT_FALSE(conn->getNextRecord(columns)); - EXPECT_EQ(0, columns.size()); + EXPECT_FALSE(conn->getNextRecord(columns, + column_count)); + checkRecordRow(columns, "", "", "", ""); - EXPECT_EQ(4, countRecords(conn, zone_id, "foo.example.com.")); - EXPECT_EQ(15, countRecords(conn, zone_id, "example.com.")); - EXPECT_EQ(0, countRecords(conn, zone_id, "foo.bar.")); - EXPECT_EQ(0, countRecords(conn, zone_id, "")); + conn->searchForRecords(zone_id, "foo.bar."); + EXPECT_FALSE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "", "", "", ""); - EXPECT_FALSE(conn->getNextRecord(columns)); - EXPECT_EQ(0, columns.size()); + conn->searchForRecords(zone_id, ""); + EXPECT_FALSE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "", "", "", ""); + + // Should error on a bad number of columns + EXPECT_THROW(conn->getNextRecord(columns, 3), DataSourceError); + EXPECT_THROW(conn->getNextRecord(columns, 5), DataSourceError); + + // now try some real searches + conn->searchForRecords(zone_id, "foo.example.com."); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "CNAME", "3600", "", + "cnametest.example.org."); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "RRSIG", "3600", "CNAME", + "CNAME 5 3 3600 20100322084538 20100220084538 33495 " + "example.com. FAKEFAKEFAKEFAKE"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "NSEC", "7200", "", + "mail.example.com. CNAME RRSIG NSEC"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "RRSIG", "7200", "NSEC", + "NSEC 5 3 7200 20100322084538 20100220084538 33495 " + "example.com. FAKEFAKEFAKEFAKE"); + EXPECT_FALSE(conn->getNextRecord(columns, column_count)); + // with no more records, the array should not have been modified + checkRecordRow(columns, "RRSIG", "7200", "NSEC", + "NSEC 5 3 7200 20100322084538 20100220084538 33495 " + "example.com. FAKEFAKEFAKEFAKE"); + + conn->searchForRecords(zone_id, "example.com."); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "SOA", "3600", "", + "master.example.com. admin.example.com. " + "1234 3600 1800 2419200 7200"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "RRSIG", "3600", "SOA", + "SOA 5 2 3600 20100322084538 20100220084538 " + "33495 example.com. FAKEFAKEFAKEFAKE"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "NS", "1200", "", "dns01.example.com."); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "NS", "3600", "", "dns02.example.com."); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "NS", "1800", "", "dns03.example.com."); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "RRSIG", "3600", "NS", + "NS 5 2 3600 20100322084538 20100220084538 " + "33495 example.com. FAKEFAKEFAKEFAKE"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "MX", "3600", "", "10 mail.example.com."); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "MX", "3600", "", + "20 mail.subzone.example.com."); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "RRSIG", "3600", "MX", + "MX 5 2 3600 20100322084538 20100220084538 " + "33495 example.com. FAKEFAKEFAKEFAKE"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "NSEC", "7200", "", + "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "RRSIG", "7200", "NSEC", + "NSEC 5 2 7200 20100322084538 20100220084538 " + "33495 example.com. FAKEFAKEFAKEFAKE"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "DNSKEY", "3600", "", + "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4q NGV7W" + "cTD0WEiuV7IjXgHE36fCmS9QsUxSSOV o1I/FMxI2PJVqTYHkX" + "FBS7AzLGsQYMU7UjBZ SotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g" + "0qcbW YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "DNSKEY", "3600", "", + "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJ tbvzg" + "62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV 4HQZJStJaZ+fHU5AwV" + "NT+bBZdtV+NujSikhd THb4FYLg2b3Cx9NyJvAVukHp/91HnWu" + "G4T36 CzAFrfPwsHIrBz9BsaIQ21VRkcmj7DswfI/i DGd8j6b" + "qiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq 23TaOrVTjB7d1a/h31OD" + "fiHAxFHrkY3t3D5J R9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86" + "Acv RyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGf oIK/aKwEN" + "rsjcKZZj660b1M="); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", + "DNSKEY 5 2 3600 20100322084538 20100220084538 " + "4456 example.com. FAKEFAKEFAKEFAKE"); + ASSERT_TRUE(conn->getNextRecord(columns, column_count)); + checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", + "DNSKEY 5 2 3600 20100322084538 20100220084538 " + "33495 example.com. FAKEFAKEFAKEFAKE"); + EXPECT_FALSE(conn->getNextRecord(columns, column_count)); + // getnextrecord returning false should mean array is not altered + checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", + "DNSKEY 5 2 3600 20100322084538 20100220084538 " + "33495 example.com. FAKEFAKEFAKEFAKE"); } + +} // end anonymous namespace From a36be891057f7a2505db032768264c79f37f05e7 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Wed, 10 Aug 2011 08:59:43 -0400 Subject: [PATCH 394/974] [1140] cleanup along the lines of [1144] --- src/lib/dns/Makefile.am | 4 +- src/lib/dns/rdata/generic/detail/txt_like.h | 22 +++++------ src/lib/dns/rdata/generic/spf_99.cc | 19 --------- src/lib/dns/rdata/generic/spf_99.h | 43 +-------------------- src/lib/dns/rdata/generic/txt_16.cc | 19 --------- src/lib/dns/rdata/generic/txt_16.h | 43 +-------------------- 6 files changed, 16 insertions(+), 134 deletions(-) delete mode 100644 src/lib/dns/rdata/generic/spf_99.cc delete mode 100644 src/lib/dns/rdata/generic/txt_16.cc diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 4a0173cb17..56444bda4b 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -23,6 +23,7 @@ EXTRA_DIST += rdata/generic/cname_5.cc EXTRA_DIST += rdata/generic/cname_5.h EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h +EXTRA_DIST += rdata/generic/detail/txt_like.h EXTRA_DIST += rdata/generic/dname_39.cc EXTRA_DIST += rdata/generic/dname_39.h EXTRA_DIST += rdata/generic/dnskey_48.cc @@ -49,7 +50,7 @@ EXTRA_DIST += rdata/generic/rrsig_46.cc EXTRA_DIST += rdata/generic/rrsig_46.h EXTRA_DIST += rdata/generic/soa_6.cc EXTRA_DIST += rdata/generic/soa_6.h -EXTRA_DIST += rdata/generic/txt_16.cc +EXTRA_DIST += rdata/generic/spf_99.h EXTRA_DIST += rdata/generic/txt_16.h EXTRA_DIST += rdata/hs_4/a_1.cc EXTRA_DIST += rdata/hs_4/a_1.h @@ -92,6 +93,7 @@ libdns___la_SOURCES += tsigkey.h tsigkey.cc libdns___la_SOURCES += tsigrecord.h tsigrecord.cc libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc +libdns___la_SOURCES += rdata/generic/detail/txt_like.h libdns___la_CPPFLAGS = $(AM_CPPFLAGS) # Most applications of libdns++ will only implicitly rely on libcryptolink, diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h index 3e117f0371..0f18c8add4 100644 --- a/src/lib/dns/rdata/generic/detail/txt_like.h +++ b/src/lib/dns/rdata/generic/detail/txt_like.h @@ -23,7 +23,7 @@ using namespace std; using namespace isc::util; -templateclass TXT_LIKE : public Rdata { +templateclass TXT_LIKE : public Rdata { public: TXT_LIKE(InputBuffer& buffer, size_t rdata_len) { if (rdata_len > MAX_RDLENGTH) { @@ -31,17 +31,17 @@ public: } if (rdata_len == 0) { // note that this couldn't happen in the loop. - isc_throw(DNSMessageFORMERR, - "Error in parsing " + RRParamRegistry::getRegistry().codeToTypeText(typeCode) - + " RDATA: 0-length character string"); + isc_throw(DNSMessageFORMERR, "Error in parsing " + + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " RDATA: 0-length character string"); } do { const uint8_t len = buffer.readUint8(); if (rdata_len < len + 1) { - isc_throw(DNSMessageFORMERR, - "Error in parsing " + RRParamRegistry::getRegistry().codeToTypeText(typeCode) - + " RDATA: character string length is too large: " << static_cast(len)); + isc_throw(DNSMessageFORMERR, "Error in parsing " + + RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " RDATA: character string length is too large: " << static_cast(len)); } vector data(len + 1); data[0] = len; @@ -71,8 +71,8 @@ public: // TBD: right now, we don't support escaped characters if (txtstr.find('\\') != string::npos) { - isc_throw(InvalidRdataText, RRParamRegistry::getRegistry().codeToTypeText(typeCode) - + " RDATA from text: escaped character is currently not supported: " << txtstr); + isc_throw(InvalidRdataText, RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + " RDATA from text: escaped character is currently not supported: " << txtstr); } vector data; @@ -83,7 +83,7 @@ public: string_list_.push_back(data); } - TXT_LIKE(const RTYPE& other) : + TXT_LIKE(const TXT_LIKE& other) : Rdata(), string_list_(other.string_list_) {} @@ -130,7 +130,7 @@ public: int compare(const Rdata& other) const { - const RTYPE& other_txt = dynamic_cast(other); + const TXT_LIKE& other_txt = dynamic_cast(other); // This implementation is not efficient. Revisit this (TBD). OutputBuffer this_buffer(0); diff --git a/src/lib/dns/rdata/generic/spf_99.cc b/src/lib/dns/rdata/generic/spf_99.cc deleted file mode 100644 index cf72d76882..0000000000 --- a/src/lib/dns/rdata/generic/spf_99.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC 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. - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/spf_99.h b/src/lib/dns/rdata/generic/spf_99.h index 7556b4bd72..dfccec2b2c 100644 --- a/src/lib/dns/rdata/generic/spf_99.h +++ b/src/lib/dns/rdata/generic/spf_99.h @@ -33,48 +33,7 @@ #include -class SPF : public TXT_LIKE { - friend class TXT_LIKE; - static string const id; - -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - -}; - -/// explicit SPF(const std::string& type_str); -inline SPF::SPF(const std::string& type_str) : TXT_LIKE(type_str) {} - -/// SPF(isc::util::InputBuffer& buffer, size_t rdata_len); -inline SPF::SPF(isc::util::InputBuffer& buffer, size_t rdata_len) : TXT_LIKE(buffer, rdata_len) {} - -/// SPF(const SPF& other); -inline SPF::SPF(const SPF& other) : TXT_LIKE(other) {} - -/// virtual std::string toText() const; -inline std::string SPF::toText() const -{ - return TXT_LIKE::toText(); -} - -/// virtual void toWire(isc::util::OutputBuffer& buffer) const; -inline void SPF::toWire(isc::util::OutputBuffer& buffer) const -{ - TXT_LIKE::toWire(buffer); -} - -/// virtual void toWire(AbstractMessageRenderer& renderer) const; -inline void SPF::toWire(AbstractMessageRenderer& renderer) const -{ - TXT_LIKE::toWire(renderer); -} - -/// virtual int compare(const Rdata& other) const; -inline int SPF::compare(const Rdata& other) const -{ - return TXT_LIKE::compare(other); -} +typedef TXT_LIKE<99> SPF; // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc deleted file mode 100644 index cf72d76882..0000000000 --- a/src/lib/dns/rdata/generic/txt_16.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC 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. - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/txt_16.h b/src/lib/dns/rdata/generic/txt_16.h index 84ac143b16..7075837e17 100644 --- a/src/lib/dns/rdata/generic/txt_16.h +++ b/src/lib/dns/rdata/generic/txt_16.h @@ -33,48 +33,7 @@ #include -class TXT : public TXT_LIKE { - friend class TXT_LIKE; - static string const id; - -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - -}; - -/// explicit TXT(const std::string& type_str); -inline TXT::TXT(const std::string& type_str) : TXT_LIKE(type_str) {} - -/// TXT(isc::util::InputBuffer& buffer, size_t rdata_len); -inline TXT::TXT(isc::util::InputBuffer& buffer, size_t rdata_len) : TXT_LIKE(buffer, rdata_len) {} - -/// TXT(const TXT& other); -inline TXT::TXT(const TXT& other) : TXT_LIKE(other) {} - -/// virtual std::string toText() const; -inline std::string TXT::toText() const -{ - return TXT_LIKE::toText(); -} - -/// virtual void toWire(isc::util::OutputBuffer& buffer) const; -inline void TXT::toWire(isc::util::OutputBuffer& buffer) const -{ - TXT_LIKE::toWire(buffer); -} - -/// virtual void toWire(AbstractMessageRenderer& renderer) const; -inline void TXT::toWire(AbstractMessageRenderer& renderer) const -{ - TXT_LIKE::toWire(renderer); -} - -/// virtual int compare(const Rdata& other) const; -inline int TXT::compare(const Rdata& other) const -{ - return TXT_LIKE::compare(other); -} +typedef TXT_LIKE<16> TXT; // END_RDATA_NAMESPACE // END_ISC_NAMESPACE From 5951ef6faaffcff62d9a9963260a932666e3decb Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 10 Aug 2011 13:36:01 +0200 Subject: [PATCH 395/974] [1062] logging in database.cc --- src/lib/datasrc/database.cc | 28 +++++++++++++++------- src/lib/datasrc/database.h | 8 ++++--- src/lib/datasrc/datasrc_messages.mes | 35 ++++++++++++++++++++++++++++ src/lib/datasrc/sqlite3_connection.h | 2 +- 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 8f13f525a4..b13f3e9935 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -23,6 +23,7 @@ #include #include +#include #include @@ -95,12 +96,8 @@ void addOrCreate(isc::dns::RRsetPtr& rrset, if (ttl < rrset->getTTL()) { rrset->setTTL(ttl); } - // make sure the type is correct - // TODO Assert? - if (type != rrset->getType()) { - isc_throw(DataSourceError, - "attempt to add multiple types to RRset in find()"); - } + // This is a check to make sure find() is not messing things up + assert(type == rrset->getType()); } try { rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str)); @@ -167,6 +164,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; RRsigStore sig_store; + logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS).arg(name).arg(type); try { connection_->searchForRecords(zone_id_, name.toText()); @@ -193,13 +191,18 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, //cur_sigtype(columns[SIGTYPE_COLUMN]); if (cur_type == type) { + if (result_rrset && + result_rrset->getType() == isc::dns::RRType::CNAME()) { + isc_throw(DataSourceError, "CNAME found but it is not " + "the only record for " + name.toText()); + } addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseConnection::RDATA_COLUMN]); } else if (cur_type == isc::dns::RRType::CNAME()) { // There should be no other data, so result_rrset should be empty. if (result_rrset) { isc_throw(DataSourceError, "CNAME found but it is not " - "the only record for " + name.toText()); + "the only record for " + name.toText()); } addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseConnection::RDATA_COLUMN]); @@ -227,26 +230,35 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } } } catch (const DataSourceError& dse) { + logger.error(DATASRC_DATABASE_FIND_ERROR).arg(dse.what()); // call cleanup and rethrow connection_->resetSearch(); throw; } catch (const isc::Exception& isce) { -// // cleanup, change it to a DataSourceError and rethrow + logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR).arg(isce.what()); + // cleanup, change it to a DataSourceError and rethrow connection_->resetSearch(); isc_throw(DataSourceError, isce.what()); } catch (const std::exception& ex) { + logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR).arg(ex.what()); connection_->resetSearch(); throw; } if (!result_rrset) { if (records_found) { + logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXRRSET) + .arg(name).arg(getClass()).arg(type); result_status = NXRRSET; } else { + logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXDOMAIN) + .arg(name).arg(getClass()).arg(type); result_status = NXDOMAIN; } } else { sig_store.appendSignatures(result_rrset); + logger.debug(DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_RRSET).arg(*result_rrset); } return (FindResult(result_status, result_rrset)); } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 0632f64321..4ad3f498af 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -96,7 +96,7 @@ public: * DatabaseConnection::RecordColumnCount elements, the elements of which * are defined in DatabaseConnection::RecordColumns, in their basic * string representation. - * + * * If you are implementing a derived database connection class, you * should have this method check the column_count value, and fill the * array with strings conforming to their description in RecordColumn. @@ -129,10 +129,12 @@ public: /** * Definitions of the fields as they are required to be filled in * by getNextRecord() - * + * * When implementing getNextRecord(), the columns array should * be filled with the values as described in this enumeration, - * in this order. + * in this order, i.e. TYPE_COLUMN should be the first element + * (index 0) of the array, TTL_COLUMN should be the second element + * (index 1), etc. */ enum RecordColumns { TYPE_COLUMN = 0, ///< The RRType of the record (A/NS/TXT etc.) diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 3fbb24d05d..af704d938e 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -63,6 +63,41 @@ The maximum allowed number of items of the hotspot cache is set to the given number. If there are too many, some of them will be dropped. The size of 0 means no limit. +% DATASRC_DATABASE_FIND_ERROR error retrieving data from database datasource: %1 +The was an internal error while reading data from a datasource. This can either +mean the specific data source implementation is not behaving correctly, or the +data it provides is invalid. The current search is aborted. +The error message contains specific information about the error. + +% DATASRC_DATABASE_FIND_RECORDS looking for record %1/%2 +Debug information. The database data source is looking up records with the given +name and type in the database. + +% DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from database datasource: %1 +There was an uncaught general exception while reading data from a datasource. +This most likely points to a logic error in the code, and can be considered a +bug. The current search is aborted. Specific information about the exception is +printed in this error message. + +% DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR uncaught error retrieving data from database datasource: %1 +There was an uncaught ISC exception while reading data from a datasource. This +most likely points to a logic error in the code, and can be considered a bug. +The current search is aborted. Specific information about the exception is +printed in this error message. + +% DATASRC_DATABASE_FOUND_NXDOMAIN search in database resulted in NXDOMAIN for %1/%2/%3 +The data returned by the database backend did not contain any data for the given +domain name, class and type. + +% DATASRC_DATABASE_FOUND_NXRRSET search in database resulted in NXRRSET for %1/%2/%3 +The data returned by the database backend contained data for the given domain +name and class, but not for the given type. + +% DATASRC_DATABASE_FOUND_RRSET search in database resulted in RRset %1 +The data returned by the database backend contained data for the given domain +name, and it either matches the type or has a relevant type. The RRset that is +returned is printed. + % DATASRC_DO_QUERY handling query for '%1/%2' A debug message indicating that a query for the given name and RR type is being processed. diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index c1968c4a34..d41b814605 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -130,7 +130,7 @@ public: * * This implements the resetSearch from DatabaseConnection. * See the documentation there for more information. - * + * * This function never throws. */ virtual void resetSearch(); From 00686a614cca93f007335d01c06d78cfd212d973 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Thu, 11 Aug 2011 11:00:21 +0800 Subject: [PATCH 396/974] [trac1113] sync with master, fix doxygen and unittest --- src/lib/dns/rdata/generic/minfo_14.h | 10 +- src/lib/dns/tests/rdata_minfo_unittest.cc | 107 +++++++----------- src/lib/dns/tests/testdata/Makefile.am | 3 +- .../dns/tests/testdata/rdata_minfo_fromWire | 55 +++++---- src/lib/util/python/gen_wiredata.py.in | 22 ++++ 5 files changed, 100 insertions(+), 97 deletions(-) diff --git a/src/lib/dns/rdata/generic/minfo_14.h b/src/lib/dns/rdata/generic/minfo_14.h index 0f3f87bb21..f93f619ff0 100644 --- a/src/lib/dns/rdata/generic/minfo_14.h +++ b/src/lib/dns/rdata/generic/minfo_14.h @@ -39,9 +39,8 @@ public: /// \brief Return the value of the rmailbox field. /// - /// This method normally does not throw an exception, but if resource - /// allocation for the returned \c Name object fails, a corresponding - /// standard exception will be thrown. + /// \exception std::bad_alloc If resource allocation for the returned + /// \c Name fails. /// /// \note /// Unlike the case of some other RDATA classes (such as @@ -59,9 +58,8 @@ public: /// \brief Return the value of the emailbox field. /// - /// This method normally does not throw an exception, but if resource - /// allocation for the returned \c Name object fails, a corresponding - /// standard exception will be thrown. + /// \exception std::bad_alloc If resource allocation for the returned + /// \c Name fails. Name getEmailbox() const { return (emailbox_); } private: diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc index 1f6129a46d..b7009689a2 100644 --- a/src/lib/dns/tests/rdata_minfo_unittest.cc +++ b/src/lib/dns/tests/rdata_minfo_unittest.cc @@ -37,107 +37,80 @@ class Rdata_MINFO_Test : public RdataTest { }; // minfo text -string minfo_txt("root.example.com. emailbx.example.com."); -string minfo_txt2("rmailbx.example.com. emailbx.example.com."); -string too_long_label("012345678901234567890123456789" - "0123456789012345678901234567890123"); +const char* const minfo_txt = "rmailbox.example.com. emailbox.example.com."; +const char* const too_long_label = "01234567890123456789012345678901234567" + "89012345678901234567890123"; -// root.example.com. emailbx.example.com. -const uint8_t uncompressed_wiredata_minfo[] = { - 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x07, 0x65, 0x6d, 0x61, - 0x69, 0x6c, 0x62, 0x78, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; -// rmailbx.example.com. emailbx.example.com. -const uint8_t uncompressed_wiredata_minfo2[] = { - 0x07, 0x72, 0x6d, 0x61, 0x69, 0x6c, 0x62, 0x78, 0x07, 0x65, 0x78, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x07, - 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x62, 0x78, 0x07, 0x65, 0x78, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; - -// root.example.com. emailbx.example.com. -const uint8_t compressed_wiredata_minfo[] = { - 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x07, 0x65, 0x6d, 0x61, - 0x69, 0x6c, 0x62, 0x78, 0xc0, 0x05}; -// rmailbx.example.com. emailbx.example.com. -const uint8_t compressed_wiredata_minfo2[] = { - 0x07, 0x72, 0x6d, 0x61, 0x69, 0x6c, 0x62, 0x78, 0x07, 0x65, 0x78, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x07, - 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x62, 0x78, 0xc0, 0x08}; - -const generic::MINFO rdata_minfo(minfo_txt); -const generic::MINFO rdata_minfo2(minfo_txt2); +const generic::MINFO rdata_minfo((string(minfo_txt))); TEST_F(Rdata_MINFO_Test, createFromText) { - EXPECT_EQ(Name("root.example.com."), rdata_minfo.getRmailbox()); - EXPECT_EQ(Name("emailbx.example.com."), rdata_minfo.getEmailbox()); + EXPECT_EQ(Name("rmailbox.example.com."), rdata_minfo.getRmailbox()); + EXPECT_EQ(Name("emailbox.example.com."), rdata_minfo.getEmailbox()); } TEST_F(Rdata_MINFO_Test, badText) { // incomplete text EXPECT_THROW(generic::MINFO("root.example.com."), InvalidRdataText); - // bad name + // number of fields (must be 2) is incorrect + EXPECT_THROW(generic::MINFO("root.example.com emailbox.example.com. " + "example.com."), + InvalidRdataText); + // bad rmailbox name EXPECT_THROW(generic::MINFO("root.example.com. emailbx.example.com." + - too_long_label), + string(too_long_label)), + TooLongLabel); + // bad emailbox name + EXPECT_THROW(generic::MINFO("root.example.com." + + string(too_long_label) + " emailbx.example.com."), TooLongLabel); } TEST_F(Rdata_MINFO_Test, createFromWire) { // compressed emailbx name EXPECT_EQ(0, rdata_minfo.compare( - *rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), + *rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), "rdata_minfo_fromWire"))); // compressed rmailbx and emailbx name EXPECT_EQ(0, rdata_minfo.compare( - *rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), - "rdata_minfo_fromWire", 30))); + *rdataFactoryFromFile(RRType("MINFO"), RRClass::IN(), + "rdata_minfo_fromWire", 35))); // RDLENGTH is too short - EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), - "rdata_minfo_fromWire", 36), + EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire", 41), InvalidRdataLength); // RDLENGTH is too long - EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), - "rdata_minfo_fromWire", 42), + EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire", 47), InvalidRdataLength); // incomplete name. the error should be detected in the name constructor - EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass("IN"), - "rdata_minfo_fromWire", 48), + EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass::IN(), + "rdata_minfo_fromWire", 53), DNSMessageFORMERR); } TEST_F(Rdata_MINFO_Test, toWireBuffer) { + obuffer.skip(2); rdata_minfo.toWire(obuffer); + vector data; + UnitTestUtil::readWireData("rdata_minfo_toWireUncompressed.wire", data); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, - obuffer.getData(), obuffer.getLength(), - uncompressed_wiredata_minfo, - sizeof(uncompressed_wiredata_minfo)); - obuffer.clear(); - rdata_minfo2.toWire(obuffer); - EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, - obuffer.getData(), obuffer.getLength(), - uncompressed_wiredata_minfo2, - sizeof(uncompressed_wiredata_minfo2)); + static_cast(obuffer.getData()) + 2, + obuffer.getLength() - 2, &data[2], data.size() - 2); } TEST_F(Rdata_MINFO_Test, toWireRenderer) { + obuffer.skip(2); rdata_minfo.toWire(renderer); + vector data; + UnitTestUtil::readWireData("rdata_minfo_toWire", data); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, - obuffer.getData(), obuffer.getLength(), - compressed_wiredata_minfo, - sizeof(compressed_wiredata_minfo)); - renderer.clear(); - rdata_minfo2.toWire(renderer); - EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, - obuffer.getData(), obuffer.getLength(), - compressed_wiredata_minfo2, - sizeof(compressed_wiredata_minfo2)); + static_cast(obuffer.getData()) + 2, + obuffer.getLength() - 2, &data[2], data.size() - 2); } TEST_F(Rdata_MINFO_Test, toText) { EXPECT_EQ(minfo_txt, rdata_minfo.toText()); - EXPECT_EQ(minfo_txt2, rdata_minfo2.toText()); } TEST_F(Rdata_MINFO_Test, compare) { @@ -145,18 +118,18 @@ TEST_F(Rdata_MINFO_Test, compare) { EXPECT_EQ(0, rdata_minfo.compare(rdata_minfo)); // names must be compared in case-insensitive manner - EXPECT_EQ(0, rdata_minfo.compare(generic::MINFO("ROOT.example.com. " - "emailbx.EXAMPLE.com."))); + EXPECT_EQ(0, rdata_minfo.compare(generic::MINFO("RMAILBOX.example.com. " + "emailbox.EXAMPLE.com."))); // another MINFO whose rmailbox name is larger than that of rdata_minfo. - const generic::MINFO large1_minfo("zzzz.example.com. " + const generic::MINFO large1_minfo("zzzzzzzz.example.com. " "emailbox.example.com."); EXPECT_GT(0, rdata_minfo.compare(large1_minfo)); EXPECT_LT(0, large1_minfo.compare(rdata_minfo)); // another MINFO whose emailbox name is larger than that of rdata_minfo. - const generic::MINFO large2_minfo("root.example.com. " - "zzzzzzz.example.com."); + const generic::MINFO large2_minfo("rmailbox.example.com. " + "zzzzzzzzzzz.example.com."); EXPECT_GT(0, rdata_minfo.compare(large2_minfo)); EXPECT_LT(0, large2_minfo.compare(rdata_minfo)); diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 79af1f2b33..7e90b2cf9f 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -101,7 +101,8 @@ EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec EXTRA_DIST += rdata_srv_fromWire -EXTRA_DIST += rdata_minfo_fromWire +EXTRA_DIST += rdata_minfo_fromWire rdata_minfo_toWireUncompressed.spec +EXTRA_DIST += rdata_minfo_toWireUncompressed.wire rdata_minfo_toWire EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire index c50f7f6324..d620931c2e 100644 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire @@ -1,38 +1,47 @@ # # various kinds of MINFO RDATA stored in an input buffer -# -# RDLENGHT=28 bytes +# +# Valid compressed RDATA for "(rmailbox.example.com. emailbox.example.com.)" +# RDLENGHT=32 bytes # 0 1 - 00 1c -# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2(bytes) - 04 72 6f 6f 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 65 6d -# 3 4 5 6 7 8 9 - 61 69 6c 62 78 c0 07 + 00 21 +# RMAILBOX: non compressed +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3(bytes) +#(8) r m a i l b o x (7) e x a m p l e (3) c o m . + 08 72 6d 61 69 6c 62 6f 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# EMAILBOX: compressed +# 4 5 6 7 8 9 30 1 2 3 4 +#(8) e m a i l b o x ptr=11 + 08 65 6d 61 69 6c 62 6f 78 c0 0b # -# compressed name +# Both RMAILBOX and EMAILBOX compressed # RDLENGHT=04 bytes -#30 1 +# 5 6 00 04 -# 2 3 4 5(bytes) - c0 02 c0 14 +# 7 8 9 40(bytes) +#ptr=02 ptr=24 + c0 02 c0 18 # -# length too short +# rdlength too short # RDLENGHT=03 bytes -# 6 7 +# 1 2 00 03 -# 8 9 40 1(bytes) - c0 02 c0 14 +# 3 4 5 6(bytes) +#ptr=02 ptr=24 + c0 02 c0 18 # -# length too long +# rdlength too long # RDLENGHT=25 bytes -# 2 3 +# 7 8 00 19 -# 4 5 6 7(bytes) - c0 02 c0 14 +# 9 50 1 2(bytes) +#ptr=02 ptr=24 + c0 02 c0 18 # -# incomplete target name +# incomplete RMAILBOX NAME # RDLENGHT=19 bytes -# 8 9 +# 3 4 00 13 -#50 1 2 3 4 5 6 7 8 9 60 1 2 3 4 5 6 7 8 (bytes) - 04 72 6f 6f 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 +# 5 6 7 8 9 60 1 2 3 4 5 6 7 8 9 60 1 2 3 (bytes) +#(8) r m a i l b o x (7) e x a m p l e (3) c + 08 72 6d 61 69 6c 62 6f 78 07 65 78 61 6d 70 6c 65 03 63 diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in index 8e1f0798bd..e35b37b994 100755 --- a/src/lib/util/python/gen_wiredata.py.in +++ b/src/lib/util/python/gen_wiredata.py.in @@ -822,6 +822,28 @@ class RP(RR): f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text)) f.write('%s %s\n' % (mailbox_wire, text_wire)) +class MINFO(RR): + '''Implements rendering MINFO RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - rmailbox (string): The rmailbox field. + - emailbox (string): The emailbox field. + These strings must be interpreted as a valid domain name. + ''' + rmailbox = 'rmailbox.example.com' + emailbox = 'emailbox.example.com' + def dump(self, f): + rmailbox_wire = encode_name(self.rmailbox) + emailbox_wire = encode_name(self.emailbox) + if self.rdlen is None: + self.rdlen = (len(rmailbox_wire) + len(emailbox_wire)) / 2 + else: + self.rdlen = int(self.rdlen) + self.dump_header(f, self.rdlen) + f.write('# RMAILBOX=%s EMAILBOX=%s\n' % (self.rmailbox, self.emailbox)) + f.write('%s %s\n' % (rmailbox_wire, emailbox_wire)) + class NSECBASE(RR): '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for these RRs. The NSEC and NSEC3 classes will be inherited from this From c293f639684d2c6625b7395c995aa813eafa5fa4 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Thu, 11 Aug 2011 11:03:33 +0800 Subject: [PATCH 397/974] [trac1113] commit minfo wire format testdata --- src/lib/dns/tests/testdata/rdata_minfo_toWire | 15 +++++++++++++++ .../testdata/rdata_minfo_toWireUncompressed.spec | 6 ++++++ .../testdata/rdata_minfo_toWireUncompressed.wire | 8 ++++++++ 3 files changed, 29 insertions(+) create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.wire diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire b/src/lib/dns/tests/testdata/rdata_minfo_toWire new file mode 100644 index 0000000000..c0d09d3968 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWire @@ -0,0 +1,15 @@ +# +# various kinds of MINFO RDATA stored in an input buffer +# +# Valid compressed RDATA for "(rmailbox.example.com. emailbox.example.com.)" +# RDLENGHT=32 bytes +# 0 1 + 00 21 +# RMAILBOX: non compressed +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3(bytes) +#(8) r m a i l b o x (7) e x a m p l e (3) c o m . + 08 72 6d 61 69 6c 62 6f 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# EMAILBOX: compressed +# 4 5 6 7 8 9 30 1 2 3 4 +#(8) e m a i l b o x ptr=11 + 08 65 6d 61 69 6c 62 6f 78 c0 0b diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.spec new file mode 100644 index 0000000000..209b30102a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.spec @@ -0,0 +1,6 @@ +# +# A simplest form of MINFO: all default parameters +# +[custom] +sections: minfo +[minfo] diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.wire new file mode 100644 index 0000000000..bd51833142 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_toWireUncompressed.spec +### + +# MINFO RDATA, RDLEN=44 +002c +# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.example.com +08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78076578616d706c6503636f6d00 From 822a00aee0d7feb845e28dad7dccb552d10d83db Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Thu, 11 Aug 2011 11:25:55 +0800 Subject: [PATCH 398/974] [trac1130] Move naptr rrdata from in_1 to rdata/generic --- .../dns/rdata/{in_1 => generic}/naptr_35.cc | 0 .../dns/rdata/{in_1 => generic}/naptr_35.h | 0 src/lib/dns/tests/Makefile.am | 2 +- src/lib/dns/tests/rdata_in_naptr_unittest.cc | 159 ------------------ 4 files changed, 1 insertion(+), 160 deletions(-) rename src/lib/dns/rdata/{in_1 => generic}/naptr_35.cc (100%) rename src/lib/dns/rdata/{in_1 => generic}/naptr_35.h (100%) delete mode 100644 src/lib/dns/tests/rdata_in_naptr_unittest.cc diff --git a/src/lib/dns/rdata/in_1/naptr_35.cc b/src/lib/dns/rdata/generic/naptr_35.cc similarity index 100% rename from src/lib/dns/rdata/in_1/naptr_35.cc rename to src/lib/dns/rdata/generic/naptr_35.cc diff --git a/src/lib/dns/rdata/in_1/naptr_35.h b/src/lib/dns/rdata/generic/naptr_35.h similarity index 100% rename from src/lib/dns/rdata/in_1/naptr_35.h rename to src/lib/dns/rdata/generic/naptr_35.h diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index dbb0a9e337..e8781237fc 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -42,7 +42,7 @@ run_unittests_SOURCES += rdata_nsec3param_unittest.cc run_unittests_SOURCES += rdata_rrsig_unittest.cc run_unittests_SOURCES += rdata_rp_unittest.cc run_unittests_SOURCES += rdata_tsig_unittest.cc -run_unittests_SOURCES += rdata_in_naptr_unittest.cc +run_unittests_SOURCES += rdata_naptr_unittest.cc run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc run_unittests_SOURCES += question_unittest.cc run_unittests_SOURCES += rrparamregistry_unittest.cc diff --git a/src/lib/dns/tests/rdata_in_naptr_unittest.cc b/src/lib/dns/tests/rdata_in_naptr_unittest.cc deleted file mode 100644 index c63cd2c512..0000000000 --- a/src/lib/dns/tests/rdata_in_naptr_unittest.cc +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC 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. - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -using isc::UnitTestUtil; -using namespace std; -using namespace isc::dns; -using namespace isc::util; -using namespace isc::dns::rdata; -using namespace isc::dns::rdata::in; - -namespace { -class Rdata_IN_NAPTR_Test : public RdataTest { -}; - -// 10 100 "S" "SIP+D2U" "" _sip._udp.example.com. -static uint8_t naptr_rdata[] = {0x00,0x0a,0x00,0x64,0x01,0x53,0x07,0x53,0x49, - 0x50,0x2b,0x44,0x32,0x55,0x00,0x04,0x5f,0x73,0x69,0x70,0x04,0x5f,0x75,0x64, - 0x70,0x07,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x03,0x63,0x6f,0x6d,0x00}; - -static const char *naptr_str = - "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str2 = - "10 100 S SIP+D2U \"\" _sip._udp.example.com."; - -static const char *naptr_str_small1 = - "9 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small2 = - "10 90 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small3 = - "10 100 \"R\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small4 = - "10 100 \"S\" \"SIP+C2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_small5 = - "10 100 \"S\" \"SIP+D2U\" \"\" _rip._udp.example.com."; - -static const char *naptr_str_large1 = - "11 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_large2 = - "10 110 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_large3 = - "10 100 \"T\" \"SIP+D2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_large4 = - "10 100 \"S\" \"SIP+E2U\" \"\" _sip._udp.example.com."; -static const char *naptr_str_large5 = - "10 100 \"S\" \"SIP+D2U\" \"\" _tip._udp.example.com."; - -TEST_F(Rdata_IN_NAPTR_Test, createFromText) { - NAPTR naptr(naptr_str); - EXPECT_EQ(10, naptr.getOrder()); - EXPECT_EQ(100, naptr.getPreference()); - EXPECT_EQ(string("S"), naptr.getFlags()); - EXPECT_EQ(string("SIP+D2U"), naptr.getServices()); - EXPECT_EQ(string(""), naptr.getRegexp()); - EXPECT_EQ(Name("_sip._udp.example.com."), naptr.getReplacement()); - - // Test that separated by space - NAPTR naptr2(naptr_str2); - EXPECT_EQ(string("S"), naptr2.getFlags()); - EXPECT_EQ(string("SIP+D2U"), naptr2.getServices()); -} - -TEST_F(Rdata_IN_NAPTR_Test, badText) { - // Order number cannot exceed 65535 - EXPECT_THROW(const NAPTR naptr("65536 10 S SIP \"\" _sip._udp.example.com."), - InvalidRdataText); - // Preference number cannot exceed 65535 - EXPECT_THROW(const NAPTR naptr("100 65536 S SIP \"\" _sip._udp.example.com."), - InvalidRdataText); - // No regexp given - EXPECT_THROW(const NAPTR naptr("100 10 S SIP _sip._udp.example.com."), - InvalidRdataText); - // The double quotes seperator must match - EXPECT_THROW(const NAPTR naptr("100 10 \"S SIP \"\" _sip._udp.example.com."), - InvalidRdataText); -} - -TEST_F(Rdata_IN_NAPTR_Test, createFromWire) { - InputBuffer input_buffer(naptr_rdata, sizeof(naptr_rdata)); - NAPTR naptr(input_buffer, sizeof(naptr_rdata)); - EXPECT_EQ(10, naptr.getOrder()); - EXPECT_EQ(100, naptr.getPreference()); - EXPECT_EQ(string("S"), naptr.getFlags()); - EXPECT_EQ(string("SIP+D2U"), naptr.getServices()); - EXPECT_EQ(string(""), naptr.getRegexp()); - EXPECT_EQ(Name("_sip._udp.example.com."), naptr.getReplacement()); -} - -TEST_F(Rdata_IN_NAPTR_Test, toWire) { - NAPTR naptr(naptr_str); - naptr.toWire(obuffer); - - EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(), - obuffer.getLength(), naptr_rdata, sizeof(naptr_rdata)); -} - -TEST_F(Rdata_IN_NAPTR_Test, toWireRenderer) { - NAPTR naptr(naptr_str); - - naptr.toWire(renderer); - EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(), - obuffer.getLength(), naptr_rdata, sizeof(naptr_rdata)); -} - -TEST_F(Rdata_IN_NAPTR_Test, toText) { - NAPTR naptr(naptr_str); - EXPECT_EQ(naptr_str, naptr.toText()); -} - -TEST_F(Rdata_IN_NAPTR_Test, compare) { - NAPTR naptr(naptr_str); - NAPTR naptr_small1(naptr_str_small1); - NAPTR naptr_small2(naptr_str_small2); - NAPTR naptr_small3(naptr_str_small3); - NAPTR naptr_small4(naptr_str_small4); - NAPTR naptr_small5(naptr_str_small5); - NAPTR naptr_large1(naptr_str_large1); - NAPTR naptr_large2(naptr_str_large2); - NAPTR naptr_large3(naptr_str_large3); - NAPTR naptr_large4(naptr_str_large4); - NAPTR naptr_large5(naptr_str_large5); - - EXPECT_EQ(0, naptr.compare(NAPTR(naptr_str))); - EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small1))); - EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small2))); - EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small3))); - EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small4))); - EXPECT_EQ(1, naptr.compare(NAPTR(naptr_str_small5))); - EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large1))); - EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large2))); - EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large3))); - EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large4))); - EXPECT_EQ(-1, naptr.compare(NAPTR(naptr_str_large5))); -} - -} From cfd1d9e142fa2fd8b21f74de0e4a0109e0a04439 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Aug 2011 02:07:39 -0700 Subject: [PATCH 399/974] [1062] some minor editorial changes, mostly just folding long lines. --- src/lib/datasrc/database.cc | 29 ++++++++++--------- src/lib/datasrc/sqlite3_connection.cc | 8 +++-- src/lib/datasrc/tests/database_unittest.cc | 2 +- .../tests/sqlite3_connection_unittest.cc | 3 +- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index b13f3e9935..abd979c464 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -179,15 +179,16 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, try { const isc::dns::RRType cur_type(columns[DatabaseConnection::TYPE_COLUMN]); const isc::dns::RRTTL cur_ttl(columns[DatabaseConnection::TTL_COLUMN]); - // Ths sigtype column was an optimization for finding the relevant - // RRSIG RRs for a lookup. Currently this column is not used in this - // revised datasource implementation. We should either start using it - // again, or remove it from use completely (i.e. also remove it from - // the schema and the backend implementation). - // Note that because we don't use it now, we also won't notice it if - // the value is wrong (i.e. if the sigtype column contains an rrtype - // that is different from the actual value of the 'type covered' field - // in the RRSIG Rdata). + // Ths sigtype column was an optimization for finding the + // relevant RRSIG RRs for a lookup. Currently this column is + // not used in this revised datasource implementation. We + // should either start using it again, or remove it from use + // completely (i.e. also remove it from the schema and the + // backend implementation). + // Note that because we don't use it now, we also won't notice + // it if the value is wrong (i.e. if the sigtype column + // contains an rrtype that is different from the actual value + // of the 'type covered' field in the RRSIG Rdata). //cur_sigtype(columns[SIGTYPE_COLUMN]); if (cur_type == type) { @@ -199,7 +200,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseConnection::RDATA_COLUMN]); } else if (cur_type == isc::dns::RRType::CNAME()) { - // There should be no other data, so result_rrset should be empty. + // There should be no other data, so result_rrset should + // be empty. if (result_rrset) { isc_throw(DataSourceError, "CNAME found but it is not " "the only record for " + name.toText()); @@ -211,9 +213,10 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // If we get signatures before we get the actual data, we // can't know which ones to keep and which to drop... // So we keep a separate store of any signature that may be - // relevant and add them to the final RRset when we are done. - // A possible optimization here is to not store them for types - // we are certain we don't need + // relevant and add them to the final RRset when we are + // done. + // A possible optimization here is to not store them for + // types we are certain we don't need sig_store.addSig(isc::dns::rdata::createRdata(cur_type, getClass(), columns[DatabaseConnection::RDATA_COLUMN])); diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index 750a62cf4c..acba0e6227 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -329,6 +329,7 @@ SQLite3Connection::searchForRecords(int zone_id, const std::string& name) { "Error in sqlite3_bind_int() for zone_id " << zone_id << ", sqlite3 result code: " << result); } + // use transient since name is a ref and may disappear result = sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, SQLITE_TRANSIENT); @@ -337,7 +338,7 @@ SQLite3Connection::searchForRecords(int zone_id, const std::string& name) { "Error in sqlite3_bind_text() for name " << name << ", sqlite3 result code: " << result); } -}; +} namespace { // This helper function converts from the unsigned char* type (used by @@ -382,8 +383,9 @@ SQLite3Connection::getNextRecord(std::string columns[], size_t column_count) { resetSearch(); isc_throw(DataSourceError, "Unexpected failure in sqlite3_step (sqlite result code " << rc << ")"); - } catch (std::bad_alloc) { - isc_throw(DataSourceError, "bad_alloc in Sqlite3Connection::getNextRecord"); + } catch (const std::bad_alloc&) { + isc_throw(DataSourceError, + "bad_alloc in Sqlite3Connection::getNextRecord"); } // Compilers might not realize isc_throw always throws return (false); diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 69678f0047..8cfbb08402 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -51,7 +51,7 @@ public: search_running_ = true; // 'hardcoded' name to trigger exceptions (for testing - // the error handling of find() (the other on is below in + // the error handling of find() (the other on is below in // if the name is "exceptiononsearch" it'll raise an exception here if (name == "dsexception.in.search.") { isc_throw(DataSourceError, "datasource exception on search"); diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 7f7032238d..8fdbf9f10b 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -127,8 +127,7 @@ TEST_F(SQLite3Conn, getRecords) { std::string columns[column_count]; // without search, getNext() should return false - EXPECT_FALSE(conn->getNextRecord(columns, - column_count)); + EXPECT_FALSE(conn->getNextRecord(columns, column_count)); checkRecordRow(columns, "", "", "", ""); conn->searchForRecords(zone_id, "foo.bar."); From 12b3473393fb7a471fc7d928476b0ba66da145e9 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 11 Aug 2011 11:39:28 +0200 Subject: [PATCH 400/974] [1062] unconst find() --- src/lib/datasrc/database.cc | 2 +- src/lib/datasrc/database.h | 3 +-- src/lib/datasrc/memory_datasrc.cc | 2 +- src/lib/datasrc/memory_datasrc.h | 2 +- src/lib/datasrc/zone.h | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index abd979c464..6a5f2d3f54 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -156,7 +156,7 @@ ZoneFinder::FindResult DatabaseClient::Finder::find(const isc::dns::Name& name, const isc::dns::RRType& type, isc::dns::RRsetList*, - const FindOptions) const + const FindOptions) { // This variable is used to determine the difference between // NXDOMAIN and NXRRSET diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 4ad3f498af..000c813d7b 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -236,8 +236,7 @@ public: virtual FindResult find(const isc::dns::Name& name, const isc::dns::RRType& type, isc::dns::RRsetList* target = NULL, - const FindOptions options = FIND_DEFAULT) - const; + const FindOptions options = FIND_DEFAULT); /** * \brief The zone ID diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 26223dad90..d06cd9ba43 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -618,7 +618,7 @@ InMemoryZoneFinder::getClass() const { ZoneFinder::FindResult InMemoryZoneFinder::find(const Name& name, const RRType& type, - RRsetList* target, const FindOptions options) const + RRsetList* target, const FindOptions options) { return (impl_->find(name, type, target, options)); } diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 9707797299..0234a916f8 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -73,7 +73,7 @@ public: virtual FindResult find(const isc::dns::Name& name, const isc::dns::RRType& type, isc::dns::RRsetList* target = NULL, - const FindOptions options = FIND_DEFAULT) const; + const FindOptions options = FIND_DEFAULT); /// \brief Inserts an rrset into the zone. /// diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index f67ed4be24..0dacc5da55 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -197,7 +197,7 @@ public: const isc::dns::RRType& type, isc::dns::RRsetList* target = NULL, const FindOptions options - = FIND_DEFAULT) const = 0; + = FIND_DEFAULT) = 0; //@} }; From b19a36e30d0d3829c68f2e0300ea1487da242af8 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 11 Aug 2011 13:09:12 +0200 Subject: [PATCH 401/974] [1062] added getDBName to DatabaseConnection and add database name to logging output --- src/lib/datasrc/database.cc | 12 ++++++++---- src/lib/datasrc/database.h | 12 ++++++++++++ src/lib/datasrc/datasrc_messages.mes | 8 ++++---- src/lib/datasrc/sqlite3_connection.cc | 5 ++++- src/lib/datasrc/sqlite3_connection.h | 3 +++ src/lib/datasrc/tests/database_unittest.cc | 13 ++++++++++++- .../datasrc/tests/sqlite3_connection_unittest.cc | 12 ++++++++++++ src/lib/util/filename.h | 5 +++++ src/lib/util/tests/filename_unittest.cc | 15 +++++++++++++++ 9 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 6a5f2d3f54..eecaa123e5 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -164,7 +164,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; RRsigStore sig_store; - logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS).arg(name).arg(type); + logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS) + .arg(connection_->getDBName()).arg(name).arg(type); try { connection_->searchForRecords(zone_id_, name.toText()); @@ -233,17 +234,20 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } } } catch (const DataSourceError& dse) { - logger.error(DATASRC_DATABASE_FIND_ERROR).arg(dse.what()); + logger.error(DATASRC_DATABASE_FIND_ERROR) + .arg(connection_->getDBName()).arg(dse.what()); // call cleanup and rethrow connection_->resetSearch(); throw; } catch (const isc::Exception& isce) { - logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR).arg(isce.what()); + logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR) + .arg(connection_->getDBName()).arg(isce.what()); // cleanup, change it to a DataSourceError and rethrow connection_->resetSearch(); isc_throw(DataSourceError, isce.what()); } catch (const std::exception& ex) { - logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR).arg(ex.what()); + logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR) + .arg(connection_->getDBName()).arg(ex.what()); connection_->resetSearch(); throw; } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 000c813d7b..e0ff3d5910 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -147,6 +147,17 @@ public: /// The number of fields the columns array passed to getNextRecord should have static const size_t RecordColumnCount = 4; + + /** + * \brief Returns a string identifying this dabase backend + * + * Any implementation is free to choose the exact string content, + * but it is advisable to make it a name that is distinguishable + * from the others. + * + * \return the name of the dabase + */ + virtual const std::string& getDBName() const = 0; }; /** @@ -273,6 +284,7 @@ public: * returned, though. */ virtual FindResult findZone(const isc::dns::Name& name) const; + private: /// \brief Our connection. const boost::shared_ptr connection_; diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index af704d938e..12a5050c10 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -63,23 +63,23 @@ The maximum allowed number of items of the hotspot cache is set to the given number. If there are too many, some of them will be dropped. The size of 0 means no limit. -% DATASRC_DATABASE_FIND_ERROR error retrieving data from database datasource: %1 +% DATASRC_DATABASE_FIND_ERROR error retrieving data from datasource %1: %2 The was an internal error while reading data from a datasource. This can either mean the specific data source implementation is not behaving correctly, or the data it provides is invalid. The current search is aborted. The error message contains specific information about the error. -% DATASRC_DATABASE_FIND_RECORDS looking for record %1/%2 +% DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3 Debug information. The database data source is looking up records with the given name and type in the database. -% DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from database datasource: %1 +% DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from datasource %1: %2 There was an uncaught general exception while reading data from a datasource. This most likely points to a logic error in the code, and can be considered a bug. The current search is aborted. Specific information about the exception is printed in this error message. -% DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR uncaught error retrieving data from database datasource: %1 +% DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR uncaught error retrieving data from datasource %1: %2 There was an uncaught ISC exception while reading data from a datasource. This most likely points to a logic error in the code, and can be considered a bug. The current search is aborted. Specific information about the exception is diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index acba0e6227..af133a4220 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -17,6 +17,7 @@ #include #include #include +#include namespace isc { namespace datasrc { @@ -48,7 +49,9 @@ struct SQLite3Parameters { SQLite3Connection::SQLite3Connection(const std::string& filename, const isc::dns::RRClass& rrclass) : dbparameters_(new SQLite3Parameters), - class_(rrclass.toText()) + class_(rrclass.toText()), + database_name_("sqlite3_" + + isc::util::Filename(filename).nameAndExtension()) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN); diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h index d41b814605..8c38b8ab4a 100644 --- a/src/lib/datasrc/sqlite3_connection.h +++ b/src/lib/datasrc/sqlite3_connection.h @@ -135,6 +135,8 @@ public: */ virtual void resetSearch(); + virtual const std::string& getDBName() const { return database_name_; } + private: /// \brief Private database data SQLite3Parameters* dbparameters_; @@ -144,6 +146,7 @@ private: void open(const std::string& filename); /// \brief Closes the database void close(); + const std::string database_name_; }; } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 8cfbb08402..5f11d7289c 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -37,7 +37,11 @@ namespace { */ class MockConnection : public DatabaseConnection { public: - MockConnection() : search_running_(false) { fillData(); } + MockConnection() : search_running_(false), + database_name_("mock_database") + { + fillData(); + } virtual std::pair getZone(const Name& name) const { if (name == Name("example.org")) { @@ -108,6 +112,9 @@ public: return (search_running_); } + virtual const std::string& getDBName() const { + return database_name_; + } private: std::map > > records; // used as internal index for getNextRecord() @@ -125,6 +132,8 @@ private: // hardcode some exceptions into getNextRecord std::string searched_name_; + const std::string database_name_; + // Adds one record to the current name in the database // The actual data will not be added to 'records' until // addCurName() is called @@ -271,6 +280,8 @@ public: // Will be deleted by client_, just keep the current value for comparison. MockConnection* current_connection_; shared_ptr client_; + const std::string database_name_; + /** * Check the zone finder is a valid one and references the zone ID and * connection available here. diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 8fdbf9f10b..2a1d471258 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -30,7 +30,9 @@ namespace { // Some test data std::string SQLITE_DBFILE_EXAMPLE = TEST_DATA_DIR "/test.sqlite3"; std::string SQLITE_DBFILE_EXAMPLE2 = TEST_DATA_DIR "/example2.com.sqlite3"; +std::string SQLITE_DBNAME_EXAMPLE2 = "sqlite3_example2.com.sqlite3"; std::string SQLITE_DBFILE_EXAMPLE_ROOT = TEST_DATA_DIR "/test-root.sqlite3"; +std::string SQLITE_DBNAME_EXAMPLE_ROOT = "sqlite3_test-root.sqlite3"; std::string SQLITE_DBFILE_BROKENDB = TEST_DATA_DIR "/brokendb.sqlite3"; std::string SQLITE_DBFILE_MEMORY = ":memory:"; @@ -101,6 +103,16 @@ TEST_F(SQLite3Conn, noClass) { EXPECT_FALSE(conn->getZone(Name("example.com")).first); } +TEST(SQLite3Open, getDBNameExample2) { + SQLite3Connection conn(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); + EXPECT_EQ(SQLITE_DBNAME_EXAMPLE2, conn.getDBName()); +} + +TEST(SQLite3Open, getDBNameExampleROOT) { + SQLite3Connection conn(SQLITE_DBFILE_EXAMPLE_ROOT, RRClass::IN()); + EXPECT_EQ(SQLITE_DBNAME_EXAMPLE_ROOT, conn.getDBName()); +} + // Simple function to cound the number of records for // any name void diff --git a/src/lib/util/filename.h b/src/lib/util/filename.h index c9874ce220..f6259386ef 100644 --- a/src/lib/util/filename.h +++ b/src/lib/util/filename.h @@ -103,6 +103,11 @@ public: return (extension_); } + /// \return Name + extension of Given File Name + std::string nameAndExtension() const { + return (name_ + extension_); + } + /// \brief Expand Name with Default /// /// A default file specified is supplied and used to fill in any missing diff --git a/src/lib/util/tests/filename_unittest.cc b/src/lib/util/tests/filename_unittest.cc index be29ff18ea..b17e374a51 100644 --- a/src/lib/util/tests/filename_unittest.cc +++ b/src/lib/util/tests/filename_unittest.cc @@ -51,42 +51,49 @@ TEST_F(FilenameTest, Components) { EXPECT_EQ("/alpha/beta/", fname.directory()); EXPECT_EQ("gamma", fname.name()); EXPECT_EQ(".delta", fname.extension()); + EXPECT_EQ("gamma.delta", fname.nameAndExtension()); // Directory only fname.setName("/gamma/delta/"); EXPECT_EQ("/gamma/delta/", fname.directory()); EXPECT_EQ("", fname.name()); EXPECT_EQ("", fname.extension()); + EXPECT_EQ("", fname.nameAndExtension()); // Filename only fname.setName("epsilon"); EXPECT_EQ("", fname.directory()); EXPECT_EQ("epsilon", fname.name()); EXPECT_EQ("", fname.extension()); + EXPECT_EQ("epsilon", fname.nameAndExtension()); // Extension only fname.setName(".zeta"); EXPECT_EQ("", fname.directory()); EXPECT_EQ("", fname.name()); EXPECT_EQ(".zeta", fname.extension()); + EXPECT_EQ(".zeta", fname.nameAndExtension()); // Missing directory fname.setName("eta.theta"); EXPECT_EQ("", fname.directory()); EXPECT_EQ("eta", fname.name()); EXPECT_EQ(".theta", fname.extension()); + EXPECT_EQ("eta.theta", fname.nameAndExtension()); // Missing filename fname.setName("/iota/.kappa"); EXPECT_EQ("/iota/", fname.directory()); EXPECT_EQ("", fname.name()); EXPECT_EQ(".kappa", fname.extension()); + EXPECT_EQ(".kappa", fname.nameAndExtension()); // Missing extension fname.setName("lambda/mu/nu"); EXPECT_EQ("lambda/mu/", fname.directory()); EXPECT_EQ("nu", fname.name()); EXPECT_EQ("", fname.extension()); + EXPECT_EQ("nu", fname.nameAndExtension()); // Check that the decomposition can occur in the presence of leading and // trailing spaces @@ -94,18 +101,21 @@ TEST_F(FilenameTest, Components) { EXPECT_EQ("lambda/mu/", fname.directory()); EXPECT_EQ("nu", fname.name()); EXPECT_EQ("", fname.extension()); + EXPECT_EQ("nu", fname.nameAndExtension()); // Empty string fname.setName(""); EXPECT_EQ("", fname.directory()); EXPECT_EQ("", fname.name()); EXPECT_EQ("", fname.extension()); + EXPECT_EQ("", fname.nameAndExtension()); // ... and just spaces fname.setName(" "); EXPECT_EQ("", fname.directory()); EXPECT_EQ("", fname.name()); EXPECT_EQ("", fname.extension()); + EXPECT_EQ("", fname.nameAndExtension()); // Check corner cases - where separators are present, but strings are // absent. @@ -113,16 +123,19 @@ TEST_F(FilenameTest, Components) { EXPECT_EQ("/", fname.directory()); EXPECT_EQ("", fname.name()); EXPECT_EQ("", fname.extension()); + EXPECT_EQ("", fname.nameAndExtension()); fname.setName("."); EXPECT_EQ("", fname.directory()); EXPECT_EQ("", fname.name()); EXPECT_EQ(".", fname.extension()); + EXPECT_EQ(".", fname.nameAndExtension()); fname.setName("/."); EXPECT_EQ("/", fname.directory()); EXPECT_EQ("", fname.name()); EXPECT_EQ(".", fname.extension()); + EXPECT_EQ(".", fname.nameAndExtension()); // Note that the space is a valid filename here; only leading and trailing // spaces should be trimmed. @@ -130,11 +143,13 @@ TEST_F(FilenameTest, Components) { EXPECT_EQ("/", fname.directory()); EXPECT_EQ(" ", fname.name()); EXPECT_EQ(".", fname.extension()); + EXPECT_EQ(".", fname.nameAndExtension()); fname.setName(" / . "); EXPECT_EQ("/", fname.directory()); EXPECT_EQ(" ", fname.name()); EXPECT_EQ(".", fname.extension()); + EXPECT_EQ(".", fname.nameAndExtension()); } // Check that the expansion with a default works. From eb8ba927115b091bb407cbc29ad2d07dfed318f1 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 11 Aug 2011 07:25:37 -0500 Subject: [PATCH 402/974] [master] point to Year3Goals wikipage instead of Year2Milestones --- README | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README b/README index a6509da2d2..a9e05f1de9 100644 --- a/README +++ b/README @@ -8,10 +8,10 @@ for serving, maintaining, and developing DNS. BIND10-devel is new development leading up to the production BIND 10 release. It contains prototype code and experimental interfaces. Nevertheless it is ready to use now for testing the -new BIND 10 infrastructure ideas. The Year 2 milestones of the -five year plan are described here: +new BIND 10 infrastructure ideas. The Year 3 goals of the five +year plan are described here: - https://bind10.isc.org/wiki/Year2Milestones + http://bind10.isc.org/wiki/Year3Goals This release includes the bind10 master process, b10-msgq message bus, b10-auth authoritative DNS server (with SQLite3 and in-memory From f03688da19c21b4d46761cc4ed9da981cebe43c1 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 11 Aug 2011 07:48:21 -0500 Subject: [PATCH 403/974] [master] document about setproctitle --- doc/guide/bind10-guide.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 6a4218207a..020fbbc6b7 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -742,6 +742,16 @@ Debian and Ubuntu: get additional debugging or diagnostic output. + + + + If the setproctitle Python module is detected at start up, + the process names for the Python-based daemons will be renamed + to better identify them instead of just python. + This is not needed on some operating systems. + + +
From 0081ce40b832f4c5abaeb0316736d772aec3f08d Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 11 Aug 2011 08:34:37 -0500 Subject: [PATCH 404/974] [master] fix output for CC_ESTABLISH socket file discussed via jabber --- src/lib/cc/session.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/cc/session.cc b/src/lib/cc/session.cc index 97d5cf14d0..e0e24cf922 100644 --- a/src/lib/cc/session.cc +++ b/src/lib/cc/session.cc @@ -119,7 +119,7 @@ private: void SessionImpl::establish(const char& socket_file) { try { - LOG_DEBUG(logger, DBG_TRACE_BASIC, CC_ESTABLISH).arg(socket_file); + LOG_DEBUG(logger, DBG_TRACE_BASIC, CC_ESTABLISH).arg(&socket_file); socket_.connect(asio::local::stream_protocol::endpoint(&socket_file), error_); LOG_DEBUG(logger, DBG_TRACE_BASIC, CC_ESTABLISHED); From d00042b03e1f85cd1d8ea8340d5ac72222e5123e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 11 Aug 2011 16:18:43 +0200 Subject: [PATCH 405/974] [1061] Rename missing names Some "Conn" variables were left out and forgotten in previous renames, fixing what could be found. --- src/lib/datasrc/datasrc_messages.mes | 4 ++-- src/lib/datasrc/tests/database_unittest.cc | 2 +- .../datasrc/tests/sqlite3_accessor_unittest.cc | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 3fbb24d05d..1a911c0adc 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -413,7 +413,7 @@ Debug information. An instance of SQLite data source is being created. % DATASRC_SQLITE_DESTROY SQLite data source destroyed Debug information. An instance of SQLite data source is being destroyed. -% DATASRC_SQLITE_DROPCONN SQLite3Connection is being deinitialized +% DATASRC_SQLITE_DROPCONN SQLite3Database is being deinitialized The object around a database connection is being destroyed. % DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1' @@ -468,7 +468,7 @@ source. The SQLite data source was asked to provide a NSEC3 record for given zone. But it doesn't contain that zone. -% DATASRC_SQLITE_NEWCONN SQLite3Connection is being initialized +% DATASRC_SQLITE_NEWCONN SQLite3Database is being initialized A wrapper object to hold database connection is being initialized. % DATASRC_SQLITE_OPEN opening SQLite database '%1' diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index ab4423ec53..4144a5bf15 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -91,7 +91,7 @@ TEST_F(DatabaseClientTest, superZone) { checkZoneFinder(zone); } -TEST_F(DatabaseClientTest, noConnException) { +TEST_F(DatabaseClientTest, noAccessorException) { EXPECT_THROW(DatabaseClient(shared_ptr()), isc::InvalidParameter); } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 101c02b420..409201d8e8 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -64,13 +64,13 @@ TEST(SQLite3Open, memoryDB) { } // Test fixture for querying the db -class SQLite3Conn : public ::testing::Test { +class SQLite3Access : public ::testing::Test { public: - SQLite3Conn() { - initConn(SQLITE_DBFILE_EXAMPLE, RRClass::IN()); + SQLite3Access() { + initAccessor(SQLITE_DBFILE_EXAMPLE, RRClass::IN()); } // So it can be re-created with different data - void initConn(const std::string& filename, const RRClass& rrclass) { + void initAccessor(const std::string& filename, const RRClass& rrclass) { db.reset(new SQLite3Database(filename, rrclass)); } // The tested db @@ -78,25 +78,25 @@ public: }; // This zone exists in the data, so it should be found -TEST_F(SQLite3Conn, getZone) { +TEST_F(SQLite3Access, getZone) { std::pair result(db->getZone(Name("example.com"))); EXPECT_TRUE(result.first); EXPECT_EQ(1, result.second); } // But it should find only the zone, nothing below it -TEST_F(SQLite3Conn, subZone) { +TEST_F(SQLite3Access, subZone) { EXPECT_FALSE(db->getZone(Name("sub.example.com")).first); } // This zone is not there at all -TEST_F(SQLite3Conn, noZone) { +TEST_F(SQLite3Access, noZone) { EXPECT_FALSE(db->getZone(Name("example.org")).first); } // This zone is there, but in different class -TEST_F(SQLite3Conn, noClass) { - initConn(SQLITE_DBFILE_EXAMPLE, RRClass::CH()); +TEST_F(SQLite3Access, noClass) { + initAccessor(SQLITE_DBFILE_EXAMPLE, RRClass::CH()); EXPECT_FALSE(db->getZone(Name("example.com")).first); } From 0af72968bfd192fa418551ae75def455adcfbb4b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 11 Aug 2011 17:19:44 +0200 Subject: [PATCH 406/974] [801] Some more notes about API --- src/bin/bind10/creatorapi.txt | 41 ++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/bin/bind10/creatorapi.txt b/src/bin/bind10/creatorapi.txt index 6100f39a1d..fd6be31a2d 100644 --- a/src/bin/bind10/creatorapi.txt +++ b/src/bin/bind10/creatorapi.txt @@ -33,6 +33,19 @@ token over the connection (so Boss will know which socket to send there, in case multiple applications ask for sockets simultaneously) and Boss sends the socket in return. +In theory, we could send the requests directly over the unix-domain +socket, but it has two disadvantages: +* The msgq handles serializing/deserializing of structured + information (like the parameters to be used), we would have to do it + manually on the socket. +* We could place some kind of security in front of msgq (in case file + permissions are not enough, for example if they are not honored on + socket files, as indicated in the first paragraph of: + http://lkml.indiana.edu/hypermail/linux/kernel/0505.2/0008.html). + The socket would have to be secured separately. With the tokens, + there's some level of security already - someone not having the + token can't request a priviledged socket. + Caching of sockets ------------------ To allow sending the same socket to multiple application, the Boss process will @@ -64,7 +77,10 @@ The commands * Command to release a socket. This one would have single parameter, the token used to get the socket. After this, boss would decrease its reference count and if it drops to zero, close its own copy of the socket. This should be used - when the module stops using the socket (and after closes it). + when the module stops using the socket (and after closes it). The + library could remember the file-descriptor to token mapping (for + common applications that don't request the same socket multiple + times in parallel). * Command to request a socket. It would have parameters to specify which socket (IP address, address family, port) and how to allow sharing. Sharing would be one of: @@ -78,3 +94,26 @@ The commands It would return either error (the socket can't be created or sharing is not possible) or the token. Then there would be some time for the application to pick up the requested socket. + +Examples +-------- +We probably would have a library with blocking calls to request the +sockets, so a code could look like: + +(socket_fd, token) = request_socket(address, port, 'UDP', SHARE_SAMENAME, 'test-application') +sock = socket.fromfd(socket_fd) + +# Some sock.send and sock.recv stuff here + +sock.close() +release_socket(socket_fd) # or release_socket(token) + +Known limitations +----------------- +Currently the socket creator doesn't support specifying any socket +options. If it turns out there are any options that need to be set +before bind(), we'll need to extend it (and extend the protocol as +well). + +The current socket creator doesn't know raw sockets, but if they are +needed, it should be easy to add. From ac15a86eb62832cc22533bc33b802ea297666ad5 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 11 Aug 2011 17:31:06 +0200 Subject: [PATCH 407/974] [1062] rest of the review comments --- src/lib/datasrc/database.cc | 14 +- src/lib/datasrc/database.h | 4 +- src/lib/datasrc/datasrc_messages.mes | 5 + src/lib/datasrc/sqlite3_connection.cc | 87 ++++---- src/lib/datasrc/tests/database_unittest.cc | 195 +++++++++++++++--- .../tests/sqlite3_connection_unittest.cc | 3 +- 6 files changed, 229 insertions(+), 79 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index eecaa123e5..7bbf28512b 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -93,11 +93,15 @@ void addOrCreate(isc::dns::RRsetPtr& rrset, if (!rrset) { rrset.reset(new isc::dns::RRset(name, cls, type, ttl)); } else { - if (ttl < rrset->getTTL()) { - rrset->setTTL(ttl); - } // This is a check to make sure find() is not messing things up assert(type == rrset->getType()); + if (ttl != rrset->getTTL()) { + if (ttl < rrset->getTTL()) { + rrset->setTTL(ttl); + } + logger.info(DATASRC_DATABASE_FIND_TTL_MISMATCH) + .arg(name).arg(cls).arg(type).arg(rrset->getTTL()); + } } try { rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str)); @@ -170,9 +174,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, try { connection_->searchForRecords(zone_id_, name.toText()); - std::string columns[DatabaseConnection::RecordColumnCount]; + std::string columns[DatabaseConnection::RECORDCOLUMNCOUNT]; while (connection_->getNextRecord(columns, - DatabaseConnection::RecordColumnCount)) { + DatabaseConnection::RECORDCOLUMNCOUNT)) { if (!records_found) { records_found = true; } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index e0ff3d5910..4a28b7c64b 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -93,7 +93,7 @@ public: * In the case of a database error, a DatasourceError is thrown. * * The columns passed is an array of std::strings consisting of - * DatabaseConnection::RecordColumnCount elements, the elements of which + * DatabaseConnection::RECORDCOLUMNCOUNT elements, the elements of which * are defined in DatabaseConnection::RecordColumns, in their basic * string representation. * @@ -146,7 +146,7 @@ public: }; /// The number of fields the columns array passed to getNextRecord should have - static const size_t RecordColumnCount = 4; + static const size_t RECORDCOLUMNCOUNT = 4; /** * \brief Returns a string identifying this dabase backend diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 12a5050c10..5a63b0dea6 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -73,6 +73,11 @@ The error message contains specific information about the error. Debug information. The database data source is looking up records with the given name and type in the database. +% DATASRC_DATABASE_FIND_TTL_MISMATCH TTL values differ for elements of %1/%2/%3, setting to %4 +The datasource backend provided resource records for the given RRset with +different TTL values. The TTL of the RRSET is set to the lowest value, which +is printed in the log message. + % DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from datasource %1: %2 There was an uncaught general exception while reading data from a datasource. This most likely points to a logic error in the code, and can be considered a diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc index af133a4220..4fd48006f7 100644 --- a/src/lib/datasrc/sqlite3_connection.cc +++ b/src/lib/datasrc/sqlite3_connection.cc @@ -325,21 +325,17 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const { void SQLite3Connection::searchForRecords(int zone_id, const std::string& name) { resetSearch(); - int result; - result = sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id); - if (result != SQLITE_OK) { + if (sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id) != SQLITE_OK) { isc_throw(DataSourceError, "Error in sqlite3_bind_int() for zone_id " << - zone_id << ", sqlite3 result code: " << result); + zone_id << ": " << sqlite3_errmsg(dbparameters_->db_)); } - // use transient since name is a ref and may disappear - result = sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, - SQLITE_TRANSIENT); - if (result != SQLITE_OK) { + if (sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, + SQLITE_TRANSIENT) != SQLITE_OK) { isc_throw(DataSourceError, "Error in sqlite3_bind_text() for name " << - name << ", sqlite3 result code: " << result); + name << ": " << sqlite3_errmsg(dbparameters_->db_)); } } @@ -349,10 +345,23 @@ namespace { // might not be directly convertable // In case sqlite3_column_text() returns NULL, we just make it an // empty string. +// The sqlite3parameters value is only used to check the error code if +// ucp == NULL const char* -convertToPlainChar(const unsigned char* ucp) { +convertToPlainChar(const unsigned char* ucp, + SQLite3Parameters* dbparameters) { if (ucp == NULL) { - return (""); + // The field can really be NULL, in which case we return an + // empty string, or sqlite may have run out of memory, in + // which case we raise an error + if (dbparameters && + sqlite3_errcode(dbparameters->db_) == SQLITE_NOMEM) { + isc_throw(DataSourceError, + "Sqlite3 backend encountered a memory allocation " + "error in sqlite3_column_text()"); + } else { + return (""); + } } const void* p = ucp; return (static_cast(p)); @@ -361,35 +370,35 @@ convertToPlainChar(const unsigned char* ucp) { bool SQLite3Connection::getNextRecord(std::string columns[], size_t column_count) { - try { - sqlite3_stmt* current_stmt = dbparameters_->q_any_; - const int rc = sqlite3_step(current_stmt); - - if (column_count != RecordColumnCount) { - isc_throw(DataSourceError, - "Datasource backend caller did not pass a column array " - "of size " << RecordColumnCount << - " to getNextRecord()"); - } - - if (rc == SQLITE_ROW) { - for (int column = 0; column < column_count; ++column) { - columns[column] = convertToPlainChar(sqlite3_column_text( - current_stmt, column)); - } - return (true); - } else if (rc == SQLITE_DONE) { - // reached the end of matching rows - resetSearch(); - return (false); - } - resetSearch(); - isc_throw(DataSourceError, - "Unexpected failure in sqlite3_step (sqlite result code " << rc << ")"); - } catch (const std::bad_alloc&) { - isc_throw(DataSourceError, - "bad_alloc in Sqlite3Connection::getNextRecord"); + if (column_count != RECORDCOLUMNCOUNT) { + isc_throw(DataSourceError, + "Datasource backend caller did not pass a column array " + "of size " << RECORDCOLUMNCOUNT << + " to getNextRecord()"); } + + sqlite3_stmt* current_stmt = dbparameters_->q_any_; + const int rc = sqlite3_step(current_stmt); + + if (rc == SQLITE_ROW) { + for (int column = 0; column < column_count; ++column) { + try { + columns[column] = convertToPlainChar(sqlite3_column_text( + current_stmt, column), + dbparameters_); + } catch (const std::bad_alloc&) { + isc_throw(DataSourceError, + "bad_alloc in Sqlite3Connection::getNextRecord"); + } + } + return (true); + } else if (rc == SQLITE_DONE) { + // reached the end of matching rows + resetSearch(); + return (false); + } + isc_throw(DataSourceError, "Unexpected failure in sqlite3_step: " << + sqlite3_errmsg(dbparameters_->db_)); // Compilers might not realize isc_throw always throws return (false); } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 5f11d7289c..609ab6ac3b 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -16,12 +16,15 @@ #include #include +#include #include #include #include #include +#include + #include using namespace isc::datasrc; @@ -89,7 +92,7 @@ public: throw std::exception(); } - if (column_count != DatabaseConnection::RecordColumnCount) { + if (column_count != DatabaseConnection::RECORDCOLUMNCOUNT) { isc_throw(DataSourceError, "Wrong column count in getNextRecord"); } if (cur_record < cur_name.size()) { @@ -328,18 +331,31 @@ doFindTest(shared_ptr finder, const isc::dns::RRType& expected_type, const isc::dns::RRTTL expected_ttl, ZoneFinder::Result expected_result, - unsigned int expected_rdata_count, - unsigned int expected_signature_count) + const std::vector& expected_rdatas, + const std::vector& expected_sig_rdatas) { ZoneFinder::FindResult result = finder->find(name, type, NULL, ZoneFinder::FIND_DEFAULT); ASSERT_EQ(expected_result, result.code) << name << " " << type; - if (expected_rdata_count > 0) { - EXPECT_EQ(expected_rdata_count, result.rrset->getRdataCount()); + if (expected_rdatas.size() > 0) { + EXPECT_EQ(expected_rdatas.size(), result.rrset->getRdataCount()); EXPECT_EQ(expected_ttl, result.rrset->getTTL()); EXPECT_EQ(expected_type, result.rrset->getType()); - if (expected_signature_count > 0) { - EXPECT_EQ(expected_signature_count, + + isc::dns::RRsetPtr expected_rrset( + new isc::dns::RRset(name, finder->getClass(), + expected_type, expected_ttl)); + for (unsigned int i = 0; i < expected_rdatas.size(); ++i) { + expected_rrset->addRdata( + isc::dns::rdata::createRdata(expected_type, + finder->getClass(), + expected_rdatas[i])); + } + isc::testutils::rrsetCheck(expected_rrset, result.rrset); + + if (expected_sig_rdatas.size() > 0) { + // TODO same for sigrrset + EXPECT_EQ(expected_sig_rdatas.size(), result.rrset->getRRsig()->getRdataCount()); } else { EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig()); @@ -357,110 +373,224 @@ TEST_F(DatabaseClientTest, find) { dynamic_pointer_cast(zone.zone_finder)); EXPECT_EQ(42, finder->zone_id()); EXPECT_FALSE(current_connection_->searchRunning()); + std::vector expected_rdatas; + std::vector expected_sig_rdatas; + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 1, 0); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_rdatas.push_back("192.0.2.2"); doFindTest(finder, isc::dns::Name("www2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 2, 0); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("2001:db8::1"); + expected_rdatas.push_back("2001:db8::2"); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 2, 0); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); + EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, 0, 0); + ZoneFinder::NXRRSET, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("www.example.org."); doFindTest(finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), - ZoneFinder::CNAME, 1, 0); + ZoneFinder::CNAME, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("www.example.org."); doFindTest(finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 1, 0); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); doFindTest(finder, isc::dns::Name("doesnotexist.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::NXDOMAIN, 0, 0); + ZoneFinder::NXDOMAIN, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 1, 2); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("2001:db8::1"); + expected_rdatas.push_back("2001:db8::2"); + expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 2, 1); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, 0, 0); + ZoneFinder::NXRRSET, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("www.example.org."); + expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signedcname1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), - ZoneFinder::CNAME, 1, 1); + ZoneFinder::CNAME, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 1, 2); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("2001:db8::2"); + expected_rdatas.push_back("2001:db8::1"); + expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 2, 1); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, 0, 0); + ZoneFinder::NXRRSET, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("www.example.org."); + expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signedcname2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), - ZoneFinder::CNAME, 1, 1); + ZoneFinder::CNAME, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("acnamesig1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 1, 1); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("acnamesig2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 1, 1); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("acnamesig3.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 1, 1); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_rdatas.push_back("192.0.2.2"); doFindTest(finder, isc::dns::Name("ttldiff1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(360), - ZoneFinder::SUCCESS, 2, 0); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_rdatas.push_back("192.0.2.2"); doFindTest(finder, isc::dns::Name("ttldiff2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(360), - ZoneFinder::SUCCESS, 2, 0); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); + EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), @@ -498,7 +628,6 @@ TEST_F(DatabaseClientTest, find) { EXPECT_FALSE(current_connection_->searchRunning()); // Trigger the hardcoded exceptions and see if find() has cleaned up - /* EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), @@ -514,7 +643,7 @@ TEST_F(DatabaseClientTest, find) { NULL, ZoneFinder::FIND_DEFAULT), std::exception); EXPECT_FALSE(current_connection_->searchRunning()); - */ + EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), @@ -534,12 +663,16 @@ TEST_F(DatabaseClientTest, find) { // This RRSIG has the wrong sigtype field, which should be // an error if we decide to keep using that field // Right now the field is ignored, so it does not error + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("badsigtype.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, 1, 1); + ZoneFinder::SUCCESS, + expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_connection_->searchRunning()); - } } diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc index 2a1d471258..e864e2532c 100644 --- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc @@ -17,7 +17,6 @@ #include #include -#include #include using namespace isc::datasrc; @@ -135,7 +134,7 @@ TEST_F(SQLite3Conn, getRecords) { const int zone_id = zone_info.second; ASSERT_EQ(1, zone_id); - const size_t column_count = DatabaseConnection::RecordColumnCount; + const size_t column_count = DatabaseConnection::RECORDCOLUMNCOUNT; std::string columns[column_count]; // without search, getNext() should return false From c7db1351d3b1c25bfc31ed9e7b6b491e6bcb1555 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 11 Aug 2011 11:02:54 -0500 Subject: [PATCH 408/974] [jreed-docs-2] begin documenting stats commands and builtin in stats items --- src/bin/stats/b10-stats.xml | 93 ++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index f0c472dd29..13e568df63 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -20,7 +20,7 @@ - Oct 15, 2010 + August 11, 2011 @@ -67,6 +67,7 @@ it. b10-stats invokes "sendstats" command for bind10 after its initial starting because it's sure to collect statistics data from bind10. +
@@ -86,6 +87,94 @@ + + DEFAULT STATISTICS + + + The b10-stats daemon contains + built-in statistics: + + + + + + report_time + + The latest report date and time in + ISO 8601 format. + + + + stats.timestamp + The current date and time represented in + seconds since UNIX epoch (1970-01-01T0 0:00:00Z) with + precision (delimited with a period) up to + one hundred thousandth of second. + + + + + + + + + + + + CONFIGURATION AND COMMANDS + + + The b10-stats command does not have any + configurable settings. + + + + + The configuration commands are: + + + + remove removes the named statistics data. + + + + reset + + + + set + + + + show will send the statistics data + in JSON format. + By default, it outputs all the statistics data it has collected. + An optional item name may be specified to receive individual output. + + + + shutdown will shutdown the + b10-stats process. + (Note that the bind10 parent may restart it.) + + + + status simply indicates that the daemon is + running. + + + + + FILES /usr/local/share/bind10-devel/stats.spec @@ -126,7 +215,7 @@ HISTORY The b10-stats daemon was initially designed - and implemented by Naoki Kambe of JPRS in Oct 2010. + and implemented by Naoki Kambe of JPRS in October 2010. + + + STATISTICS DATA + + + The statistics data collected by the b10-stats + daemon include: + + + + + + bind10.boot_time + + The date and time that the bind10 + process started. + This is represented in ISO 8601 format. + + + + + + + + If you want to specify logging for one specific library - within the module, you set the name to 'module.library'. - For example, the logger used by the nameserver address - store component has the full name of 'Resolver.nsas'. If + within the module, you set the name to + module.library. For example, the + logger used by the nameserver address store component + has the full name of Resolver.nsas. If there is no entry in Logging for a particular library, it will use the configuration given for the module. - + + + To illustrate this, suppose you want the cache library to log messages of severity DEBUG, and the rest of the resolver code to log messages of severity INFO. To achieve - this you specify two loggers, one with the name 'Resolver' - and severity INFO, and one with the name 'Resolver.cache' - with severity DEBUG. As there are no entries for other - libraries (e.g. the nsas), they will use the configuration - for the module ('Resolver'), so giving the desired - behavior. + this you specify two loggers, one with the name + Resolver and severity INFO, and one with + the name Resolver.cache with severity + DEBUG. As there are no entries for other libraries (e.g. + the nsas), they will use the configuration for the module + (Resolver), so giving the desired behavior. - One special case is that of a module name of '*', which - is interpreted as 'any module'. You can set global logging - options by using this, including setting the logging - configuration for a library that is used by multiple - modules (e.g. '*.config" specifies the configuration - library code in whatever module is using it). + One special case is that of a module name of * + (asterisks), which is interpreted as any + module. You can set global logging options by using this, + including setting the logging configuration for a library + that is used by multiple modules (e.g. *.config + specifies the configuration library code in whatever + module is using it). @@ -1661,13 +1676,15 @@ then change those defaults with config set Resolver/forward_addresses[0]/address configuration that might match a particular logger, the specification with the more specific logger name takes precedence. For example, if there are entries for for - both '*' and 'Resolver', the resolver module - and all - libraries it uses - will log messages according to the - configuration in the second entry ('Resolver'). All other - modules will use the configuration of the first entry - ('*'). If there was also a configuration entry for - 'Resolver.cache', the cache library within the resolver - would use that in preference to the entry for 'Resolver'. + both * and Resolver, the + resolver module — and all libraries it uses — + will log messages according to the configuration in the + second entry (Resolver). All other modules + will use the configuration of the first entry + (*). If there was also a configuration + entry for Resolver.cache, the cache library + within the resolver would use that in preference to the + entry for Resolver. @@ -1675,14 +1692,15 @@ then change those defaults with config set Resolver/forward_addresses[0]/address One final note about the naming. When specifying the module name within a logger, use the name of the module - as specified in bindctl, e.g. 'Resolver' for the resolver - module, 'Xfrout' for the xfrout module etc. When the - message is logged, the message will include the name of - the logger generating the message, but with the module + as specified in bindctl, e.g. + Resolver for the resolver module, + Xfrout for the xfrout module, etc. When + the message is logged, the message will include the name + of the logger generating the message, but with the module name replaced by the name of the process implementing the module (so for example, a message generated by the - 'Auth.cache' logger will appear in the output with a - logger name of 'b10-auth.cache'). + Auth.cache logger will appear in the output + with a logger name of b10-auth.cache). @@ -1694,11 +1712,6 @@ then change those defaults with config set Resolver/forward_addresses[0]/address This specifies the category of messages logged. - - - - - Each message is logged with an associated severity which may be one of the following (in descending order of severity): @@ -1730,7 +1743,7 @@ then change those defaults with config set Resolver/forward_addresses[0]/address When the severity of a logger is set to one of these values, it will only log messages of that severity, and - the severities below it. The severity may also be set to + the severities above it. The severity may also be set to NONE, in which case all messages from that logger are inhibited. @@ -1745,9 +1758,9 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - Each logger can have zero or more output_options. These - specify where log messages are sent to. These are explained - in detail below. + Each logger can have zero or more + . These specify where log + messages are sent to. These are explained in detail below. @@ -1766,14 +1779,17 @@ then change those defaults with config set Resolver/forward_addresses[0]/address When a logger's severity is set to DEBUG, this value specifies what debug messages should be printed. It ranges - from 0 (least verbose) to 99 (most verbose). The general - classification of debug message types is - - - + from 0 (least verbose) to 99 (most verbose). - + + @@ -1788,13 +1804,15 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - If this is true, the output_options from the parent will - be used. For example, if there are two loggers configured; - 'Resolver' and 'Resolver.cache', and additive is true in - the second, it will write the log messages not only to - the destinations specified for 'Resolver.cache', but also - to the destinations as specified in the output_options - in the logger named Resolver'. + If this is true, the from + the parent will be used. For example, if there are two + loggers configured; Resolver and + Resolver.cache, and + is true in the second, it will write the log messages + not only to the destinations specified for + Resolver.cache, but also to the destinations + as specified in the in + the logger named Resolver. @@ -1809,9 +1827,10 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - The main settings for an output option are the 'destination' - and a value called 'output', the meaning of which depends - on the destination that is set. + The main settings for an output option are the + and a value called + , the meaning of which depends on + the destination that is set. @@ -1855,18 +1874,19 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - destination is 'console' + is console - The value of output must be one of 'stdout' - (messages printed to standard output) or 'stderr' - (messages printed to standard error). + The value of output must be one of stdout + (messages printed to standard output) or + stderr (messages printed to standard + error). - destination is 'file' + is file The value of output is interpreted as a file name; @@ -1876,12 +1896,13 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - destination is 'syslog' + is syslog - The value of output is interpreted as the syslog - facility (e.g. 'local0') that should be used for - log messages. + The value of output is interpreted as the + syslog facility (e.g. + local0) that should be used + for log messages. @@ -1890,7 +1911,7 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - The other options for output_options are: + The other options for are: @@ -1912,9 +1933,10 @@ then change those defaults with config set Resolver/forward_addresses[0]/address Only relevant when destination is file, this is maximum file size of output files in bytes. When the maximum - size is reached, the file is renamed (a ".1" is appended - to the name - if a ".1" file exists, it is renamed ".2" - etc.) and a new file opened. + size is reached, the file is renamed and a new file opened. + (For example, a ".1" is appended to the name — + if a ".1" file exists, it is renamed ".2", + etc.) @@ -1928,8 +1950,8 @@ then change those defaults with config set Resolver/forward_addresses[0]/address Maximum number of old log files to keep around when - rolling the output file. Only relevant when destination - if 'file'. + rolling the output file. Only relevant when + is file. @@ -1944,15 +1966,16 @@ then change those defaults with config set Resolver/forward_addresses[0]/address In this example we want to set the global logging to - write to the file /var/log/my_bind10.log, at severity - WARN. We want the authoritative server to log at DEBUG - with debuglevel 40, to a different file (/tmp/debug_messages). + write to the file /var/log/my_bind10.log, + at severity WARN. We want the authoritative server to + log at DEBUG with debuglevel 40, to a different file + (/tmp/debug_messages). -Start bindctl + Start bindctl. @@ -2144,7 +2167,7 @@ Logging/loggers[0]/output_options[0]/maxver 8 integer (modified) And every module will now be using the values from the - logger named '*'. + logger named *. From c46b0bc28c22f2ae4b46c592f450e745774846d4 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Fri, 12 Aug 2011 08:21:22 -0500 Subject: [PATCH 437/974] [1011] move Logging Message Format section after Logging configuration section --- doc/guide/bind10-guide.xml | 178 ++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 90 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 48593e5e07..9f3ee80d68 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1470,96 +1470,6 @@ then change those defaults with config set Resolver/forward_addresses[0]/address Logging - - -
- Logging Message Format - - - Each message written by BIND 10 to the configured logging - destinations comprises a number of components that identify - the origin of the message and, if the message indicates - a problem, information about the problem that may be - useful in fixing it. - - - - Consider the message below logged to a file: - 2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink] - ASIODNS_OPENSOCK error 111 opening TCP socket to 127.0.0.1(53) - - - - Note: the layout of messages written to the system logging - file (syslog) may be slightly different. This message has - been split across two lines here for display reasons; in the - logging file, it will appear on one line.) - - - - The log message comprises a number of components: - - - - 2011-06-15 13:48:22.034 - - - The date and time at which the message was generated. - - - - - ERROR - - The severity of the message. - - - - - [b10-resolver.asiolink] - - The source of the message. This comprises two components: - the BIND 10 process generating the message (in this - case, b10-resolver) and the module - within the program from which the message originated - (which in the example is the asynchronous I/O link - module, asiolink). - - - - - ASIODNS_OPENSOCK - - The message identification. Every message in BIND 10 - has a unique identification, which can be used as an - index into the BIND 10 Messages - Manual () from which more information can be obtained. - - - - - error 111 opening TCP socket to 127.0.0.1(53) - - A brief description of the cause of the problem. - Within this text, information relating to the condition - that caused the message to be logged will be included. - In this example, error number 111 (an operating - system-specific error number) was encountered when - trying to open a TCP connection to port 53 on the - local system (address 127.0.0.1). The next step - would be to find out the reason for the failure by - consulting your system's documentation to identify - what error number 111 means. - - - - - -
-
Logging configuration @@ -2175,6 +2085,94 @@ Logging/loggers[0]/output_options[0]/maxver 8 integer (modified)
+
+ Logging Message Format + + + Each message written by BIND 10 to the configured logging + destinations comprises a number of components that identify + the origin of the message and, if the message indicates + a problem, information about the problem that may be + useful in fixing it. + + + + Consider the message below logged to a file: + 2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink] + ASIODNS_OPENSOCK error 111 opening TCP socket to 127.0.0.1(53) + + + + Note: the layout of messages written to the system logging + file (syslog) may be slightly different. This message has + been split across two lines here for display reasons; in the + logging file, it will appear on one line.) + + + + The log message comprises a number of components: + + + + 2011-06-15 13:48:22.034 + + + The date and time at which the message was generated. + + + + + ERROR + + The severity of the message. + + + + + [b10-resolver.asiolink] + + The source of the message. This comprises two components: + the BIND 10 process generating the message (in this + case, b10-resolver) and the module + within the program from which the message originated + (which in the example is the asynchronous I/O link + module, asiolink). + + + + + ASIODNS_OPENSOCK + + The message identification. Every message in BIND 10 + has a unique identification, which can be used as an + index into the BIND 10 Messages + Manual () from which more information can be obtained. + + + + + error 111 opening TCP socket to 127.0.0.1(53) + + A brief description of the cause of the problem. + Within this text, information relating to the condition + that caused the message to be logged will be included. + In this example, error number 111 (an operating + system-specific error number) was encountered when + trying to open a TCP connection to port 53 on the + local system (address 127.0.0.1). The next step + would be to find out the reason for the failure by + consulting your system's documentation to identify + what error number 111 means. + + + + + +
+
From e021b84f7fc20b3e3927093ed87e9c873d33a443 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 12 Aug 2011 15:23:04 +0200 Subject: [PATCH 438/974] [1063] Check signatures They work out of the box thanks to the getRRset method, but it needs to be tested explicitly. The tests test only the NS and DNAME, the rest is tested above. --- src/lib/datasrc/tests/database_unittest.cc | 43 +++++++++++++--------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index e3d1393154..f4b5d0948c 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -267,12 +267,16 @@ private: // Data for testing delegation (with NS and DNAME) addRecord("NS", "3600", "", "ns.example.com."); addRecord("NS", "3600", "", "ns.delegation.example.org."); + addRecord("RRSIG", "3600", "", "NS 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("delegation.example.org."); addRecord("A", "3600", "", "192.0.2.1"); addCurName("ns.delegation.example.org."); addRecord("A", "3600", "", "192.0.2.1"); addRecord("DNAME", "3600", "", "dname.example.com."); + addRecord("RRSIG", "3600", "", "DNAME 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("dname.example.org."); addRecord("A", "3600", "", "192.0.2.1"); addCurName("below.dname.example.org."); @@ -294,6 +298,8 @@ private: // doesn't break anything addRecord("NS", "3600", "", "ns.example.com."); addRecord("A", "3600", "", "192.0.2.1"); + addRecord("RRSIG", "3600", "", "NS 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("example.org."); } }; @@ -396,9 +402,10 @@ doFindTest(shared_ptr finder, expected_rdatas); if (expected_sig_rdatas.size() > 0) { - checkRRset(result.rrset->getRRsig(), name, - finder->getClass(), isc::dns::RRType::RRSIG(), - expected_ttl, expected_sig_rdatas); + checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ? + expected_name : name, finder->getClass(), + isc::dns::RRType::RRSIG(), expected_ttl, + expected_sig_rdatas); } else { EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig()); } @@ -729,6 +736,8 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.clear(); expected_rdatas.push_back("ns.example.com."); + expected_sig_rdatas.push_back("NS 5 3 3600 20000101000000 20000201000000 " + "12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, @@ -741,6 +750,8 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("ns.example.com."); expected_rdatas.push_back("ns.delegation.example.org."); + expected_sig_rdatas.push_back("NS 5 3 3600 20000101000000 20000201000000 " + "12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::A(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas, @@ -753,12 +764,7 @@ TEST_F(DatabaseClientTest, find) { EXPECT_FALSE(current_database_->searchRunning()); // Even when we check directly at the delegation point, we should get - // the NS (both when the RRset does and doesn't exist in data) - doFindTest(finder, isc::dns::Name("delegation.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas, - expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + // the NS doFindTest(finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas, @@ -777,6 +783,10 @@ TEST_F(DatabaseClientTest, find) { // the behaviour anyway just to make sure) expected_rdatas.clear(); expected_rdatas.push_back("dname.example.com."); + expected_sig_rdatas.clear(); + expected_sig_rdatas.push_back("DNAME 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. " + "FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas, @@ -788,9 +798,16 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas, isc::dns::Name("dname.example.org.")); EXPECT_FALSE(current_database_->searchRunning()); + // Asking direcly for DNAME should give SUCCESS + doFindTest(finder, isc::dns::Name("dname.example.org."), + isc::dns::RRType::DNAME(), isc::dns::RRType::DNAME(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, + expected_sig_rdatas); + // But we don't delegate at DNAME point expected_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); + expected_sig_rdatas.clear(); doFindTest(finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, @@ -803,14 +820,6 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas); EXPECT_FALSE(current_database_->searchRunning()); - // Asking direcly for DNAME should give SUCCESS - expected_rdatas.clear(); - expected_rdatas.push_back("dname.example.com."); - doFindTest(finder, isc::dns::Name("dname.example.org."), - isc::dns::RRType::DNAME(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, - expected_sig_rdatas); - // This is broken dname, it contains two targets EXPECT_THROW(finder->find(isc::dns::Name("below.baddname.example.org."), isc::dns::RRType::A(), NULL, From 17a87c6bb9d16e992fadd47b11b3eb26af54ac69 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Fri, 12 Aug 2011 08:27:48 -0500 Subject: [PATCH 439/974] [1011] changelog entry for trac #1011 --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5a145584ce..56bf8e97d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +278. [doc] jelte + Add logging configuration documentation to the guide. + (Trac #1011, git TODO) + 277. [func] jerry Implement the SRV rrtype according to RFC2782. (Trac #1128, git 5fd94aa027828c50e63ae1073d9d6708e0a9c223) From 575bd3ec2fe918257cb448eee8ebbff269d85431 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 12 Aug 2011 14:35:48 -0700 Subject: [PATCH 440/974] [1068] use Makefile under datasrc testdata for a writable sqlite3 DB file. --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 6e129b6093..6999a21d74 100644 --- a/configure.ac +++ b/configure.ac @@ -856,6 +856,7 @@ AC_CONFIG_FILES([Makefile src/lib/exceptions/tests/Makefile src/lib/datasrc/Makefile src/lib/datasrc/tests/Makefile + src/lib/datasrc/tests/testdata/Makefile src/lib/xfr/Makefile src/lib/log/Makefile src/lib/log/compiler/Makefile From 1351cb42c92cd415003adf6234d96507c8a6d2db Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 12 Aug 2011 14:45:48 -0700 Subject: [PATCH 441/974] [1068] update (write) support in the SQLite3 accessor. It includes refactoring/cleanups on managing SQLite3 statements. --- src/lib/datasrc/sqlite3_accessor.cc | 371 +++++++++++------- src/lib/datasrc/sqlite3_accessor.h | 35 +- src/lib/datasrc/tests/Makefile.am | 6 +- .../tests/sqlite3_accessor_unittest.cc | 288 ++++++++++++++ 4 files changed, 562 insertions(+), 138 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 817d53087f..9b4d6c1e60 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -14,36 +14,104 @@ #include +#include +#include + +#include + #include #include #include #include +using namespace std; + namespace isc { namespace datasrc { +// The following enum and char* array define the SQL statements commonly +// used in this implementation. Corresponding prepared statements (of +// type sqlite3_stmt*) are maintained in the statements_ array of the +// SQLite3Parameters structure. + +enum StatementID { + ZONE = 0, + ANY = 1, + BEGIN = 2, + COMMIT = 3, + ROLLBACK = 4, + DEL_ZONE_RECORDS = 5, + ADD_RECORD = 6, + DEL_RECORD = 7, + NUM_STATEMENTS = 8 +}; + +const char* const text_statements[NUM_STATEMENTS] = { + "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2", // ZONE + "SELECT rdtype, ttl, sigtype, rdata FROM records " // ANY + "WHERE zone_id=?1 AND name=?2", + "BEGIN", // BEGIN + "COMMIT", // COMMIT + "ROLLBACK", // ROLLBACK + "DELETE FROM records WHERE zone_id=?1", // DEL_ZONE_RECORDS + "INSERT INTO records " // ADD_RECORD + "(zone_id, name, rname, ttl, rdtype, sigtype, rdata) " + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)", + "DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD + "AND rdtype=?3 AND rdata=?4" +}; + struct SQLite3Parameters { SQLite3Parameters() : - db_(NULL), version_(-1), - q_zone_(NULL), q_any_(NULL) - /*q_record_(NULL), q_addrs_(NULL), q_referral_(NULL), - q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL), - q_prevnsec3_(NULL) */ - {} + db_(NULL), version_(-1), updating_zone(false), updated_zone_id(-1) + { + for (int i = 0; i < NUM_STATEMENTS; ++i) { + statements_[i] = NULL; + } + } sqlite3* db_; int version_; - sqlite3_stmt* q_zone_; - sqlite3_stmt* q_any_; - /* - TODO: Yet unneeded statements - sqlite3_stmt* q_record_; - sqlite3_stmt* q_addrs_; - sqlite3_stmt* q_referral_; - sqlite3_stmt* q_count_; - sqlite3_stmt* q_previous_; - sqlite3_stmt* q_nsec3_; - sqlite3_stmt* q_prevnsec3_; - */ + sqlite3_stmt* statements_[NUM_STATEMENTS]; + bool updating_zone; // whether or not updating the zone + int updated_zone_id; // valid only when updating_zone is true +}; + +// This is a helper class to encapsulate the code logic of executing +// a specific SQLite3 statement, ensuring the corresponding prepared +// statement is always reset whether the execution is completed successfully +// or it results in an exception. +// Note that an object of this class is intended to be used for "ephemeral" +// statement, which is completed with a single "step" (normally within a +// single call to an SQLite3Database method). In particular, it cannot be +// used for "SELECT" variants, which generally expect multiple matching rows. +class StatementExecuter { +public: + // desc will be used on failure in the what() message of the resulting + // DataSourceError exception. + StatementExecuter(SQLite3Parameters* dbparameters, StatementID stmt_id, + const char* desc) : + dbparameters_(dbparameters), stmt_id_(stmt_id), desc_(desc) + { + sqlite3_clear_bindings(dbparameters_->statements_[stmt_id_]); + } + + ~StatementExecuter() { + sqlite3_reset(dbparameters_->statements_[stmt_id_]); + } + + void exec() { + if (sqlite3_step(dbparameters_->statements_[stmt_id_]) != + SQLITE_DONE) { + sqlite3_reset(dbparameters_->statements_[stmt_id_]); + isc_throw(DataSourceError, "failed to " << desc_ << ": " << + sqlite3_errmsg(dbparameters_->db_)); + } + } + +private: + SQLite3Parameters* dbparameters_; + const StatementID stmt_id_; + const char* const desc_; }; SQLite3Database::SQLite3Database(const std::string& filename, @@ -70,35 +138,10 @@ namespace { class Initializer { public: ~Initializer() { - if (params_.q_zone_ != NULL) { - sqlite3_finalize(params_.q_zone_); + for (int i = 0; i < NUM_STATEMENTS; ++i) { + sqlite3_finalize(params_.statements_[i]); } - if (params_.q_any_ != NULL) { - sqlite3_finalize(params_.q_any_); - } - /* - if (params_.q_record_ != NULL) { - sqlite3_finalize(params_.q_record_); - } - if (params_.q_addrs_ != NULL) { - sqlite3_finalize(params_.q_addrs_); - } - if (params_.q_referral_ != NULL) { - sqlite3_finalize(params_.q_referral_); - } - if (params_.q_count_ != NULL) { - sqlite3_finalize(params_.q_count_); - } - if (params_.q_previous_ != NULL) { - sqlite3_finalize(params_.q_previous_); - } - if (params_.q_nsec3_ != NULL) { - sqlite3_finalize(params_.q_nsec3_); - } - if (params_.q_prevnsec3_ != NULL) { - sqlite3_finalize(params_.q_prevnsec3_); - } - */ + if (params_.db_ != NULL) { sqlite3_close(params_.db_); } @@ -134,41 +177,6 @@ const char* const SCHEMA_LIST[] = { NULL }; -const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"; - -const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata " - "FROM records WHERE zone_id=?1 AND name=?2"; - -/* TODO: Prune the statements, not everything will be needed maybe? -const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata " - "FROM records WHERE zone_id=?1 AND name=?2 AND " - "((rdtype=?3 OR sigtype=?3) OR " - "(rdtype='CNAME' OR sigtype='CNAME') OR " - "(rdtype='NS' OR sigtype='NS'))"; - -const char* const q_addrs_str = "SELECT rdtype, ttl, sigtype, rdata " - "FROM records WHERE zone_id=?1 AND name=?2 AND " - "(rdtype='A' OR sigtype='A' OR rdtype='AAAA' OR sigtype='AAAA')"; - -const char* const q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM " - "records WHERE zone_id=?1 AND name=?2 AND" - "(rdtype='NS' OR sigtype='NS' OR rdtype='DS' OR sigtype='DS' OR " - "rdtype='DNAME' OR sigtype='DNAME')"; - -const char* const q_count_str = "SELECT COUNT(*) FROM records " - "WHERE zone_id=?1 AND rname LIKE (?2 || '%');"; - -const char* const q_previous_str = "SELECT name FROM records " - "WHERE zone_id=?1 AND rdtype = 'NSEC' AND " - "rname < $2 ORDER BY rname DESC LIMIT 1"; - -const char* const q_nsec3_str = "SELECT rdtype, ttl, rdata FROM nsec3 " - "WHERE zone_id = ?1 AND hash = $2"; - -const char* const q_prevnsec3_str = "SELECT hash FROM nsec3 " - "WHERE zone_id = ?1 AND hash <= $2 ORDER BY hash DESC LIMIT 1"; - */ - sqlite3_stmt* prepare(sqlite3* const db, const char* const statement) { sqlite3_stmt* prepared = NULL; @@ -203,17 +211,9 @@ checkAndSetupSchema(Initializer* initializer) { } } - initializer->params_.q_zone_ = prepare(db, q_zone_str); - initializer->params_.q_any_ = prepare(db, q_any_str); - /* TODO: Yet unneeded statements - initializer->params_.q_record_ = prepare(db, q_record_str); - initializer->params_.q_addrs_ = prepare(db, q_addrs_str); - initializer->params_.q_referral_ = prepare(db, q_referral_str); - initializer->params_.q_count_ = prepare(db, q_count_str); - initializer->params_.q_previous_ = prepare(db, q_previous_str); - initializer->params_.q_nsec3_ = prepare(db, q_nsec3_str); - initializer->params_.q_prevnsec3_ = prepare(db, q_prevnsec3_str); - */ + for (int i = 0; i < NUM_STATEMENTS; ++i) { + initializer->params_.statements_[i] = prepare(db, text_statements[i]); + } } } @@ -253,34 +253,10 @@ SQLite3Database::close(void) { } // XXX: sqlite3_finalize() could fail. What should we do in that case? - sqlite3_finalize(dbparameters_->q_zone_); - dbparameters_->q_zone_ = NULL; - - sqlite3_finalize(dbparameters_->q_any_); - dbparameters_->q_any_ = NULL; - - /* TODO: Once they are needed or not, uncomment or drop - sqlite3_finalize(dbparameters->q_record_); - dbparameters->q_record_ = NULL; - - sqlite3_finalize(dbparameters->q_addrs_); - dbparameters->q_addrs_ = NULL; - - sqlite3_finalize(dbparameters->q_referral_); - dbparameters->q_referral_ = NULL; - - sqlite3_finalize(dbparameters->q_count_); - dbparameters->q_count_ = NULL; - - sqlite3_finalize(dbparameters->q_previous_); - dbparameters->q_previous_ = NULL; - - sqlite3_finalize(dbparameters->q_prevnsec3_); - dbparameters->q_prevnsec3_ = NULL; - - sqlite3_finalize(dbparameters->q_nsec3_); - dbparameters->q_nsec3_ = NULL; - */ + for (int i = 0; i < NUM_STATEMENTS; ++i) { + sqlite3_finalize(dbparameters_->statements_[i]); + dbparameters_->statements_[i] = NULL; + } sqlite3_close(dbparameters_->db_); dbparameters_->db_ = NULL; @@ -288,36 +264,38 @@ SQLite3Database::close(void) { std::pair SQLite3Database::getZone(const isc::dns::Name& name) const { + return (getZone(name.toText())); +} + +std::pair +SQLite3Database::getZone(const string& name) const { int rc; + sqlite3_stmt* const stmt = dbparameters_->statements_[ZONE]; // Take the statement (simple SELECT id FROM zones WHERE...) // and prepare it (bind the parameters to it) - sqlite3_reset(dbparameters_->q_zone_); - rc = sqlite3_bind_text(dbparameters_->q_zone_, 1, name.toText().c_str(), - -1, SQLITE_TRANSIENT); + sqlite3_reset(stmt); + rc = sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_STATIC); if (rc != SQLITE_OK) { isc_throw(SQLite3Error, "Could not bind " << name << " to SQL statement (zone)"); } - rc = sqlite3_bind_text(dbparameters_->q_zone_, 2, class_.c_str(), -1, - SQLITE_STATIC); + rc = sqlite3_bind_text(stmt, 2, class_.c_str(), -1, SQLITE_STATIC); if (rc != SQLITE_OK) { isc_throw(SQLite3Error, "Could not bind " << class_ << " to SQL statement (zone)"); } // Get the data there and see if it found anything - rc = sqlite3_step(dbparameters_->q_zone_); + rc = sqlite3_step(stmt); std::pair result; if (rc == SQLITE_ROW) { - result = std::pair(true, - sqlite3_column_int(dbparameters_-> - q_zone_, 0)); + result = std::pair(true, sqlite3_column_int(stmt, 0)); } else { result = std::pair(false, 0); } // Free resources - sqlite3_reset(dbparameters_->q_zone_); + sqlite3_reset(stmt); return (result); } @@ -325,14 +303,16 @@ SQLite3Database::getZone(const isc::dns::Name& name) const { void SQLite3Database::searchForRecords(int zone_id, const std::string& name) { resetSearch(); - if (sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id) != SQLITE_OK) { + + sqlite3_stmt* const stmt = dbparameters_->statements_[ANY]; + if (sqlite3_bind_int(stmt, 1, zone_id) != SQLITE_OK) { isc_throw(DataSourceError, "Error in sqlite3_bind_int() for zone_id " << zone_id << ": " << sqlite3_errmsg(dbparameters_->db_)); } // use transient since name is a ref and may disappear - if (sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, - SQLITE_TRANSIENT) != SQLITE_OK) { + if (sqlite3_bind_text(stmt, 2, name.c_str(), -1, SQLITE_TRANSIENT) != + SQLITE_OK) { isc_throw(DataSourceError, "Error in sqlite3_bind_text() for name " << name << ": " << sqlite3_errmsg(dbparameters_->db_)); @@ -376,7 +356,7 @@ SQLite3Database::getNextRecord(std::string columns[], size_t column_count) { "of size " << COLUMN_COUNT << " to getNextRecord()"); } - sqlite3_stmt* current_stmt = dbparameters_->q_any_; + sqlite3_stmt* current_stmt = dbparameters_->statements_[ANY]; const int rc = sqlite3_step(current_stmt); if (rc == SQLITE_ROW) { @@ -404,8 +384,127 @@ SQLite3Database::getNextRecord(std::string columns[], size_t column_count) { void SQLite3Database::resetSearch() { - sqlite3_reset(dbparameters_->q_any_); - sqlite3_clear_bindings(dbparameters_->q_any_); + sqlite3_reset(dbparameters_->statements_[ANY]); + sqlite3_clear_bindings(dbparameters_->statements_[ANY]); +} + +pair +SQLite3Database::startUpdateZone(const string& zone_name, const bool replace) { + if (dbparameters_->updating_zone) { + isc_throw(DataSourceError, + "duplicate zone update on SQLite3 data source"); + } + + const pair zone_info(getZone(zone_name)); + if (!zone_info.first) { + return (zone_info); + } + + dbparameters_->updating_zone = true; + dbparameters_->updated_zone_id = zone_info.second; + + StatementExecuter(dbparameters_, BEGIN, + "start an SQLite3 transaction").exec(); + + if (replace) { + StatementExecuter delzone_exec(dbparameters_, DEL_ZONE_RECORDS, + "delete zone records"); + + sqlite3_clear_bindings(dbparameters_->statements_[DEL_ZONE_RECORDS]); + if (sqlite3_bind_int(dbparameters_->statements_[DEL_ZONE_RECORDS], + 1, zone_info.second) != SQLITE_OK) { + isc_throw(DataSourceError, + "failed to bind SQLite3 parameter: " << + sqlite3_errmsg(dbparameters_->db_)); + } + + delzone_exec.exec(); + } + + return (zone_info); +} + +void +SQLite3Database::commitUpdateZone() { + if (!dbparameters_->updating_zone) { + isc_throw(DataSourceError, "committing zone update on SQLite3 " + "data source without transaction"); + } + + StatementExecuter(dbparameters_, COMMIT, + "commit an SQLite3 transaction").exec(); + dbparameters_->updating_zone = false; + dbparameters_->updated_zone_id = -1; +} + +void +SQLite3Database::rollbackUpdateZone() { + if (!dbparameters_->updating_zone) { + isc_throw(DataSourceError, "rolling back zone update on SQLite3 " + "data source without transaction"); + } + + // We expect that ROLLBACK always succeeds. But could that fail? + // If so, what should we do? Is it okay to simply propagate the + // DataSourceError exception? + StatementExecuter(dbparameters_, ROLLBACK, + "commit an SQLite3 transaction").exec(); + dbparameters_->updating_zone = false; + dbparameters_->updated_zone_id = -1; +} + +namespace { +// Commonly used code sequence for adding/deleting record +void +doUpdate(SQLite3Parameters* dbparams, StatementID stmt_id, + const vector& update_params, const char* exec_desc) +{ + sqlite3_stmt* const stmt = dbparams->statements_[stmt_id]; + StatementExecuter executer(dbparams, stmt_id, exec_desc); + + int param_id = 0; + if (sqlite3_bind_int(stmt, ++param_id, dbparams->updated_zone_id) + != SQLITE_OK) { + isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " << + sqlite3_errmsg(dbparams->db_)); + } + BOOST_FOREACH(const string& column, update_params) { + if (sqlite3_bind_text(stmt, ++param_id, column.c_str(), -1, + SQLITE_TRANSIENT) != SQLITE_OK) { + isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " << + sqlite3_errmsg(dbparams->db_)); + } + } + executer.exec(); +} +} + +void +SQLite3Database::addRecordToZone(const vector& columns) { + if (!dbparameters_->updating_zone) { + isc_throw(DataSourceError, "adding record to SQLite3 " + "data source without transaction"); + } + if (columns.size() != ADD_COLUMN_COUNT) { + isc_throw(DataSourceError, "adding incompatible number of columns " + "to SQLite3 data source: " << columns.size()); + } + + doUpdate(dbparameters_, ADD_RECORD, columns, "add record to zone"); +} + +void +SQLite3Database::deleteRecordInZone(const vector& params) { + if (!dbparameters_->updating_zone) { + isc_throw(DataSourceError, "deleting record in SQLite3 " + "data source without transaction"); + } + if (params.size() != DEL_PARAM_COUNT) { + isc_throw(DataSourceError, "incompatible # of parameters for " + "deleting in SQLite3 data source: " << params.size()); + } + + doUpdate(dbparameters_, DEL_RECORD, params, "delete record from zone"); } } diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 4c2ec8bfd3..00c19f6787 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -135,6 +135,30 @@ public: */ virtual void resetSearch(); + /// TBD + /// This cannot be nested. + virtual std::pair startUpdateZone(const std::string& zone_name, + bool replace); + + /// TBD + /// Note: we are quite impatient here: it's quite possible that the COMMIT + /// fails due to other process performing SELECT on the same database + /// (consider the case where COMMIT is done by xfrin or dynamic update + /// server while an authoritative server is busy reading the DB). + /// In a future version we should probably need to introduce some retry + /// attempt and/or increase timeout before giving up the COMMIT, even + /// if it still doesn't guarantee 100% success. + virtual void commitUpdateZone(); + + /// TBD + virtual void rollbackUpdateZone(); + + /// TBD + virtual void addRecordToZone(const std::vector& columns); + + /// TBD + virtual void deleteRecordInZone(const std::vector& params); + /// The SQLite3 implementation of this method returns a string starting /// with a fixed prefix of "sqlite3_" followed by the DB file name /// removing any path name. For example, for the DB file @@ -143,6 +167,11 @@ public: virtual const std::string& getDBName() const { return (database_name_); } private: + // same as the public version except it takes name as a string + // (actually this is the intended interface. this should replace the + // current public version). + std::pair getZone(const std::string& name) const; + /// \brief Private database data SQLite3Parameters* dbparameters_; /// \brief The class for which the queries are done @@ -157,4 +186,8 @@ private: } } -#endif +#endif // __DATASRC_SQLITE3_CONNECTION_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index 1a65f82849..df640b2cd0 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -1,8 +1,12 @@ +SUBDIRS = . testdata + AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CPPFLAGS += $(SQLITE_CFLAGS) -AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\" +AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\" +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_builddir)/testdata\" +AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" AM_CXXFLAGS = $(B10_CXXFLAGS) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 097c821330..5e0b83423c 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -11,6 +11,9 @@ // 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. + +#include + #include #include @@ -20,7 +23,9 @@ #include #include +using namespace std; using namespace isc::datasrc; +using boost::shared_ptr; using isc::data::ConstElementPtr; using isc::data::Element; using isc::dns::RRClass; @@ -242,4 +247,287 @@ TEST_F(SQLite3Access, getRecords) { "33495 example.com. FAKEFAKEFAKEFAKE"); } +// +// Commonly used data for update tests +// +const char* const common_expected_data[] = { + // Test record already stored in the tested sqlite3 DB file. + "foo.bar.example.com.", "com.example.bar.foo.", "3600", "A", "", + "192.0.2.1" +}; +const char* const new_data[] = { + // Newly added data commonly used by some of the tests below + "newdata.example.com.", "com.example.newdata.", "3600", "A", "", + "192.0.2.1" +}; +const char* const deleted_data[] = { + // Existing data to be removed commonly used by some of the tests below + "foo.bar.example.com.", "A", "192.0.2.1" +}; + +class SQLite3Update : public SQLite3Access { +protected: + SQLite3Update() { + ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR + "/test.sqlite3 " + TEST_DATA_BUILDDIR "/test.sqlite3.copied")); + initAccessor(TEST_DATA_BUILDDIR "/test.sqlite3.copied", RRClass::IN()); + zone_id = db->getZone(Name("example.com")).second; + another_db.reset(new SQLite3Database( + TEST_DATA_BUILDDIR "/test.sqlite3.copied", + RRClass::IN())); + expected_stored.push_back(common_expected_data); + } + + int zone_id; + std::string get_columns[DatabaseAccessor::COLUMN_COUNT]; + std::vector update_columns; + + vector expected_stored; // placeholder for checkRecords + vector empty_stored; // indicate no corresponding data + + // Another accessor, emulating one running on a different process/thread + shared_ptr another_db; +}; + +void +checkRecords(SQLite3Database& db, int zone_id, const std::string& name, + vector expected_rows) +{ + db.searchForRecords(zone_id, name); + std::string columns[DatabaseAccessor::COLUMN_COUNT]; + vector::const_iterator it = expected_rows.begin(); + while (db.getNextRecord(columns, DatabaseAccessor::COLUMN_COUNT)) { + ASSERT_TRUE(it != expected_rows.end()); + checkRecordRow(columns, (*it)[3], (*it)[2], (*it)[4], (*it)[5]); + ++it; + } + EXPECT_TRUE(it == expected_rows.end()); +} + +TEST_F(SQLite3Update, emptyUpdate) { + // If we do nothing between start and commit, the zone content + // should be intact. + + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + zone_id = db->startUpdateZone("example.com.", false).second; + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + db->commitUpdateZone(); + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); +} + +TEST_F(SQLite3Update, flushZone) { + // With 'replace' being true startUpdateZone() will flush the existing + // zone content. + + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + zone_id = db->startUpdateZone("example.com.", true).second; + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + db->commitUpdateZone(); + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); +} + +TEST_F(SQLite3Update, readWhileUpdate) { + zone_id = db->startUpdateZone("example.com.", true).second; + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + + // Until commit is done, the other accessor should see the old data + another_db->searchForRecords(zone_id, "foo.bar.example.com."); + checkRecords(*another_db, zone_id, "foo.bar.example.com.", + expected_stored); + + // Once the changes are committed, the other accessor will see the new + // data. + db->commitUpdateZone(); + another_db->searchForRecords(zone_id, "foo.bar.example.com."); + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); +} + +TEST_F(SQLite3Update, rollback) { + zone_id = db->startUpdateZone("example.com.", true).second; + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + + // Rollback will revert the change made by startUpdateZone(, true). + db->rollbackUpdateZone(); + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); +} + +TEST_F(SQLite3Update, commitConflict) { + // Start reading the DB by another accessor. We should stop at a single + // call to getNextRecord() to keep holding the lock. + another_db->searchForRecords(zone_id, "foo.example.com."); + EXPECT_TRUE(another_db->getNextRecord(get_columns, + DatabaseAccessor::COLUMN_COUNT)); + + // Due to getNextRecord() above, the other accessor holds a DB lock, + // which will prevent commit. + zone_id = db->startUpdateZone("example.com.", true).second; + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + EXPECT_THROW(db->commitUpdateZone(), DataSourceError); + db->rollbackUpdateZone(); // rollback should still succeed + + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); +} + +TEST_F(SQLite3Update, updateConflict) { + // Similar to the previous case, but this is a conflict with another + // update attempt. Note that these two accessors modify disjoint sets + // of data; sqlite3 only has a coarse-grained lock so we cannot allow + // these updates to run concurrently. + EXPECT_TRUE(another_db->startUpdateZone("sql1.example.com.", true).first); + EXPECT_THROW(db->startUpdateZone("example.com.", true), DataSourceError); + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); +} + +TEST_F(SQLite3Update, duplicateUpdate) { + db->startUpdateZone("example.com.", false); + EXPECT_THROW(db->startUpdateZone("example.com.", false), DataSourceError); +} + +TEST_F(SQLite3Update, commitWithoutTransaction) { + EXPECT_THROW(db->commitUpdateZone(), DataSourceError); +} + +TEST_F(SQLite3Update, rollbackWithoutTransaction) { + EXPECT_THROW(db->rollbackUpdateZone(), DataSourceError); +} + +TEST_F(SQLite3Update, addRecord) { + // Before update, there should be no record for this name + checkRecords(*db, zone_id, "newdata.example.com.", empty_stored); + + zone_id = db->startUpdateZone("example.com.", false).second; + update_columns.assign(new_data, + new_data + DatabaseAccessor::ADD_COLUMN_COUNT); + db->addRecordToZone(update_columns); + + expected_stored.clear(); + expected_stored.push_back(new_data); + checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); + + // Commit the change, and confirm the new data is still there. + db->commitUpdateZone(); + checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); +} + +TEST_F(SQLite3Update, addThenRollback) { + zone_id = db->startUpdateZone("example.com.", false).second; + update_columns.assign(new_data, + new_data + DatabaseAccessor::ADD_COLUMN_COUNT); + db->addRecordToZone(update_columns); + + expected_stored.clear(); + expected_stored.push_back(new_data); + checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); + + db->rollbackUpdateZone(); + checkRecords(*db, zone_id, "newdata.example.com.", empty_stored); +} + +TEST_F(SQLite3Update, duplicateAdd) { + const char* const dup_data[] = { + "foo.bar.example.com.", "com.example.bar.foo.", "3600", "A", "", + "192.0.2.1" + }; + expected_stored.clear(); + expected_stored.push_back(dup_data); + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + + // Adding exactly the same data. As this backend is "dumb", another + // row of the same content will be inserted. + update_columns.assign(dup_data, + dup_data + DatabaseAccessor::ADD_COLUMN_COUNT); + zone_id = db->startUpdateZone("example.com.", false).second; + db->addRecordToZone(update_columns); + expected_stored.push_back(dup_data); + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); +} + +TEST_F(SQLite3Update, invalidAdd) { + // An attempt of add before an explicit start of transaction + EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); + + // Short column vector + update_columns.clear(); + zone_id = db->startUpdateZone("example.com.", false).second; + EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); + + // Too many columns + for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT + 1; ++i) { + update_columns.push_back(""); + } + EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); +} + +TEST_F(SQLite3Update, deleteRecord) { + zone_id = db->startUpdateZone("example.com.", false).second; + + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + + update_columns.assign(deleted_data, deleted_data + + DatabaseAccessor::DEL_PARAM_COUNT); + db->deleteRecordInZone(update_columns); + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + + // Commit the change, and confirm the deleted data still isn't there. + db->commitUpdateZone(); + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); +} + +TEST_F(SQLite3Update, deleteThenRollback) { + zone_id = db->startUpdateZone("example.com.", false).second; + + update_columns.assign(deleted_data, deleted_data + + DatabaseAccessor::DEL_PARAM_COUNT); + db->deleteRecordInZone(update_columns); + checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + + // Rollback the change, and confirm the data still exists. + db->rollbackUpdateZone(); + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); +} + +TEST_F(SQLite3Update, deleteNonexistent) { + zone_id = db->startUpdateZone("example.com.", false).second; + update_columns.assign(deleted_data, deleted_data + + DatabaseAccessor::DEL_PARAM_COUNT); + + // Replace the name with a non existent one, then try to delete it. + // nothing should happen. + update_columns[0] = "no-such-name.example.com."; + checkRecords(*db, zone_id, "no-such-name.example.com.", empty_stored); + db->deleteRecordInZone(update_columns); + checkRecords(*db, zone_id, "no-such-name.example.com.", empty_stored); + + // Name exists but the RR type is different. Delete attempt shouldn't + // delete only by name. + update_columns.assign(deleted_data, deleted_data + + DatabaseAccessor::DEL_PARAM_COUNT); + update_columns[1] = "AAAA"; + db->deleteRecordInZone(update_columns); + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + + // Similar to the previous case, but RDATA is different. + update_columns.assign(deleted_data, deleted_data + + DatabaseAccessor::DEL_PARAM_COUNT); + update_columns[2] = "192.0.2.2"; + db->deleteRecordInZone(update_columns); + checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); +} + +TEST_F(SQLite3Update, invalidDelete) { + // An attempt of delete before an explicit start of transaction + EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); + + // Short column vector + update_columns.clear(); + zone_id = db->startUpdateZone("example.com.", false).second; + EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); + + // Too many parameters + for (int i = 0; i < DatabaseAccessor::DEL_PARAM_COUNT + 1; ++i) { + update_columns.push_back(""); + } + EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); +} } // end anonymous namespace From 8e82cd7374cda9ef55f88504a94d31b06d7e1bd4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 12 Aug 2011 16:05:04 -0700 Subject: [PATCH 442/974] [1068] implemented some initial steps of the updater class - defined updater and related classes - implemented a simple constructor and updater finder --- src/lib/datasrc/client.h | 14 ++ src/lib/datasrc/database.cc | 29 +++ src/lib/datasrc/database.h | 66 ++++++- src/lib/datasrc/memory_datasrc.cc | 9 + src/lib/datasrc/memory_datasrc.h | 5 + src/lib/datasrc/tests/database_unittest.cc | 169 +++++++++++++----- .../datasrc/tests/memory_datasrc_unittest.cc | 6 +- src/lib/datasrc/zone.h | 14 +- 8 files changed, 263 insertions(+), 49 deletions(-) diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index 9fe6519532..9f2a445857 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -143,6 +143,20 @@ public: /// \param name A domain name for which the search is performed. /// \return A \c FindResult object enclosing the search result (see above). virtual FindResult findZone(const isc::dns::Name& name) const = 0; + + /// TBD + /// + /// We allow having a read-only data source. For such data source + /// this method will result in a NotImplemented exception. + /// + /// To avoid throwing the exception accidentally with a lazy + /// implementation, we still keep this method pure virtual without + /// an implementation. All derived classes must explicitly write the + /// definition of this method, even if it simply throws the NotImplemented + /// exception. + virtual ZoneUpdaterPtr startUpdateZone(const isc::dns::Name& name, + bool replace) + const = 0; }; } } diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index d2202cd43c..a470071c04 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -27,6 +27,7 @@ #include +using boost::shared_ptr; using isc::dns::Name; namespace isc { @@ -308,5 +309,33 @@ DatabaseClient::Finder::getClass() const { return isc::dns::RRClass::IN(); } +ZoneUpdaterPtr +DatabaseClient::startUpdateZone(const isc::dns::Name& name, + bool replace) const +{ + // TODO: create a dedicated accessor + + const std::pair zone(database_->startUpdateZone(name.toText(), + replace)); + if (!zone.first) { + return (ZoneUpdaterPtr()); + } + + // At this moment this one cannot anything except giving a finder. + return (ZoneUpdaterPtr(new DatabaseClient::Updater(database_, + zone.second))); +} + +DatabaseClient::Updater::Updater(shared_ptr database, + int zone_id) : + database_(database), zone_id_(zone_id), + finder_(new Finder::Finder(database_, zone_id_)) +{ +} + +ZoneFinder& +DatabaseClient::Updater::getFinder() { + return (*finder_); +} } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 5253e6072b..94d222103e 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -15,6 +15,8 @@ #ifndef __DATABASE_DATASRC_H #define __DATABASE_DATASRC_H +#include + #include namespace isc { @@ -52,6 +54,7 @@ public: * classes in polymorphic way. */ virtual ~DatabaseAccessor() { } + /** * \brief Retrieve a zone identifier * @@ -146,7 +149,46 @@ public: RDATA_COLUMN = 3 ///< Full text representation of the record's RDATA }; - /// The number of fields the columns array passed to getNextRecord should have + /// TBD + virtual std::pair startUpdateZone(const std::string& zone_name, + bool replace) = 0; + + /// TBD + virtual void commitUpdateZone() = 0; + + /// TBD + virtual void rollbackUpdateZone() = 0; + + /// TBD + /// This is "dumb"; doesn't inspect the content of columns. + /// The zone is the one specified in startUpdateZone(). + virtual void addRecordToZone(const std::vector& columns) = 0; + + /// TBD + /// + /// note: in dynamic update or IXFR, TTL may also be specified, but + /// we intentionally ignore that in this interface, because it's not + /// guaranteed that all records have the same TTL (unlike the RRset + /// assumption) and there can even be multiple records for the same name, + /// type and rdata with different TTLs. If we only delete one of them, + /// subsequent lookup will still return a positive answer, which would + /// be confusing. It's a higher layer's responsibility to check if + /// there is at least one record in the database that has the given + /// TTL. + virtual void deleteRecordInZone( + const std::vector& params) = 0; + + /// TBD + /// Compliant database should support the following columns: + /// name, rname, ttl, rdtype, sigtype, rdata + /// (even though their internal representation may be different). + static const size_t ADD_COLUMN_COUNT = 6; + + /// TBD + static const size_t DEL_PARAM_COUNT = 3; + + /// The number of fields the columns array passed to getNextRecord should + /// have. static const size_t COLUMN_COUNT = 4; /** @@ -291,6 +333,18 @@ public: boost::shared_ptr database_; const int zone_id_; }; + + class Updater : public ZoneUpdater { + public: + Updater(boost::shared_ptr database, int zone_id); + virtual ZoneFinder& getFinder(); + + private: + boost::shared_ptr database_; + const int zone_id_; + boost::scoped_ptr finder_; + }; + /** * \brief Find a zone in the database * @@ -307,6 +361,10 @@ public: */ virtual FindResult findZone(const isc::dns::Name& name) const; + /// TBD + virtual ZoneUpdaterPtr startUpdateZone(const isc::dns::Name& name, + bool replace) const; + private: /// \brief Our database. const boost::shared_ptr database_; @@ -315,4 +373,8 @@ private: } } -#endif +#endif // __DATABASE_DATASRC_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index d06cd9ba43..2d28478fb7 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -17,6 +17,8 @@ #include #include +#include + #include #include #include @@ -703,5 +705,12 @@ InMemoryClient::findZone(const isc::dns::Name& name) const { return (FindResult(impl_->zone_table.findZone(name).code, impl_->zone_table.findZone(name).zone)); } + +ZoneUpdaterPtr +InMemoryClient::startUpdateZone(const isc::dns::Name&, bool) const { + // TODO: once #1067 is merged, we should replace Unexpected with + // NotImplemented. + isc_throw(isc::Unexpected, "Update attempt on in memory data source"); +} } // end of namespace datasrc } // end of namespace dns diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 0234a916f8..7fd0b3daa6 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -258,6 +258,11 @@ public: /// For other details see \c DataSourceClient::findZone(). virtual FindResult findZone(const isc::dns::Name& name) const; + /// In-memory data source is read-only, so this derived method will + /// result in a NotImplemented (once merged) exception. + virtual ZoneUpdaterPtr startUpdateZone(const isc::dns::Name& name, + bool replace) const; + private: // TODO: Do we still need the PImpl if nobody should manipulate this class // directly any more (it should be handled through DataSourceClient)? diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index fcfcefe23c..380d2deb50 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -31,14 +31,21 @@ using namespace isc::datasrc; using namespace std; using namespace boost; using isc::dns::Name; +using isc::dns::RRType; +using isc::dns::RRTTL; namespace { +const int READONLY_ZONE_ID = 42; +const int WRITABLE_ZONE_ID = 4200; + /* * A virtual database database that pretends it contains single zone -- * example.org. */ class MockAccessor : public DatabaseAccessor { + typedef std::map > > + RECORDS; public: MockAccessor() : search_running_(false), database_name_("mock_database") @@ -46,9 +53,12 @@ public: fillData(); } - virtual std::pair getZone(const Name& name) const { - if (name == Name("example.org")) { - return (std::pair(true, 42)); + virtual pair getZone(const Name& name) const { + return (getZone(name.toText())); + } + pair getZone(const string& name) const { + if (name == "example.org.") { + return (std::pair(true, READONLY_ZONE_ID)); } else { return (std::pair(false, 0)); } @@ -72,16 +82,13 @@ public: // we're not aiming for efficiency in this test, simply // copy the relevant vector from records cur_record = 0; - if (zone_id == 42) { - if (records.count(name) > 0) { - cur_name = records.find(name)->second; - } else { - cur_name.clear(); - } + const RECORDS& cur_records = getRecords(zone_id); + if (cur_records.count(name) > 0) { + cur_name = cur_records.find(name)->second; } else { cur_name.clear(); } - }; + } virtual bool getNextRecord(std::string columns[], size_t column_count) { if (searched_name_ == "dsexception.in.getnext.") { @@ -119,7 +126,10 @@ public: return (database_name_); } private: - std::map > > records; + RECORDS readonly_records; + RECORDS update_records; + RECORDS empty_records; + // used as internal index for getNextRecord() size_t cur_record; // used as temporary storage after searchForRecord() and during @@ -137,6 +147,15 @@ private: const std::string database_name_; + const RECORDS& getRecords(int zone_id) const { + if (zone_id == READONLY_ZONE_ID) { + return (readonly_records); + } else if (zone_id == WRITABLE_ZONE_ID) { + return (update_records); + } + return (empty_records); + } + // Adds one record to the current name in the database // The actual data will not be added to 'records' until // addCurName() is called @@ -156,8 +175,8 @@ private: // to the actual fake database. This will clear cur_name, // so we can immediately start adding new records. void addCurName(const std::string& name) { - ASSERT_EQ(0, records.count(name)); - records[name] = cur_name; + ASSERT_EQ(0, readonly_records.count(name)); + readonly_records[name] = cur_name; cur_name.clear(); } @@ -264,10 +283,34 @@ private: addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("badsigtype.example.org."); } + + virtual pair startUpdateZone(const std::string& zone_name, + bool replace) + { + const pair zone_info = getZone(zone_name); + if (!zone_info.first) { + return (pair(false, 0)); + } + + // Prepare the record set for update. If replacing the existing one, + // we use an empty set; otherwise we use a writable copy of the + // original. + if (replace) { + update_records.clear(); + } else { + update_records = readonly_records; + } + + return (pair(true, WRITABLE_ZONE_ID)); + } + virtual void commitUpdateZone() {} + virtual void rollbackUpdateZone() {} + virtual void addRecordToZone(const vector&) {} + virtual void deleteRecordInZone(const vector&) {} }; class DatabaseClientTest : public ::testing::Test { -public: +protected: DatabaseClientTest() { createClient(); } @@ -285,6 +328,10 @@ public: shared_ptr client_; const std::string database_name_; + const std::vector empty_rdatas; // for NXRRSET/NXDOMAIN + std::vector expected_rdatas; + std::vector expected_sig_rdatas; + /** * Check the zone finder is a valid one and references the zone ID and * database available here. @@ -295,7 +342,7 @@ public: dynamic_pointer_cast(zone.zone_finder)); ASSERT_NE(shared_ptr(), finder) << "Wrong type of finder"; - EXPECT_EQ(42, finder->zone_id()); + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); EXPECT_EQ(current_database_, &finder->database()); } }; @@ -325,6 +372,17 @@ TEST_F(DatabaseClientTest, noAccessorException) { isc::InvalidParameter); } +TEST_F(DatabaseClientTest, startUpdate) { + // startUpdate will succeed only when there's an exact match zone. + + EXPECT_EQ(ZoneUpdaterPtr(), + client_->startUpdateZone(Name("example.com"), false)); + EXPECT_NE(ZoneUpdaterPtr(), + client_->startUpdateZone(Name("example.org"), false)); + EXPECT_EQ(ZoneUpdaterPtr(), + client_->startUpdateZone(Name("sub.example.org"), false)); +} + namespace { // checks if the given rrset matches the // given name, class, type and rdatas @@ -346,7 +404,7 @@ checkRRset(isc::dns::ConstRRsetPtr rrset, } void -doFindTest(shared_ptr finder, +doFindTest(ZoneFinder& finder, const isc::dns::Name& name, const isc::dns::RRType& type, const isc::dns::RRType& expected_type, @@ -356,15 +414,15 @@ doFindTest(shared_ptr finder, const std::vector& expected_sig_rdatas) { ZoneFinder::FindResult result = - finder->find(name, type, NULL, ZoneFinder::FIND_DEFAULT); + finder.find(name, type, NULL, ZoneFinder::FIND_DEFAULT); ASSERT_EQ(expected_result, result.code) << name << " " << type; if (expected_rdatas.size() > 0) { - checkRRset(result.rrset, name, finder->getClass(), + checkRRset(result.rrset, name, finder.getClass(), expected_type, expected_ttl, expected_rdatas); if (expected_sig_rdatas.size() > 0) { checkRRset(result.rrset->getRRsig(), name, - finder->getClass(), isc::dns::RRType::RRSIG(), + finder.getClass(), isc::dns::RRType::RRSIG(), expected_ttl, expected_sig_rdatas); } else { EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig()); @@ -380,15 +438,13 @@ TEST_F(DatabaseClientTest, find) { ASSERT_EQ(result::SUCCESS, zone.code); shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); - EXPECT_EQ(42, finder->zone_id()); + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); EXPECT_FALSE(current_database_->searchRunning()); - std::vector expected_rdatas; - std::vector expected_sig_rdatas; expected_rdatas.clear(); expected_sig_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); - doFindTest(finder, isc::dns::Name("www.example.org."), + doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -399,7 +455,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); expected_rdatas.push_back("192.0.2.2"); - doFindTest(finder, isc::dns::Name("www2.example.org."), + doFindTest(*finder, isc::dns::Name("www2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -410,7 +466,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("2001:db8::1"); expected_rdatas.push_back("2001:db8::2"); - doFindTest(finder, isc::dns::Name("www.example.org."), + doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -419,7 +475,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.clear(); expected_sig_rdatas.clear(); - doFindTest(finder, isc::dns::Name("www.example.org."), + doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, @@ -429,7 +485,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.clear(); expected_sig_rdatas.clear(); expected_rdatas.push_back("www.example.org."); - doFindTest(finder, isc::dns::Name("cname.example.org."), + doFindTest(*finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, @@ -439,7 +495,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.clear(); expected_sig_rdatas.clear(); expected_rdatas.push_back("www.example.org."); - doFindTest(finder, isc::dns::Name("cname.example.org."), + doFindTest(*finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -448,7 +504,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.clear(); expected_sig_rdatas.clear(); - doFindTest(finder, isc::dns::Name("doesnotexist.example.org."), + doFindTest(*finder, isc::dns::Name("doesnotexist.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, @@ -460,7 +516,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.push_back("192.0.2.1"); expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signed1.example.org."), + doFindTest(*finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -472,7 +528,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.push_back("2001:db8::1"); expected_rdatas.push_back("2001:db8::2"); expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signed1.example.org."), + doFindTest(*finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -481,7 +537,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.clear(); expected_sig_rdatas.clear(); - doFindTest(finder, isc::dns::Name("signed1.example.org."), + doFindTest(*finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, @@ -492,7 +548,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("www.example.org."); expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signedcname1.example.org."), + doFindTest(*finder, isc::dns::Name("signedcname1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, @@ -504,7 +560,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.push_back("192.0.2.1"); expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signed2.example.org."), + doFindTest(*finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -516,7 +572,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.push_back("2001:db8::2"); expected_rdatas.push_back("2001:db8::1"); expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signed2.example.org."), + doFindTest(*finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -525,7 +581,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas.clear(); expected_sig_rdatas.clear(); - doFindTest(finder, isc::dns::Name("signed2.example.org."), + doFindTest(*finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, @@ -536,7 +592,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("www.example.org."); expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signedcname2.example.org."), + doFindTest(*finder, isc::dns::Name("signedcname2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, @@ -548,7 +604,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("acnamesig1.example.org."), + doFindTest(*finder, isc::dns::Name("acnamesig1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -559,7 +615,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("acnamesig2.example.org."), + doFindTest(*finder, isc::dns::Name("acnamesig2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -570,7 +626,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("acnamesig3.example.org."), + doFindTest(*finder, isc::dns::Name("acnamesig3.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -581,7 +637,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); expected_rdatas.push_back("192.0.2.2"); - doFindTest(finder, isc::dns::Name("ttldiff1.example.org."), + doFindTest(*finder, isc::dns::Name("ttldiff1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(360), ZoneFinder::SUCCESS, @@ -592,14 +648,13 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); expected_rdatas.push_back("192.0.2.2"); - doFindTest(finder, isc::dns::Name("ttldiff2.example.org."), + doFindTest(*finder, isc::dns::Name("ttldiff2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(360), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_database_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), @@ -676,7 +731,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("badsigtype.example.org."), + doFindTest(*finder, isc::dns::Name("badsigtype.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -684,4 +739,30 @@ TEST_F(DatabaseClientTest, find) { EXPECT_FALSE(current_database_->searchRunning()); } +TEST_F(DatabaseClientTest, updaterFinder) { + ZoneUpdaterPtr updater = client_->startUpdateZone(Name("example.org"), + false); + ASSERT_TRUE(updater); + + // If this update isn't replacing the zone, the finder should work + // just like the normal find() case. + EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( + updater->getFinder()).zone_id()); + expected_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + doFindTest(updater->getFinder(), Name("www.example.org."), + RRType::A(), RRType::A(), RRTTL(3600), ZoneFinder::SUCCESS, + expected_rdatas, empty_rdatas); + + // When replacing the zone, the updater's finder shouldn't see anything + // in the zone until something is added. + updater = client_->startUpdateZone(Name("example.org"), true); + ASSERT_TRUE(updater); + EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( + updater->getFinder()).zone_id()); + doFindTest(updater->getFinder(), Name("www.example.org."), + RRType::A(), RRType::A(), RRTTL(3600), ZoneFinder::NXDOMAIN, + empty_rdatas, empty_rdatas); +} + } diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 22723fc76d..f00b3c0721 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -158,6 +158,11 @@ TEST_F(InMemoryClientTest, getZoneCount) { EXPECT_EQ(2, memory_client.getZoneCount()); } +TEST_F(InMemoryClientTest, startUpdateZone) { + EXPECT_THROW(memory_client.startUpdateZone(Name("example.org"), false), + isc::Unexpected); +} + // A helper callback of masterLoad() used in InMemoryZoneFinderTest. void setRRset(RRsetPtr rrset, vector::iterator& it) { @@ -1058,5 +1063,4 @@ TEST_F(InMemoryZoneFinderTest, getFileName) { EXPECT_EQ(TEST_DATA_DIR "/root.zone", zone_finder_.getFileName()); EXPECT_TRUE(rootzone.getFileName().empty()); } - } diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 0dacc5da55..46a1a1c3b9 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -207,8 +207,18 @@ typedef boost::shared_ptr ZoneFinderPtr; /// \brief A pointer-like type pointing to a \c ZoneFinder object. typedef boost::shared_ptr ConstZoneFinderPtr; -} -} +/// The base class to make updates to a single zone. +class ZoneUpdater { +public: + /// TBD + virtual ZoneFinder& getFinder() = 0; +}; + +/// \brief A pointer-like type pointing to a \c ZoneUpdater object. +typedef boost::shared_ptr ZoneUpdaterPtr; + +} // end of datasrc +} // end of isc #endif // __ZONE_H From a577b387b7e5c9c8afd371767fccc85009e84485 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 12 Aug 2011 16:27:57 -0700 Subject: [PATCH 443/974] [1068] intermediate editorial cleanup: change database/db, etc to accessor throughout the code --- src/lib/datasrc/database.cc | 58 ++-- src/lib/datasrc/database.h | 17 +- src/lib/datasrc/sqlite3_accessor.cc | 28 +- src/lib/datasrc/sqlite3_accessor.h | 6 +- src/lib/datasrc/tests/database_unittest.cc | 78 ++--- .../tests/sqlite3_accessor_unittest.cc | 292 +++++++++--------- 6 files changed, 243 insertions(+), 236 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index a470071c04..a8b789f3ae 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -34,10 +34,10 @@ namespace isc { namespace datasrc { DatabaseClient::DatabaseClient(boost::shared_ptr - database) : - database_(database) + accessor) : + accessor_(accessor) { - if (database_.get() == NULL) { + if (!accessor_) { isc_throw(isc::InvalidParameter, "No database provided to DatabaseClient"); } @@ -45,20 +45,20 @@ DatabaseClient::DatabaseClient(boost::shared_ptr DataSourceClient::FindResult DatabaseClient::findZone(const Name& name) const { - std::pair zone(database_->getZone(name)); + std::pair zone(accessor_->getZone(name)); // Try exact first if (zone.first) { return (FindResult(result::SUCCESS, - ZoneFinderPtr(new Finder(database_, + ZoneFinderPtr(new Finder(accessor_, zone.second)))); } // Than super domains // Start from 1, as 0 is covered above for (size_t i(1); i < name.getLabelCount(); ++i) { - zone = database_->getZone(name.split(i)); + zone = accessor_->getZone(name.split(i)); if (zone.first) { return (FindResult(result::PARTIALMATCH, - ZoneFinderPtr(new Finder(database_, + ZoneFinderPtr(new Finder(accessor_, zone.second)))); } } @@ -66,9 +66,9 @@ DatabaseClient::findZone(const Name& name) const { return (FindResult(result::NOTFOUND, ZoneFinderPtr())); } -DatabaseClient::Finder::Finder(boost::shared_ptr - database, int zone_id) : - database_(database), +DatabaseClient::Finder::Finder(boost::shared_ptr accessor, + int zone_id) : + accessor_(accessor), zone_id_(zone_id) { } @@ -177,13 +177,13 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, ZoneFinder::Result result_status = SUCCESS; RRsigStore sig_store; logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS) - .arg(database_->getDBName()).arg(name).arg(type); + .arg(accessor_->getDBName()).arg(name).arg(type); try { - database_->searchForRecords(zone_id_, name.toText()); + accessor_->searchForRecords(zone_id_, name.toText()); std::string columns[DatabaseAccessor::COLUMN_COUNT]; - while (database_->getNextRecord(columns, + while (accessor_->getNextRecord(columns, DatabaseAccessor::COLUMN_COUNT)) { if (!records_found) { records_found = true; @@ -215,7 +215,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseAccessor:: RDATA_COLUMN], - *database_); + *accessor_); } else if (cur_type == isc::dns::RRType::CNAME()) { // There should be no other data, so result_rrset should // be empty. @@ -226,7 +226,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseAccessor:: RDATA_COLUMN], - *database_); + *accessor_); result_status = CNAME; } else if (cur_type == isc::dns::RRType::RRSIG()) { // If we get signatures before we get the actual data, we @@ -257,20 +257,20 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } } catch (const DataSourceError& dse) { logger.error(DATASRC_DATABASE_FIND_ERROR) - .arg(database_->getDBName()).arg(dse.what()); + .arg(accessor_->getDBName()).arg(dse.what()); // call cleanup and rethrow - database_->resetSearch(); + accessor_->resetSearch(); throw; } catch (const isc::Exception& isce) { logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR) - .arg(database_->getDBName()).arg(isce.what()); + .arg(accessor_->getDBName()).arg(isce.what()); // cleanup, change it to a DataSourceError and rethrow - database_->resetSearch(); + accessor_->resetSearch(); isc_throw(DataSourceError, isce.what()); } catch (const std::exception& ex) { logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR) - .arg(database_->getDBName()).arg(ex.what()); - database_->resetSearch(); + .arg(accessor_->getDBName()).arg(ex.what()); + accessor_->resetSearch(); throw; } @@ -278,13 +278,13 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, if (records_found) { logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXRRSET) - .arg(database_->getDBName()).arg(name) + .arg(accessor_->getDBName()).arg(name) .arg(getClass()).arg(type); result_status = NXRRSET; } else { logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXDOMAIN) - .arg(database_->getDBName()).arg(name) + .arg(accessor_->getDBName()).arg(name) .arg(getClass()).arg(type); result_status = NXDOMAIN; } @@ -292,7 +292,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, sig_store.appendSignatures(result_rrset); logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_RRSET) - .arg(database_->getDBName()).arg(*result_rrset); + .arg(accessor_->getDBName()).arg(*result_rrset); } return (FindResult(result_status, result_rrset)); } @@ -315,21 +315,21 @@ DatabaseClient::startUpdateZone(const isc::dns::Name& name, { // TODO: create a dedicated accessor - const std::pair zone(database_->startUpdateZone(name.toText(), + const std::pair zone(accessor_->startUpdateZone(name.toText(), replace)); if (!zone.first) { return (ZoneUpdaterPtr()); } // At this moment this one cannot anything except giving a finder. - return (ZoneUpdaterPtr(new DatabaseClient::Updater(database_, + return (ZoneUpdaterPtr(new DatabaseClient::Updater(accessor_, zone.second))); } -DatabaseClient::Updater::Updater(shared_ptr database, +DatabaseClient::Updater::Updater(shared_ptr accessor, int zone_id) : - database_(database), zone_id_(zone_id), - finder_(new Finder::Finder(database_, zone_id_)) + accessor_(accessor), zone_id_(zone_id), + finder_(new Finder::Finder(accessor_, zone_id_)) { } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 94d222103e..2ff024403d 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -319,18 +319,19 @@ public: * applications shouldn't need it. */ int zone_id() const { return (zone_id_); } + /** - * \brief The database. + * \brief The database accessor. * - * This function provides the database stored inside as + * This function provides the database accessor stored inside as * passed to the constructor. This is meant for testing purposes and * normal applications shouldn't need it. */ - const DatabaseAccessor& database() const { - return (*database_); + const DatabaseAccessor& getAccessor() const { + return (*accessor_); } private: - boost::shared_ptr database_; + boost::shared_ptr accessor_; const int zone_id_; }; @@ -340,7 +341,7 @@ public: virtual ZoneFinder& getFinder(); private: - boost::shared_ptr database_; + boost::shared_ptr accessor_; const int zone_id_; boost::scoped_ptr finder_; }; @@ -366,8 +367,8 @@ public: bool replace) const; private: - /// \brief Our database. - const boost::shared_ptr database_; + /// \brief The accessor to our database. + const boost::shared_ptr accessor_; }; } diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 9b4d6c1e60..2959b9ae48 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -114,7 +114,7 @@ private: const char* const desc_; }; -SQLite3Database::SQLite3Database(const std::string& filename, +SQLite3Accessor::SQLite3Accessor(const std::string& filename, const isc::dns::RRClass& rrclass) : dbparameters_(new SQLite3Parameters), class_(rrclass.toText()), @@ -219,7 +219,7 @@ checkAndSetupSchema(Initializer* initializer) { } void -SQLite3Database::open(const std::string& name) { +SQLite3Accessor::open(const std::string& name) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNOPEN).arg(name); if (dbparameters_->db_ != NULL) { // There shouldn't be a way to trigger this anyway @@ -236,7 +236,7 @@ SQLite3Database::open(const std::string& name) { initializer.move(dbparameters_); } -SQLite3Database::~SQLite3Database() { +SQLite3Accessor::~SQLite3Accessor() { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DROPCONN); if (dbparameters_->db_ != NULL) { close(); @@ -245,7 +245,7 @@ SQLite3Database::~SQLite3Database() { } void -SQLite3Database::close(void) { +SQLite3Accessor::close(void) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNCLOSE); if (dbparameters_->db_ == NULL) { isc_throw(DataSourceError, @@ -263,12 +263,12 @@ SQLite3Database::close(void) { } std::pair -SQLite3Database::getZone(const isc::dns::Name& name) const { +SQLite3Accessor::getZone(const isc::dns::Name& name) const { return (getZone(name.toText())); } std::pair -SQLite3Database::getZone(const string& name) const { +SQLite3Accessor::getZone(const string& name) const { int rc; sqlite3_stmt* const stmt = dbparameters_->statements_[ZONE]; @@ -301,7 +301,7 @@ SQLite3Database::getZone(const string& name) const { } void -SQLite3Database::searchForRecords(int zone_id, const std::string& name) { +SQLite3Accessor::searchForRecords(int zone_id, const std::string& name) { resetSearch(); sqlite3_stmt* const stmt = dbparameters_->statements_[ANY]; @@ -349,7 +349,7 @@ convertToPlainChar(const unsigned char* ucp, } bool -SQLite3Database::getNextRecord(std::string columns[], size_t column_count) { +SQLite3Accessor::getNextRecord(std::string columns[], size_t column_count) { if (column_count != COLUMN_COUNT) { isc_throw(DataSourceError, "Datasource backend caller did not pass a column array " @@ -383,13 +383,13 @@ SQLite3Database::getNextRecord(std::string columns[], size_t column_count) { } void -SQLite3Database::resetSearch() { +SQLite3Accessor::resetSearch() { sqlite3_reset(dbparameters_->statements_[ANY]); sqlite3_clear_bindings(dbparameters_->statements_[ANY]); } pair -SQLite3Database::startUpdateZone(const string& zone_name, const bool replace) { +SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) { if (dbparameters_->updating_zone) { isc_throw(DataSourceError, "duplicate zone update on SQLite3 data source"); @@ -425,7 +425,7 @@ SQLite3Database::startUpdateZone(const string& zone_name, const bool replace) { } void -SQLite3Database::commitUpdateZone() { +SQLite3Accessor::commitUpdateZone() { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "committing zone update on SQLite3 " "data source without transaction"); @@ -438,7 +438,7 @@ SQLite3Database::commitUpdateZone() { } void -SQLite3Database::rollbackUpdateZone() { +SQLite3Accessor::rollbackUpdateZone() { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "rolling back zone update on SQLite3 " "data source without transaction"); @@ -480,7 +480,7 @@ doUpdate(SQLite3Parameters* dbparams, StatementID stmt_id, } void -SQLite3Database::addRecordToZone(const vector& columns) { +SQLite3Accessor::addRecordToZone(const vector& columns) { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "adding record to SQLite3 " "data source without transaction"); @@ -494,7 +494,7 @@ SQLite3Database::addRecordToZone(const vector& columns) { } void -SQLite3Database::deleteRecordInZone(const vector& params) { +SQLite3Accessor::deleteRecordInZone(const vector& params) { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "deleting record in SQLite3 " "data source without transaction"); diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 00c19f6787..bada46a32b 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -51,7 +51,7 @@ struct SQLite3Parameters; * According to the design, it doesn't interpret the data in any way, it just * provides unified access to the DB. */ -class SQLite3Database : public DatabaseAccessor { +class SQLite3Accessor : public DatabaseAccessor { public: /** * \brief Constructor @@ -66,14 +66,14 @@ public: * file can contain multiple classes of data, single database can * provide only one class). */ - SQLite3Database(const std::string& filename, + SQLite3Accessor(const std::string& filename, const isc::dns::RRClass& rrclass); /** * \brief Destructor * * Closes the database. */ - ~SQLite3Database(); + ~SQLite3Accessor(); /** * \brief Look up a zone * diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 380d2deb50..77bbe24c09 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -319,12 +319,12 @@ protected: * times per test. */ void createClient() { - current_database_ = new MockAccessor(); + current_accessor_ = new MockAccessor(); client_.reset(new DatabaseClient(shared_ptr( - current_database_))); + current_accessor_))); } // Will be deleted by client_, just keep the current value for comparison. - MockAccessor* current_database_; + MockAccessor* current_accessor_; shared_ptr client_; const std::string database_name_; @@ -343,7 +343,7 @@ protected: ASSERT_NE(shared_ptr(), finder) << "Wrong type of finder"; EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); - EXPECT_EQ(current_database_, &finder->database()); + EXPECT_EQ(current_accessor_, &finder->getAccessor()); } }; @@ -439,7 +439,7 @@ TEST_F(DatabaseClientTest, find) { shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -449,7 +449,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -460,7 +460,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -471,7 +471,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -480,7 +480,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -490,7 +490,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::CNAME, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -500,7 +500,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -509,7 +509,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -521,7 +521,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -533,7 +533,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -542,7 +542,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -553,7 +553,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::CNAME, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -565,7 +565,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -577,7 +577,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -586,7 +586,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -597,7 +597,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::CNAME, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); @@ -609,7 +609,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -620,7 +620,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -631,7 +631,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -642,7 +642,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(360), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); expected_rdatas.clear(); expected_sig_rdatas.clear(); @@ -653,76 +653,76 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(360), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badcname2.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badcname3.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badsig.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); // Trigger the hardcoded exceptions and see if find() has cleaned up EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), std::exception); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), std::exception); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); // This RRSIG has the wrong sigtype field, which should be // an error if we decide to keep using that field @@ -736,7 +736,7 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_database_->searchRunning()); + EXPECT_FALSE(current_accessor_->searchRunning()); } TEST_F(DatabaseClientTest, updaterFinder) { diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 5e0b83423c..669ac2d081 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -49,73 +49,73 @@ std::string SQLITE_DBFILE_NOTEXIST = TEST_DATA_DIR "/nodir/notexist"; // Opening works (the content is tested in different tests) TEST(SQLite3Open, common) { - EXPECT_NO_THROW(SQLite3Database db(SQLITE_DBFILE_EXAMPLE, - RRClass::IN())); + EXPECT_NO_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_EXAMPLE, + RRClass::IN())); } // The file can't be opened TEST(SQLite3Open, notExist) { - EXPECT_THROW(SQLite3Database db(SQLITE_DBFILE_NOTEXIST, - RRClass::IN()), SQLite3Error); + EXPECT_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_NOTEXIST, + RRClass::IN()), SQLite3Error); } // It rejects broken DB TEST(SQLite3Open, brokenDB) { - EXPECT_THROW(SQLite3Database db(SQLITE_DBFILE_BROKENDB, - RRClass::IN()), SQLite3Error); + EXPECT_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_BROKENDB, + RRClass::IN()), SQLite3Error); } // Test we can create the schema on the fly TEST(SQLite3Open, memoryDB) { - EXPECT_NO_THROW(SQLite3Database db(SQLITE_DBFILE_MEMORY, - RRClass::IN())); + EXPECT_NO_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_MEMORY, + RRClass::IN())); } // Test fixture for querying the db -class SQLite3Access : public ::testing::Test { +class SQLite3AccessorTest : public ::testing::Test { public: - SQLite3Access() { + SQLite3AccessorTest() { initAccessor(SQLITE_DBFILE_EXAMPLE, RRClass::IN()); } // So it can be re-created with different data void initAccessor(const std::string& filename, const RRClass& rrclass) { - db.reset(new SQLite3Database(filename, rrclass)); + accessor.reset(new SQLite3Accessor(filename, rrclass)); } - // The tested dbection - boost::scoped_ptr db; + // The tested accessor + boost::scoped_ptr accessor; }; // This zone exists in the data, so it should be found -TEST_F(SQLite3Access, getZone) { - std::pair result(db->getZone(Name("example.com"))); +TEST_F(SQLite3AccessorTest, getZone) { + std::pair result(accessor->getZone(Name("example.com"))); EXPECT_TRUE(result.first); EXPECT_EQ(1, result.second); } // But it should find only the zone, nothing below it -TEST_F(SQLite3Access, subZone) { - EXPECT_FALSE(db->getZone(Name("sub.example.com")).first); +TEST_F(SQLite3AccessorTest, subZone) { + EXPECT_FALSE(accessor->getZone(Name("sub.example.com")).first); } // This zone is not there at all -TEST_F(SQLite3Access, noZone) { - EXPECT_FALSE(db->getZone(Name("example.org")).first); +TEST_F(SQLite3AccessorTest, noZone) { + EXPECT_FALSE(accessor->getZone(Name("example.org")).first); } // This zone is there, but in different class -TEST_F(SQLite3Access, noClass) { +TEST_F(SQLite3AccessorTest, noClass) { initAccessor(SQLITE_DBFILE_EXAMPLE, RRClass::CH()); - EXPECT_FALSE(db->getZone(Name("example.com")).first); + EXPECT_FALSE(accessor->getZone(Name("example.com")).first); } TEST(SQLite3Open, getDBNameExample2) { - SQLite3Database db(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); - EXPECT_EQ(SQLITE_DBNAME_EXAMPLE2, db.getDBName()); + SQLite3Accessor accessor(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); + EXPECT_EQ(SQLITE_DBNAME_EXAMPLE2, accessor.getDBName()); } TEST(SQLite3Open, getDBNameExampleROOT) { - SQLite3Database db(SQLITE_DBFILE_EXAMPLE_ROOT, RRClass::IN()); - EXPECT_EQ(SQLITE_DBNAME_EXAMPLE_ROOT, db.getDBName()); + SQLite3Accessor accessor(SQLITE_DBFILE_EXAMPLE_ROOT, RRClass::IN()); + EXPECT_EQ(SQLITE_DBNAME_EXAMPLE_ROOT, accessor.getDBName()); } // Simple function to cound the number of records for @@ -133,8 +133,9 @@ checkRecordRow(const std::string columns[], EXPECT_EQ(field3, columns[3]); } -TEST_F(SQLite3Access, getRecords) { - const std::pair zone_info(db->getZone(Name("example.com"))); +TEST_F(SQLite3AccessorTest, getRecords) { + const std::pair zone_info( + accessor->getZone(Name("example.com"))); ASSERT_TRUE(zone_info.first); const int zone_id = zone_info.second; @@ -144,85 +145,85 @@ TEST_F(SQLite3Access, getRecords) { std::string columns[column_count]; // without search, getNext() should return false - EXPECT_FALSE(db->getNextRecord(columns, column_count)); + EXPECT_FALSE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "", "", "", ""); - db->searchForRecords(zone_id, "foo.bar."); - EXPECT_FALSE(db->getNextRecord(columns, column_count)); + accessor->searchForRecords(zone_id, "foo.bar."); + EXPECT_FALSE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "", "", "", ""); - db->searchForRecords(zone_id, ""); - EXPECT_FALSE(db->getNextRecord(columns, column_count)); + accessor->searchForRecords(zone_id, ""); + EXPECT_FALSE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "", "", "", ""); // Should error on a bad number of columns - EXPECT_THROW(db->getNextRecord(columns, 3), DataSourceError); - EXPECT_THROW(db->getNextRecord(columns, 5), DataSourceError); + EXPECT_THROW(accessor->getNextRecord(columns, 3), DataSourceError); + EXPECT_THROW(accessor->getNextRecord(columns, 5), DataSourceError); // now try some real searches - db->searchForRecords(zone_id, "foo.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + accessor->searchForRecords(zone_id, "foo.example.com."); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "CNAME", "3600", "", "cnametest.example.org."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "CNAME", "CNAME 5 3 3600 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "NSEC", "7200", "", "mail.example.com. CNAME RRSIG NSEC"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE"); - EXPECT_FALSE(db->getNextRecord(columns, column_count)); + EXPECT_FALSE(accessor->getNextRecord(columns, column_count)); // with no more records, the array should not have been modified checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE"); - db->searchForRecords(zone_id, "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + accessor->searchForRecords(zone_id, "example.com."); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "SOA", "3600", "", "master.example.com. admin.example.com. " "1234 3600 1800 2419200 7200"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "SOA", "SOA 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "NS", "1200", "", "dns01.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "NS", "3600", "", "dns02.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "NS", "1800", "", "dns03.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "NS", "NS 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "MX", "3600", "", "10 mail.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "MX", "3600", "", "20 mail.subzone.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "MX", "MX 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "NSEC", "7200", "", "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 2 7200 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "DNSKEY", "3600", "", "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4q NGV7W" "cTD0WEiuV7IjXgHE36fCmS9QsUxSSOV o1I/FMxI2PJVqTYHkX" "FBS7AzLGsQYMU7UjBZ SotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g" "0qcbW YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "DNSKEY", "3600", "", "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJ tbvzg" "62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV 4HQZJStJaZ+fHU5AwV" @@ -232,15 +233,15 @@ TEST_F(SQLite3Access, getRecords) { "fiHAxFHrkY3t3D5J R9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86" "Acv RyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGf oIK/aKwEN" "rsjcKZZj660b1M="); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " "4456 example.com. FAKEFAKEFAKEFAKE"); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + ASSERT_TRUE(accessor->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE"); - EXPECT_FALSE(db->getNextRecord(columns, column_count)); + EXPECT_FALSE(accessor->getNextRecord(columns, column_count)); // getnextrecord returning false should mean array is not altered checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " @@ -265,17 +266,17 @@ const char* const deleted_data[] = { "foo.bar.example.com.", "A", "192.0.2.1" }; -class SQLite3Update : public SQLite3Access { +class SQLite3Update : public SQLite3AccessorTest { protected: SQLite3Update() { ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR "/test.sqlite3 " TEST_DATA_BUILDDIR "/test.sqlite3.copied")); initAccessor(TEST_DATA_BUILDDIR "/test.sqlite3.copied", RRClass::IN()); - zone_id = db->getZone(Name("example.com")).second; - another_db.reset(new SQLite3Database( - TEST_DATA_BUILDDIR "/test.sqlite3.copied", - RRClass::IN())); + zone_id = accessor->getZone(Name("example.com")).second; + another_accessor.reset(new SQLite3Accessor( + TEST_DATA_BUILDDIR "/test.sqlite3.copied", + RRClass::IN())); expected_stored.push_back(common_expected_data); } @@ -287,17 +288,17 @@ protected: vector empty_stored; // indicate no corresponding data // Another accessor, emulating one running on a different process/thread - shared_ptr another_db; + shared_ptr another_accessor; }; void -checkRecords(SQLite3Database& db, int zone_id, const std::string& name, +checkRecords(SQLite3Accessor& accessor, int zone_id, const std::string& name, vector expected_rows) { - db.searchForRecords(zone_id, name); + accessor.searchForRecords(zone_id, name); std::string columns[DatabaseAccessor::COLUMN_COUNT]; vector::const_iterator it = expected_rows.begin(); - while (db.getNextRecord(columns, DatabaseAccessor::COLUMN_COUNT)) { + while (accessor.getNextRecord(columns, DatabaseAccessor::COLUMN_COUNT)) { ASSERT_TRUE(it != expected_rows.end()); checkRecordRow(columns, (*it)[3], (*it)[2], (*it)[4], (*it)[5]); ++it; @@ -309,64 +310,64 @@ TEST_F(SQLite3Update, emptyUpdate) { // If we do nothing between start and commit, the zone content // should be intact. - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); - zone_id = db->startUpdateZone("example.com.", false).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); - db->commitUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); + zone_id = accessor->startUpdateZone("example.com.", false).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); + accessor->commitUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, flushZone) { // With 'replace' being true startUpdateZone() will flush the existing // zone content. - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); - zone_id = db->startUpdateZone("example.com.", true).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); - db->commitUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); + zone_id = accessor->startUpdateZone("example.com.", true).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); + accessor->commitUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); } TEST_F(SQLite3Update, readWhileUpdate) { - zone_id = db->startUpdateZone("example.com.", true).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + zone_id = accessor->startUpdateZone("example.com.", true).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Until commit is done, the other accessor should see the old data - another_db->searchForRecords(zone_id, "foo.bar.example.com."); - checkRecords(*another_db, zone_id, "foo.bar.example.com.", + another_accessor->searchForRecords(zone_id, "foo.bar.example.com."); + checkRecords(*another_accessor, zone_id, "foo.bar.example.com.", expected_stored); // Once the changes are committed, the other accessor will see the new // data. - db->commitUpdateZone(); - another_db->searchForRecords(zone_id, "foo.bar.example.com."); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + accessor->commitUpdateZone(); + another_accessor->searchForRecords(zone_id, "foo.bar.example.com."); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); } TEST_F(SQLite3Update, rollback) { - zone_id = db->startUpdateZone("example.com.", true).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + zone_id = accessor->startUpdateZone("example.com.", true).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Rollback will revert the change made by startUpdateZone(, true). - db->rollbackUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + accessor->rollbackUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, commitConflict) { // Start reading the DB by another accessor. We should stop at a single // call to getNextRecord() to keep holding the lock. - another_db->searchForRecords(zone_id, "foo.example.com."); - EXPECT_TRUE(another_db->getNextRecord(get_columns, + another_accessor->searchForRecords(zone_id, "foo.example.com."); + EXPECT_TRUE(another_accessor->getNextRecord(get_columns, DatabaseAccessor::COLUMN_COUNT)); // Due to getNextRecord() above, the other accessor holds a DB lock, // which will prevent commit. - zone_id = db->startUpdateZone("example.com.", true).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); - EXPECT_THROW(db->commitUpdateZone(), DataSourceError); - db->rollbackUpdateZone(); // rollback should still succeed + zone_id = accessor->startUpdateZone("example.com.", true).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); + EXPECT_THROW(accessor->commitUpdateZone(), DataSourceError); + accessor->rollbackUpdateZone(); // rollback should still succeed - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, updateConflict) { @@ -374,54 +375,57 @@ TEST_F(SQLite3Update, updateConflict) { // update attempt. Note that these two accessors modify disjoint sets // of data; sqlite3 only has a coarse-grained lock so we cannot allow // these updates to run concurrently. - EXPECT_TRUE(another_db->startUpdateZone("sql1.example.com.", true).first); - EXPECT_THROW(db->startUpdateZone("example.com.", true), DataSourceError); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + EXPECT_TRUE(another_accessor->startUpdateZone("sql1.example.com.", + true).first); + EXPECT_THROW(accessor->startUpdateZone("example.com.", true), + DataSourceError); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, duplicateUpdate) { - db->startUpdateZone("example.com.", false); - EXPECT_THROW(db->startUpdateZone("example.com.", false), DataSourceError); + accessor->startUpdateZone("example.com.", false); + EXPECT_THROW(accessor->startUpdateZone("example.com.", false), + DataSourceError); } TEST_F(SQLite3Update, commitWithoutTransaction) { - EXPECT_THROW(db->commitUpdateZone(), DataSourceError); + EXPECT_THROW(accessor->commitUpdateZone(), DataSourceError); } TEST_F(SQLite3Update, rollbackWithoutTransaction) { - EXPECT_THROW(db->rollbackUpdateZone(), DataSourceError); + EXPECT_THROW(accessor->rollbackUpdateZone(), DataSourceError); } TEST_F(SQLite3Update, addRecord) { // Before update, there should be no record for this name - checkRecords(*db, zone_id, "newdata.example.com.", empty_stored); + checkRecords(*accessor, zone_id, "newdata.example.com.", empty_stored); - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; update_columns.assign(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT); - db->addRecordToZone(update_columns); + accessor->addRecordToZone(update_columns); expected_stored.clear(); expected_stored.push_back(new_data); - checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored); // Commit the change, and confirm the new data is still there. - db->commitUpdateZone(); - checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); + accessor->commitUpdateZone(); + checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored); } TEST_F(SQLite3Update, addThenRollback) { - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; update_columns.assign(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT); - db->addRecordToZone(update_columns); + accessor->addRecordToZone(update_columns); expected_stored.clear(); expected_stored.push_back(new_data); - checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored); - db->rollbackUpdateZone(); - checkRecords(*db, zone_id, "newdata.example.com.", empty_stored); + accessor->rollbackUpdateZone(); + checkRecords(*accessor, zone_id, "newdata.example.com.", empty_stored); } TEST_F(SQLite3Update, duplicateAdd) { @@ -431,103 +435,105 @@ TEST_F(SQLite3Update, duplicateAdd) { }; expected_stored.clear(); expected_stored.push_back(dup_data); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); // Adding exactly the same data. As this backend is "dumb", another // row of the same content will be inserted. update_columns.assign(dup_data, dup_data + DatabaseAccessor::ADD_COLUMN_COUNT); - zone_id = db->startUpdateZone("example.com.", false).second; - db->addRecordToZone(update_columns); + zone_id = accessor->startUpdateZone("example.com.", false).second; + accessor->addRecordToZone(update_columns); expected_stored.push_back(dup_data); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, invalidAdd) { // An attempt of add before an explicit start of transaction - EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); // Short column vector update_columns.clear(); - zone_id = db->startUpdateZone("example.com.", false).second; - EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); + zone_id = accessor->startUpdateZone("example.com.", false).second; + EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); // Too many columns for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT + 1; ++i) { update_columns.push_back(""); } - EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); } TEST_F(SQLite3Update, deleteRecord) { - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Commit the change, and confirm the deleted data still isn't there. - db->commitUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + accessor->commitUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); } TEST_F(SQLite3Update, deleteThenRollback) { - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Rollback the change, and confirm the data still exists. - db->rollbackUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + accessor->rollbackUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, deleteNonexistent) { - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); // Replace the name with a non existent one, then try to delete it. // nothing should happen. update_columns[0] = "no-such-name.example.com."; - checkRecords(*db, zone_id, "no-such-name.example.com.", empty_stored); - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "no-such-name.example.com.", empty_stored); + checkRecords(*accessor, zone_id, "no-such-name.example.com.", + empty_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "no-such-name.example.com.", + empty_stored); // Name exists but the RR type is different. Delete attempt shouldn't // delete only by name. update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); update_columns[1] = "AAAA"; - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); // Similar to the previous case, but RDATA is different. update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); update_columns[2] = "192.0.2.2"; - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, invalidDelete) { // An attempt of delete before an explicit start of transaction - EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); // Short column vector update_columns.clear(); - zone_id = db->startUpdateZone("example.com.", false).second; - EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); + zone_id = accessor->startUpdateZone("example.com.", false).second; + EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); // Too many parameters for (int i = 0; i < DatabaseAccessor::DEL_PARAM_COUNT + 1; ++i) { update_columns.push_back(""); } - EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); } } // end anonymous namespace From d71b7da05d3e1a82047e35c2720c759bdc0fb44f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 12 Aug 2011 18:55:58 -0700 Subject: [PATCH 444/974] [1068] supported commit and (implicit) rollback operation. added some log messages. to make the log/exception messages useful, also added an RRClass parameter to the Client constructor. --- src/lib/datasrc/database.cc | 55 +++++++++++-- src/lib/datasrc/database.h | 21 ++++- src/lib/datasrc/datasrc_messages.mes | 22 +++++ src/lib/datasrc/tests/database_unittest.cc | 94 ++++++++++++++++++---- src/lib/datasrc/zone.h | 11 +++ 5 files changed, 179 insertions(+), 24 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index a8b789f3ae..e68173eb7c 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -12,12 +12,14 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include #include #include #include #include +#include #include #include #include @@ -27,15 +29,18 @@ #include +using namespace std; using boost::shared_ptr; using isc::dns::Name; +using isc::dns::RRClass; namespace isc { namespace datasrc { -DatabaseClient::DatabaseClient(boost::shared_ptr +DatabaseClient::DatabaseClient(RRClass rrclass, + boost::shared_ptr accessor) : - accessor_(accessor) + rrclass_(rrclass), accessor_(accessor) { if (!accessor_) { isc_throw(isc::InvalidParameter, @@ -321,21 +326,55 @@ DatabaseClient::startUpdateZone(const isc::dns::Name& name, return (ZoneUpdaterPtr()); } - // At this moment this one cannot anything except giving a finder. - return (ZoneUpdaterPtr(new DatabaseClient::Updater(accessor_, - zone.second))); + return (ZoneUpdaterPtr(new Updater(accessor_, zone.second, + name.toText(), rrclass_.toText()))); } DatabaseClient::Updater::Updater(shared_ptr accessor, - int zone_id) : - accessor_(accessor), zone_id_(zone_id), - finder_(new Finder::Finder(accessor_, zone_id_)) + int zone_id, const string& zone_name, + const string& class_name) : + committed_(false), accessor_(accessor), zone_id_(zone_id), + db_name_(accessor->getDBName()), zone_name_(zone_name), + class_name_(class_name), + finder_(new Finder(accessor_, zone_id_)) { + logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED) + .arg(zone_name_).arg(class_name_).arg(db_name_); +} + +DatabaseClient::Updater::~Updater() { + if (!committed_) { + accessor_->rollbackUpdateZone(); + logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK) + .arg(zone_name_).arg(class_name_).arg(db_name_); + } + + logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED) + .arg(zone_name_).arg(class_name_).arg(db_name_); } ZoneFinder& DatabaseClient::Updater::getFinder() { return (*finder_); } + +void +DatabaseClient::Updater::commit() { + if (committed_) { + isc_throw(DataSourceError, "Duplicate commit attempt for " + << zone_name_ << "/" << class_name_ << " on " + << db_name_); + } + accessor_->commitUpdateZone(); + + // We release the accessor immediately after commit is completed so that + // we don't hold the possible internal resource any longer. + accessor_.reset(); + + committed_ = true; + + logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_COMMIT) + .arg(zone_name_).arg(class_name_).arg(db_name_); +} } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 2ff024403d..8e5d7e9783 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -15,8 +15,12 @@ #ifndef __DATABASE_DATASRC_H #define __DATABASE_DATASRC_H +#include + #include +#include + #include namespace isc { @@ -230,11 +234,14 @@ public: * \exception isc::InvalidParameter if database is NULL. It might throw * standard allocation exception as well, but doesn't throw anything else. * + * \param rrclass The RR class of the zones that this client will handle. * \param database The database to use to get data. As the parameter * suggests, the client takes ownership of the database and will * delete it when itself deleted. */ - DatabaseClient(boost::shared_ptr database); + DatabaseClient(isc::dns::RRClass rrclass, + boost::shared_ptr database); + /** * \brief Corresponding ZoneFinder implementation * @@ -337,12 +344,19 @@ public: class Updater : public ZoneUpdater { public: - Updater(boost::shared_ptr database, int zone_id); + Updater(boost::shared_ptr database, int zone_id, + const std::string& zone_name, const std::string& class_name); + ~Updater(); virtual ZoneFinder& getFinder(); + virtual void commit(); private: + bool committed_; boost::shared_ptr accessor_; const int zone_id_; + std::string db_name_; + std::string zone_name_; + std::string class_name_; boost::scoped_ptr finder_; }; @@ -367,6 +381,9 @@ public: bool replace) const; private: + /// \brief The RR class that this client handles. + const isc::dns::RRClass rrclass_; + /// \brief The accessor to our database. const boost::shared_ptr accessor_; }; diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 6af4fe6678..f1525638fd 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -549,3 +549,25 @@ data source. % DATASRC_UNEXPECTED_QUERY_STATE unexpected query state This indicates a programming error. An internal task of unknown type was generated. + +% DATASRC_DATABASE_UPDATER_CREATED zone updater created for '%1/%2' on %3 +Debug information. A zone updater object is created to make updates to +the shown zone on the shown backend database. + +% DATASRC_DATABASE_UPDATER_DESTROYED zone updater destroyed for '%1/%2' on %3 +Debug information. A zone updater object is destroyed, either successfully +or after failure of, making updates to the shown zone on the shown backend +database. + +%DATASRC_DATABASE_UPDATER_ROLLBACK zone updates roll-backed for '%1/%2' on %3 +A zone updater is being destroyed without committing the changes. +This would typically mean the update attempt was aborted due to some +error, but may also be a bug of the application that forgets committing +the changes. The intermediate changes made through the updater won't +be applied to the underlying database. The zone name, its class, and +the underlying database name are shown in the log message. + +% DATASRC_DATABASE_UPDATER_COMMIT updates committed for '%1/%2' on %3 +Debug information. A set of updates to a zone has been successfully +committed to the corresponding database backend. The zone name, +its class and the database name are printed. diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 77bbe24c09..fad4cba181 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -32,6 +32,7 @@ using namespace std; using namespace boost; using isc::dns::Name; using isc::dns::RRType; +using isc::dns::RRClass; using isc::dns::RRTTL; namespace { @@ -47,8 +48,8 @@ class MockAccessor : public DatabaseAccessor { typedef std::map > > RECORDS; public: - MockAccessor() : search_running_(false), - database_name_("mock_database") + MockAccessor() : search_running_(false), rollbacked_(false), + database_name_("mock_database") { fillData(); } @@ -112,16 +113,20 @@ public: resetSearch(); return (false); } - }; + } virtual void resetSearch() { search_running_ = false; - }; + } bool searchRunning() const { return (search_running_); } + bool isRollbacked() const { + return (rollbacked_); + } + virtual const std::string& getDBName() const { return (database_name_); } @@ -141,6 +146,10 @@ private: // when it encounters an error bool search_running_; + // Whether rollback operation has been performed for the database. + // Not useful except for purely testing purpose. + bool rollbacked_; + // We store the name passed to searchForRecords, so we can // hardcode some exceptions into getNextRecord std::string searched_name_; @@ -303,15 +312,19 @@ private: return (pair(true, WRITABLE_ZONE_ID)); } - virtual void commitUpdateZone() {} - virtual void rollbackUpdateZone() {} + virtual void commitUpdateZone() { + readonly_records = update_records; + } + virtual void rollbackUpdateZone() { + rollbacked_ = true; + } virtual void addRecordToZone(const vector&) {} virtual void deleteRecordInZone(const vector&) {} }; class DatabaseClientTest : public ::testing::Test { protected: - DatabaseClientTest() { + DatabaseClientTest() : qname("www.example.org"), qtype("A") { createClient(); } /* @@ -320,14 +333,19 @@ protected: */ void createClient() { current_accessor_ = new MockAccessor(); - client_.reset(new DatabaseClient(shared_ptr( - current_accessor_))); + client_.reset(new DatabaseClient(RRClass::IN(), + shared_ptr( + current_accessor_))); } // Will be deleted by client_, just keep the current value for comparison. MockAccessor* current_accessor_; shared_ptr client_; const std::string database_name_; + const Name qname; // commonly used name to be found + const RRType qtype; // commonly used RR type with qname + + ZoneUpdaterPtr updater; const std::vector empty_rdatas; // for NXRRSET/NXDOMAIN std::vector expected_rdatas; std::vector expected_sig_rdatas; @@ -366,9 +384,7 @@ TEST_F(DatabaseClientTest, superZone) { } TEST_F(DatabaseClientTest, noAccessorException) { - // We need a dummy variable here; some compiler would regard it a mere - // declaration instead of an instantiation and make the test fail. - EXPECT_THROW(DatabaseClient dummy((shared_ptr())), + EXPECT_THROW(DatabaseClient(RRClass::IN(), shared_ptr()), isc::InvalidParameter); } @@ -740,8 +756,7 @@ TEST_F(DatabaseClientTest, find) { } TEST_F(DatabaseClientTest, updaterFinder) { - ZoneUpdaterPtr updater = client_->startUpdateZone(Name("example.org"), - false); + updater = client_->startUpdateZone(Name("example.org"), false); ASSERT_TRUE(updater); // If this update isn't replacing the zone, the finder should work @@ -765,4 +780,55 @@ TEST_F(DatabaseClientTest, updaterFinder) { empty_rdatas, empty_rdatas); } +TEST_F(DatabaseClientTest, flushZone) { + // A simple update case: flush the entire zone + + // Before update, the name exists. + ZoneFinderPtr finder = client_->findZone(Name("example.org")).zone_finder; + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname, qtype).code); + + // start update in the replace mode. the normal finder should still + // be able to see the record, but the updater's finder shouldn't. + updater = client_->startUpdateZone(Name("example.org"), true); + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname, qtype).code); + EXPECT_EQ(ZoneFinder::NXDOMAIN, + updater->getFinder().find(qname, qtype).code); + + // commit the update. now the normal finder shouldn't see it. + updater->commit(); + EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(qname, qtype).code); + + // Check rollback wasn't accidentally performed. + EXPECT_FALSE(current_accessor_->isRollbacked()); +} + +TEST_F(DatabaseClientTest, updateCancel) { + // similar to the previous test, but destruct the updater before commit. + + ZoneFinderPtr finder = client_->findZone(Name("example.org")).zone_finder; + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname, qtype).code); + + updater = client_->startUpdateZone(Name("example.org"), true); + EXPECT_EQ(ZoneFinder::NXDOMAIN, + updater->getFinder().find(qname, qtype).code); + // DB should not have been rolled back yet. + EXPECT_FALSE(current_accessor_->isRollbacked()); + updater.reset(); // destruct without commit + + // reset() should have triggered rollback (although it doesn't affect + // anything to the mock accessor implementation except for the result of + // isRollbacked()) + EXPECT_TRUE(current_accessor_->isRollbacked()); + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname, qtype).code); +} + +TEST_F(DatabaseClientTest, duplicateCommit) { + // duplicate commit. should result in exception. + updater = client_->startUpdateZone(Name("example.org"), true); + updater->commit(); + EXPECT_THROW(updater->commit(), DataSourceError); +} + +// add/delete after commit. should error + } diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 46a1a1c3b9..066865f711 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -209,9 +209,20 @@ typedef boost::shared_ptr ConstZoneFinderPtr; /// The base class to make updates to a single zone. class ZoneUpdater { +protected: + ZoneUpdater() {} + public: + virtual ~ZoneUpdater() {} + /// TBD virtual ZoneFinder& getFinder() = 0; + + /// TBD + /// + /// This operation can only be performed at most once. A duplicate call + /// must result in a DatasourceError exception. + virtual void commit() = 0; }; /// \brief A pointer-like type pointing to a \c ZoneUpdater object. From 2f49e3eb0ddf31d601184b516b7f44ab4ea6eece Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 13 Aug 2011 13:56:28 +0200 Subject: [PATCH 445/974] [801] Notes about limitations --- src/bin/bind10/creatorapi.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/creatorapi.txt b/src/bin/bind10/creatorapi.txt index fd6be31a2d..c23d907f9c 100644 --- a/src/bin/bind10/creatorapi.txt +++ b/src/bin/bind10/creatorapi.txt @@ -85,7 +85,9 @@ The commands (IP address, address family, port) and how to allow sharing. Sharing would be one of: - None - - Same kind of application + - Same kind of application (however, it is not entirely clear what + this means, in case it won't work out intuitively, we'll need to + define it somehow) - Any kind of application And a kind of application would be provided, to decide if the sharing is possible (eg. if auth allows sharing with the same kind and something else @@ -113,7 +115,9 @@ Known limitations Currently the socket creator doesn't support specifying any socket options. If it turns out there are any options that need to be set before bind(), we'll need to extend it (and extend the protocol as -well). +well). If we want to support them, we'll have to solve a possible +conflict (what to do when two applications request the same socket and +want to share it, but want different options). The current socket creator doesn't know raw sockets, but if they are needed, it should be easy to add. From 978ae99ac4aa211ba4ba960f56bb6cdd84b648ae Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 13 Aug 2011 14:37:14 +0200 Subject: [PATCH 446/974] [1064] Tests for GLUE_OK mode It should just go trough NS delegation points (but not trough DNAME ones). --- src/lib/datasrc/tests/database_unittest.cc | 64 +++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f4b5d0948c..2a1cb3c362 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -390,11 +390,12 @@ doFindTest(shared_ptr finder, ZoneFinder::Result expected_result, const std::vector& expected_rdatas, const std::vector& expected_sig_rdatas, - const isc::dns::Name& expected_name = isc::dns::Name::ROOT_NAME()) + const isc::dns::Name& expected_name = isc::dns::Name::ROOT_NAME(), + const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT) { SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText()); ZoneFinder::FindResult result = - finder->find(name, type, NULL, ZoneFinder::FIND_DEFAULT); + finder->find(name, type, NULL, options); ASSERT_EQ(expected_result, result.code) << name << " " << type; if (expected_rdatas.size() > 0) { checkRRset(result.rrset, expected_name != Name(".") ? expected_name : @@ -839,6 +840,65 @@ TEST_F(DatabaseClientTest, find) { ZoneFinder::FIND_DEFAULT), DataSourceError); EXPECT_FALSE(current_database_->searchRunning()); + + // Glue-OK mode. Just go trough NS delegations down (but not trough + // DNAME) and pretend it is not there. + { + SCOPED_TRACE("Glue OK"); + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas, + expected_sig_rdatas, + isc::dns::Name("ns.delegation.example.org."), + ZoneFinder::FIND_GLUE_OK); + doFindTest(finder, isc::dns::Name("nothere.delegation.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, + expected_rdatas, expected_sig_rdatas, + isc::dns::Name("nothere.delegation.example.org."), + ZoneFinder::FIND_GLUE_OK); + expected_rdatas.push_back("192.0.2.1"); + doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, + expected_sig_rdatas, + isc::dns::Name("ns.delegation.example.org."), + ZoneFinder::FIND_GLUE_OK); + expected_rdatas.clear(); + expected_rdatas.push_back("ns.example.com."); + expected_rdatas.push_back("ns.delegation.example.org."); + expected_sig_rdatas.clear(); + expected_sig_rdatas.push_back("NS 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. " + "FAKEFAKEFAKE"); + // When we request the NS, it should be SUCCESS, not DELEGATION + // (different in GLUE_OK) + doFindTest(finder, isc::dns::Name("delegation.example.org."), + isc::dns::RRType::NS(), isc::dns::RRType::NS(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, + expected_sig_rdatas, isc::dns::Name("delegation.example.org."), + ZoneFinder::FIND_GLUE_OK); + expected_rdatas.clear(); + expected_rdatas.push_back("dname.example.com."); + expected_sig_rdatas.clear(); + expected_sig_rdatas.push_back("DNAME 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. " + "FAKEFAKEFAKE"); + doFindTest(finder, isc::dns::Name("below.dname.example.org."), + isc::dns::RRType::A(), isc::dns::RRType::DNAME(), + isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas, + expected_sig_rdatas, isc::dns::Name("dname.example.org."), + ZoneFinder::FIND_GLUE_OK); + EXPECT_FALSE(current_database_->searchRunning()); + doFindTest(finder, isc::dns::Name("below.dname.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), + isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas, + expected_sig_rdatas, isc::dns::Name("dname.example.org."), + ZoneFinder::FIND_GLUE_OK); + EXPECT_FALSE(current_database_->searchRunning()); + } // End of GLUE_OK } TEST_F(DatabaseClientTest, getOrigin) { From b9f87e9332895be6915e2f2960a2e921375e8e7f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 13 Aug 2011 14:54:57 +0200 Subject: [PATCH 447/974] [1064] Implement GLUE_OK mode It just turns off the flag for the getRRset method when the mode is on, to ignore it on the way. --- src/lib/datasrc/database.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 6afd3dce85..8ed49768a7 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -284,11 +284,12 @@ ZoneFinder::FindResult DatabaseClient::Finder::find(const isc::dns::Name& name, const isc::dns::RRType& type, isc::dns::RRsetList*, - const FindOptions) + const FindOptions options) { // This variable is used to determine the difference between // NXDOMAIN and NXRRSET bool records_found = false; + bool glue_ok(options & FIND_GLUE_OK); isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; std::pair found; @@ -307,7 +308,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, Name superdomain(name.split(i)); // Look if there's NS or DNAME (but ignore the NS in origin) found = getRRset(superdomain, NULL, false, true, - i != removeLabels); + i != removeLabels && !glue_ok); if (found.second) { // We found something redirecting somewhere else // (it can be only NS or DNAME here) @@ -326,10 +327,11 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Try getting the final result and extract it // It is special if there's a CNAME or NS, DNAME is ignored here // And we don't consider the NS in origin - found = getRRset(name, &type, true, false, name != origin); + found = getRRset(name, &type, true, false, + name != origin && !glue_ok); records_found = found.first; result_rrset = found.second; - if (result_rrset && name != origin && + if (result_rrset && name != origin && !glue_ok && result_rrset->getType() == isc::dns::RRType::NS()) { result_status = DELEGATION; } else if (result_rrset && type != isc::dns::RRType::CNAME() && From 7e89c625c5d12b5816c857d0c0910922f8803f82 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 13 Aug 2011 15:30:38 +0200 Subject: [PATCH 448/974] [1065] Tests for empty domain --- src/lib/datasrc/tests/database_unittest.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index fcfcefe23c..0496081707 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -263,6 +263,10 @@ private: addRecord("A", "3600", "", "192.0.2.1"); addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("badsigtype.example.org."); + + // This is because of empty domain test + addRecord("A", "3600", "", "192.0.2.1"); + addCurName("a.b.example.org."); } }; @@ -682,6 +686,15 @@ TEST_F(DatabaseClientTest, find) { ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); EXPECT_FALSE(current_database_->searchRunning()); + + // Check empty domain + expected_rdatas.clear(); + expected_sig_rdatas.clear(); + // This domain doesn't exist, but a subdomain of it does. + // Therefore we should pretend the domain is there, but contains no RRsets + doFindTest(finder, isc::dns::Name("b.example.org."), isc::dns::RRType::A(), + isc::dns::RRType::A(), isc::dns::RRTTL(3600), + ZoneFinder::NXRRSET, expected_rdatas, expected_sig_rdatas); } } From 59b545e90d30444a97c8e925569d240c819d42b4 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 13 Aug 2011 17:05:20 +0200 Subject: [PATCH 449/974] [1065] Support for subdomain match Only in the interface and test implementation for now. The SQLite version just ignores the parameter for now, will be done soon in this branch. --- src/lib/datasrc/database.h | 8 +++++- src/lib/datasrc/sqlite3_accessor.cc | 2 +- src/lib/datasrc/sqlite3_accessor.h | 4 ++- src/lib/datasrc/tests/database_unittest.cc | 33 +++++++++++++++++----- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 5253e6072b..187f5b18d1 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -84,8 +84,14 @@ public: * * \param zone_id The zone to search in, as returned by getZone() * \param name The name of the records to find + * \param subdomains If set to true, match subdomains of name instead + * of name itself. It is used to find empty domains and match + * wildcards. + * \todo Should we return the name as well? If we search for subdomains + * it might be useful (and needed in case of wildcard). */ - virtual void searchForRecords(int zone_id, const std::string& name) = 0; + virtual void searchForRecords(int zone_id, const std::string& name, + bool subdomains = false) = 0; /** * \brief Retrieves the next record from the search started with searchForRecords() diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 817d53087f..e66aff4b7d 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -323,7 +323,7 @@ SQLite3Database::getZone(const isc::dns::Name& name) const { } void -SQLite3Database::searchForRecords(int zone_id, const std::string& name) { +SQLite3Database::searchForRecords(int zone_id, const std::string& name, bool) { resetSearch(); if (sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id) != SQLITE_OK) { isc_throw(DataSourceError, diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 4c2ec8bfd3..a7f9bb6f83 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -100,8 +100,10 @@ public: * * \param zone_id The zone to seach in, as returned by getZone() * \param name The name to find records for + * \param subdomains Match subdomains instead of the name. */ - virtual void searchForRecords(int zone_id, const std::string& name); + virtual void searchForRecords(int zone_id, const std::string& name, + bool subdomains = false); /** * \brief Retrieve the next record from the search started with diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 0496081707..e6a208e7e6 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -54,7 +54,9 @@ public: } } - virtual void searchForRecords(int zone_id, const std::string& name) { + virtual void searchForRecords(int zone_id, const std::string& name, + bool subdomains) + { search_running_ = true; // 'hardcoded' name to trigger exceptions (for testing @@ -69,14 +71,29 @@ public: } searched_name_ = name; - // we're not aiming for efficiency in this test, simply - // copy the relevant vector from records cur_record = 0; if (zone_id == 42) { - if (records.count(name) > 0) { - cur_name = records.find(name)->second; - } else { + if (subdomains) { cur_name.clear(); + // Just walk everything and check if it is a subdomain. + // If it is, just copy all data from there. + for (Domains::iterator i(records.begin()); i != records.end(); + ++ i) { + Name local(i->first); + if (local.compare(isc::dns::Name(name)).getRelation() == + isc::dns::NameComparisonResult::SUBDOMAIN) { + cur_name.insert(cur_name.end(), i->second.begin(), + i->second.end()); + } + } + } else { + if (records.count(name) > 0) { + // we're not aiming for efficiency in this test, simply + // copy the relevant vector from records + cur_name = records.find(name)->second; + } else { + cur_name.clear(); + } } } else { cur_name.clear(); @@ -119,7 +136,9 @@ public: return (database_name_); } private: - std::map > > records; + typedef std::map > > + Domains; + Domains records; // used as internal index for getNextRecord() size_t cur_record; // used as temporary storage after searchForRecord() and during From 9882d600d0bbbc115671b12646e690ccddbf5348 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 13 Aug 2011 17:17:09 +0200 Subject: [PATCH 450/974] [1065] Implement empty nonterminal in databases When there's a subdomain, we fake we found at RRset in this domain in the right place. --- src/lib/datasrc/database.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index d2202cd43c..70ce62f8b6 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -254,6 +254,17 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, RDATA_COLUMN]); } } + if (!result_rrset && !records_found) { + // Nothing lives here. But check if something lives below this + // domain and if so, pretend something is here as well. + database_->searchForRecords(zone_id_, name.toText(), true); + if (database_->getNextRecord(columns, + DatabaseAccessor::COLUMN_COUNT)) { + records_found = true; + // We don't consume everything, so get rid of the rest + database_->resetSearch(); + } + } } catch (const DataSourceError& dse) { logger.error(DATASRC_DATABASE_FIND_ERROR) .arg(database_->getDBName()).arg(dse.what()); From 0c9db8bbeb7187218a5b47d82df18e38128d06a3 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 13 Aug 2011 17:42:41 +0200 Subject: [PATCH 451/974] [1065] Tests for subdomain match in SQLite Both test where it should match and test where it should not. --- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 097c821330..9a7a41d9ed 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -240,6 +240,16 @@ TEST_F(SQLite3Access, getRecords) { checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE"); + + // Try searching for subdomain + // There's foo.bar.example.com in the data + db->searchForRecords(zone_id, "bar.example.com.", true); + EXPECT_TRUE(db->getNextRecord(columns, column_count)); + checkRecordRow(columns, "A", "3600", "", "192.0.2.1"); + EXPECT_FALSE(db->getNextRecord(columns, column_count)); + // But we shouldn't match mix.example.com here + db->searchForRecords(zone_id, "ix.example.com.", true); + EXPECT_FALSE(db->getNextRecord(columns, column_count)); } } // end anonymous namespace From 39a0a5c65d0802f40ab428474b1e6d981a91fbce Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sat, 13 Aug 2011 17:56:53 +0200 Subject: [PATCH 452/974] [1065] Subdomain match for SQLite3 By simple WHERE name LIKE %.domain statement and switching between them. --- src/lib/datasrc/sqlite3_accessor.cc | 34 ++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index e66aff4b7d..ed929caaae 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -25,7 +25,8 @@ namespace datasrc { struct SQLite3Parameters { SQLite3Parameters() : db_(NULL), version_(-1), - q_zone_(NULL), q_any_(NULL) + q_zone_(NULL), q_any_(NULL), + q_any_sub_(NULL), q_current_(NULL) /*q_record_(NULL), q_addrs_(NULL), q_referral_(NULL), q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL), q_prevnsec3_(NULL) */ @@ -34,6 +35,8 @@ struct SQLite3Parameters { int version_; sqlite3_stmt* q_zone_; sqlite3_stmt* q_any_; + sqlite3_stmt* q_any_sub_; + sqlite3_stmt* q_current_; /* TODO: Yet unneeded statements sqlite3_stmt* q_record_; @@ -76,6 +79,11 @@ public: if (params_.q_any_ != NULL) { sqlite3_finalize(params_.q_any_); } + if (params_.q_any_sub_ != NULL) { + sqlite3_finalize(params_.q_any_sub_); + } + // we do NOT finalize q_current_ - that is just a pointer to one of + // the other statements, not a separate one. /* if (params_.q_record_ != NULL) { sqlite3_finalize(params_.q_record_); @@ -139,6 +147,9 @@ const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata " "FROM records WHERE zone_id=?1 AND name=?2"; +const char* const q_any_sub_str = "SELECT rdtype, ttl, sigtype, rdata " + "FROM records WHERE zone_id=?1 AND name LIKE (\"%.\" || ?2)"; + /* TODO: Prune the statements, not everything will be needed maybe? const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata " "FROM records WHERE zone_id=?1 AND name=?2 AND " @@ -204,7 +215,10 @@ checkAndSetupSchema(Initializer* initializer) { } initializer->params_.q_zone_ = prepare(db, q_zone_str); - initializer->params_.q_any_ = prepare(db, q_any_str); + // Make sure the current is initialized to one of the statements, not NULL + initializer->params_.q_current_ = initializer->params_.q_any_ = + prepare(db, q_any_str); + initializer->params_.q_any_sub_ = prepare(db, q_any_sub_str); /* TODO: Yet unneeded statements initializer->params_.q_record_ = prepare(db, q_record_str); initializer->params_.q_addrs_ = prepare(db, q_addrs_str); @@ -323,15 +337,19 @@ SQLite3Database::getZone(const isc::dns::Name& name) const { } void -SQLite3Database::searchForRecords(int zone_id, const std::string& name, bool) { +SQLite3Database::searchForRecords(int zone_id, const std::string& name, + bool subdomains) +{ + dbparameters_->q_current_ = subdomains ? dbparameters_->q_any_sub_ : + dbparameters_->q_any_; resetSearch(); - if (sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id) != SQLITE_OK) { + if (sqlite3_bind_int(dbparameters_->q_current_, 1, zone_id) != SQLITE_OK) { isc_throw(DataSourceError, "Error in sqlite3_bind_int() for zone_id " << zone_id << ": " << sqlite3_errmsg(dbparameters_->db_)); } // use transient since name is a ref and may disappear - if (sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, + if (sqlite3_bind_text(dbparameters_->q_current_, 2, name.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) { isc_throw(DataSourceError, "Error in sqlite3_bind_text() for name " << @@ -376,7 +394,7 @@ SQLite3Database::getNextRecord(std::string columns[], size_t column_count) { "of size " << COLUMN_COUNT << " to getNextRecord()"); } - sqlite3_stmt* current_stmt = dbparameters_->q_any_; + sqlite3_stmt* current_stmt = dbparameters_->q_current_; const int rc = sqlite3_step(current_stmt); if (rc == SQLITE_ROW) { @@ -404,8 +422,8 @@ SQLite3Database::getNextRecord(std::string columns[], size_t column_count) { void SQLite3Database::resetSearch() { - sqlite3_reset(dbparameters_->q_any_); - sqlite3_clear_bindings(dbparameters_->q_any_); + sqlite3_reset(dbparameters_->q_current_); + sqlite3_clear_bindings(dbparameters_->q_current_); } } From e9e36557849ba6b650e503841596bd31034c1936 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 20 Jul 2011 09:00:53 +0900 Subject: [PATCH 453/974] [trac928] add statistics category and statistics items into some spec files (bob.spec, auth.spec, stats.spec) --- src/bin/auth/auth.spec.pre.in | 18 ++++++++++++++ src/bin/bind10/bob.spec | 11 +++++++++ src/bin/stats/stats.spec | 45 +++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/src/bin/auth/auth.spec.pre.in b/src/bin/auth/auth.spec.pre.in index d88ffb5e3e..2ce044e440 100644 --- a/src/bin/auth/auth.spec.pre.in +++ b/src/bin/auth/auth.spec.pre.in @@ -122,6 +122,24 @@ } ] } + ], + "statistics": [ + { + "item_name": "queries.tcp", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "Queries TCP ", + "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" + }, + { + "item_name": "queries.udp", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "Queries UDP", + "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially" + } ] } } diff --git a/src/bin/bind10/bob.spec b/src/bin/bind10/bob.spec index 1184fd1fc2..b4cfac6da6 100644 --- a/src/bin/bind10/bob.spec +++ b/src/bin/bind10/bob.spec @@ -37,6 +37,17 @@ "command_description": "List the running BIND 10 processes", "command_args": [] } + ], + "statistics": [ + { + "item_name": "boot_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Boot time", + "item_description": "A date time when bind10 process starts initially", + "item_format": "date-time" + } ] } } diff --git a/src/bin/stats/stats.spec b/src/bin/stats/stats.spec index 25f6b54827..635eb486a1 100644 --- a/src/bin/stats/stats.spec +++ b/src/bin/stats/stats.spec @@ -56,6 +56,51 @@ "command_description": "Shut down the stats module", "command_args": [] } + ], + "statistics": [ + { + "item_name": "report_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Report time", + "item_description": "A date time when stats module reports", + "item_format": "date-time" + }, + { + "item_name": "boot_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Boot time", + "item_description": "A date time when the stats module starts initially or when the stats module restarts", + "item_format": "date-time" + }, + { + "item_name": "last_update_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Last update time", + "item_description": "The latest date time when the stats module receives from other modules like auth server or boss process and so on", + "item_format": "date-time" + }, + { + "item_name": "timestamp", + "item_type": "real", + "item_optional": false, + "item_default": 0.0, + "item_title": "Timestamp", + "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)" + }, + { + "item_name": "lname", + "item_type": "string", + "item_optional": false, + "item_default": "", + "item_title": "Local Name", + "item_description": "A localname of stats module given via CC protocol" + } ] } } From 990247bfd2248be5ae4293928101eec87e1997e9 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:44:40 +0900 Subject: [PATCH 454/974] [trac929] add some spec files for unittest of statistics category --- src/lib/config/tests/testdata/Makefile.am | 8 ++++ src/lib/config/tests/testdata/data33_1.data | 7 +++ src/lib/config/tests/testdata/data33_2.data | 7 +++ src/lib/config/tests/testdata/spec33.spec | 50 +++++++++++++++++++++ src/lib/config/tests/testdata/spec34.spec | 14 ++++++ src/lib/config/tests/testdata/spec35.spec | 15 +++++++ src/lib/config/tests/testdata/spec36.spec | 17 +++++++ src/lib/config/tests/testdata/spec37.spec | 7 +++ src/lib/config/tests/testdata/spec38.spec | 17 +++++++ 9 files changed, 142 insertions(+) create mode 100644 src/lib/config/tests/testdata/data33_1.data create mode 100644 src/lib/config/tests/testdata/data33_2.data create mode 100644 src/lib/config/tests/testdata/spec33.spec create mode 100644 src/lib/config/tests/testdata/spec34.spec create mode 100644 src/lib/config/tests/testdata/spec35.spec create mode 100644 src/lib/config/tests/testdata/spec36.spec create mode 100644 src/lib/config/tests/testdata/spec37.spec create mode 100644 src/lib/config/tests/testdata/spec38.spec diff --git a/src/lib/config/tests/testdata/Makefile.am b/src/lib/config/tests/testdata/Makefile.am index 91d7f04540..0d8b92ecb5 100644 --- a/src/lib/config/tests/testdata/Makefile.am +++ b/src/lib/config/tests/testdata/Makefile.am @@ -25,6 +25,8 @@ EXTRA_DIST += data22_10.data EXTRA_DIST += data32_1.data EXTRA_DIST += data32_2.data EXTRA_DIST += data32_3.data +EXTRA_DIST += data33_1.data +EXTRA_DIST += data33_2.data EXTRA_DIST += spec1.spec EXTRA_DIST += spec2.spec EXTRA_DIST += spec3.spec @@ -57,3 +59,9 @@ EXTRA_DIST += spec29.spec EXTRA_DIST += spec30.spec EXTRA_DIST += spec31.spec EXTRA_DIST += spec32.spec +EXTRA_DIST += spec33.spec +EXTRA_DIST += spec34.spec +EXTRA_DIST += spec35.spec +EXTRA_DIST += spec36.spec +EXTRA_DIST += spec37.spec +EXTRA_DIST += spec38.spec diff --git a/src/lib/config/tests/testdata/data33_1.data b/src/lib/config/tests/testdata/data33_1.data new file mode 100644 index 0000000000..429852c974 --- /dev/null +++ b/src/lib/config/tests/testdata/data33_1.data @@ -0,0 +1,7 @@ +{ + "dummy_str": "Dummy String", + "dummy_int": 118, + "dummy_datetime": "2011-05-27T19:42:57Z", + "dummy_date": "2011-05-27", + "dummy_time": "19:42:57" +} diff --git a/src/lib/config/tests/testdata/data33_2.data b/src/lib/config/tests/testdata/data33_2.data new file mode 100644 index 0000000000..eb0615c1c9 --- /dev/null +++ b/src/lib/config/tests/testdata/data33_2.data @@ -0,0 +1,7 @@ +{ + "dummy_str": "Dummy String", + "dummy_int": 118, + "dummy_datetime": "xxxx", + "dummy_date": "xxxx", + "dummy_time": "xxxx" +} diff --git a/src/lib/config/tests/testdata/spec33.spec b/src/lib/config/tests/testdata/spec33.spec new file mode 100644 index 0000000000..3002488b72 --- /dev/null +++ b/src/lib/config/tests/testdata/spec33.spec @@ -0,0 +1,50 @@ +{ + "module_spec": { + "module_name": "Spec33", + "statistics": [ + { + "item_name": "dummy_str", + "item_type": "string", + "item_optional": false, + "item_default": "Dummy", + "item_title": "Dummy String", + "item_description": "A dummy string" + }, + { + "item_name": "dummy_int", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "Dummy Integer", + "item_description": "A dummy integer" + }, + { + "item_name": "dummy_datetime", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Dummy DateTime", + "item_description": "A dummy datetime", + "item_format": "date-time" + }, + { + "item_name": "dummy_date", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01", + "item_title": "Dummy Date", + "item_description": "A dummy date", + "item_format": "date" + }, + { + "item_name": "dummy_time", + "item_type": "string", + "item_optional": false, + "item_default": "00:00:00", + "item_title": "Dummy Time", + "item_description": "A dummy time", + "item_format": "time" + } + ] + } +} diff --git a/src/lib/config/tests/testdata/spec34.spec b/src/lib/config/tests/testdata/spec34.spec new file mode 100644 index 0000000000..dd1f3ca952 --- /dev/null +++ b/src/lib/config/tests/testdata/spec34.spec @@ -0,0 +1,14 @@ +{ + "module_spec": { + "module_name": "Spec34", + "statistics": [ + { + "item_name": "dummy_str", + "item_type": "string", + "item_optional": false, + "item_default": "Dummy", + "item_description": "A dummy string" + } + ] + } +} diff --git a/src/lib/config/tests/testdata/spec35.spec b/src/lib/config/tests/testdata/spec35.spec new file mode 100644 index 0000000000..86aaf145a0 --- /dev/null +++ b/src/lib/config/tests/testdata/spec35.spec @@ -0,0 +1,15 @@ +{ + "module_spec": { + "module_name": "Spec35", + "statistics": [ + { + "item_name": "dummy_str", + "item_type": "string", + "item_optional": false, + "item_default": "Dummy", + "item_title": "Dummy String" + } + ] + } +} + diff --git a/src/lib/config/tests/testdata/spec36.spec b/src/lib/config/tests/testdata/spec36.spec new file mode 100644 index 0000000000..fb9ce26084 --- /dev/null +++ b/src/lib/config/tests/testdata/spec36.spec @@ -0,0 +1,17 @@ +{ + "module_spec": { + "module_name": "Spec36", + "statistics": [ + { + "item_name": "dummy_str", + "item_type": "string", + "item_optional": false, + "item_default": "Dummy", + "item_title": "Dummy String", + "item_description": "A dummy string", + "item_format": "dummy" + } + ] + } +} + diff --git a/src/lib/config/tests/testdata/spec37.spec b/src/lib/config/tests/testdata/spec37.spec new file mode 100644 index 0000000000..bc444d107c --- /dev/null +++ b/src/lib/config/tests/testdata/spec37.spec @@ -0,0 +1,7 @@ +{ + "module_spec": { + "module_name": "Spec37", + "statistics": 8 + } +} + diff --git a/src/lib/config/tests/testdata/spec38.spec b/src/lib/config/tests/testdata/spec38.spec new file mode 100644 index 0000000000..1892e887fb --- /dev/null +++ b/src/lib/config/tests/testdata/spec38.spec @@ -0,0 +1,17 @@ +{ + "module_spec": { + "module_name": "Spec38", + "statistics": [ + { + "item_name": "dummy_datetime", + "item_type": "string", + "item_optional": false, + "item_default": "11", + "item_title": "Dummy DateTime", + "item_description": "A dummy datetime", + "item_format": "date-time" + } + ] + } +} + From 7d5c3d56743fb696405f509663b3e1558fa72e25 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:45:15 +0900 Subject: [PATCH 455/974] [trac929] add a statistics category into "spec2.spec" and modify message string to be compared with in EXPECT_EQ --- src/lib/config/tests/ccsession_unittests.cc | 4 ++-- src/lib/config/tests/testdata/spec2.spec | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc index 5ea4f32e3e..793fa30457 100644 --- a/src/lib/config/tests/ccsession_unittests.cc +++ b/src/lib/config/tests/ccsession_unittests.cc @@ -184,7 +184,7 @@ TEST_F(CCSessionTest, session2) { ConstElementPtr msg; std::string group, to; msg = session.getFirstMessage(group, to); - EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_spec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": { }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\" } ] }", msg->str()); + EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_spec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": { }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\", \"statistics\": [ { \"item_default\": \"1970-01-01T00:00:00Z\", \"item_description\": \"A dummy date time\", \"item_format\": \"date-time\", \"item_name\": \"dummy_time\", \"item_optional\": false, \"item_title\": \"Dummy Time\", \"item_type\": \"string\" } ] } ] }", msg->str()); EXPECT_EQ("ConfigManager", group); EXPECT_EQ("*", to); EXPECT_EQ(0, session.getMsgQueue()->size()); @@ -231,7 +231,7 @@ TEST_F(CCSessionTest, session3) { ConstElementPtr msg; std::string group, to; msg = session.getFirstMessage(group, to); - EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_spec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": { }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\" } ] }", msg->str()); + EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_spec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": { }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\", \"statistics\": [ { \"item_default\": \"1970-01-01T00:00:00Z\", \"item_description\": \"A dummy date time\", \"item_format\": \"date-time\", \"item_name\": \"dummy_time\", \"item_optional\": false, \"item_title\": \"Dummy Time\", \"item_type\": \"string\" } ] } ] }", msg->str()); EXPECT_EQ("ConfigManager", group); EXPECT_EQ("*", to); EXPECT_EQ(1, session.getMsgQueue()->size()); diff --git a/src/lib/config/tests/testdata/spec2.spec b/src/lib/config/tests/testdata/spec2.spec index 59b8ebcbbb..43524224a2 100644 --- a/src/lib/config/tests/testdata/spec2.spec +++ b/src/lib/config/tests/testdata/spec2.spec @@ -66,6 +66,17 @@ "command_description": "Shut down BIND 10", "command_args": [] } + ], + "statistics": [ + { + "item_name": "dummy_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Dummy Time", + "item_description": "A dummy date time", + "item_format": "date-time" + } ] } } From 5e621bce015d2847104303fba574989fdf0399e0 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:45:28 +0900 Subject: [PATCH 456/974] [trac929] add COMMAND_GET_STATISTICS_SPEC for "get_statistics_spec" --- src/lib/python/isc/config/ccsession.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index 4fa9d58f9c..ba7724ce55 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -91,6 +91,7 @@ COMMAND_CONFIG_UPDATE = "config_update" COMMAND_MODULE_SPECIFICATION_UPDATE = "module_specification_update" COMMAND_GET_COMMANDS_SPEC = "get_commands_spec" +COMMAND_GET_STATISTICS_SPEC = "get_statistics_spec" COMMAND_GET_CONFIG = "get_config" COMMAND_SET_CONFIG = "set_config" COMMAND_GET_MODULE_SPEC = "get_module_spec" From ab5085e81007711f9d18ed77f3d78f51cf37545c Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:46:27 +0900 Subject: [PATCH 457/974] [trac929] add "get_statistics_spec" into cfgmgr.py it pushes contents in statistics category of each spec file. --- src/lib/python/isc/config/cfgmgr.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py index 18e001c306..1db9fd389f 100644 --- a/src/lib/python/isc/config/cfgmgr.py +++ b/src/lib/python/isc/config/cfgmgr.py @@ -267,6 +267,19 @@ class ConfigManager: commands[module_name] = self.module_specs[module_name].get_commands_spec() return commands + def get_statistics_spec(self, name = None): + """Returns a dict containing 'module_name': statistics_spec for + all modules. If name is specified, only that module will + be included""" + statistics = {} + if name: + if name in self.module_specs: + statistics[name] = self.module_specs[name].get_statistics_spec() + else: + for module_name in self.module_specs.keys(): + statistics[module_name] = self.module_specs[module_name].get_statistics_spec() + return statistics + def read_config(self): """Read the current configuration from the file specificied at init()""" try: @@ -457,6 +470,8 @@ class ConfigManager: if cmd: if cmd == ccsession.COMMAND_GET_COMMANDS_SPEC: answer = ccsession.create_answer(0, self.get_commands_spec()) + elif cmd == ccsession.COMMAND_GET_STATISTICS_SPEC: + answer = ccsession.create_answer(0, self.get_statistics_spec()) elif cmd == ccsession.COMMAND_GET_MODULE_SPEC: answer = self._handle_get_module_spec(arg) elif cmd == ccsession.COMMAND_GET_CONFIG: From cddcafd790288f5e666198effa142132b6fc43fa Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:46:46 +0900 Subject: [PATCH 458/974] [trac929] add "validate_statistics" which validates statistics specification in the spec file It checks data types and data format of statistics specification --- src/lib/python/isc/config/module_spec.py | 91 +++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/src/lib/python/isc/config/module_spec.py b/src/lib/python/isc/config/module_spec.py index 9aa49e03e7..976be79dec 100644 --- a/src/lib/python/isc/config/module_spec.py +++ b/src/lib/python/isc/config/module_spec.py @@ -23,6 +23,7 @@ import json import sys +import time import isc.cc.data @@ -117,6 +118,26 @@ class ModuleSpec: return False + def validate_statistics(self, full, stat, errors = None): + """Check whether the given piece of data conforms to this + data definition. If so, it returns True. If not, it will + return false. If errors is given, and is an array, a string + describing the error will be appended to it. The current + version stops as soon as there is one error so this list + will not be exhaustive. If 'full' is true, it also errors on + non-optional missing values. Set this to False if you want to + validate only a part of a statistics tree (like a list of + non-default values). Also it checks 'item_format' in case + of time""" + stat_spec = self.get_statistics_spec() + if stat_spec: + return _validate_spec_list(stat_spec, full, stat, errors) + else: + # no spec, always bad + if errors != None: + errors.append("No statistics specification") + return False + def get_module_name(self): """Returns a string containing the name of the module as specified by the specification given at __init__()""" @@ -152,6 +173,14 @@ class ModuleSpec: else: return None + def get_statistics_spec(self): + """Returns a dict representation of the statistics part of the + specification, or None if there is none.""" + if 'statistics' in self._module_spec: + return self._module_spec['statistics'] + else: + return None + def __str__(self): """Returns a string representation of the full specification""" return self._module_spec.__str__() @@ -160,8 +189,9 @@ def _check(module_spec): """Checks the full specification. This is a dict that contains the element "module_spec", which is in itself a dict that must contain at least a "module_name" (string) and optionally - a "config_data" and a "commands" element, both of which are lists - of dicts. Raises a ModuleSpecError if there is a problem.""" + a "config_data", a "commands" and a "statistics" element, all + of which are lists of dicts. Raises a ModuleSpecError if there + is a problem.""" if type(module_spec) != dict: raise ModuleSpecError("data specification not a dict") if "module_name" not in module_spec: @@ -173,6 +203,8 @@ def _check(module_spec): _check_config_spec(module_spec["config_data"]) if "commands" in module_spec: _check_command_spec(module_spec["commands"]) + if "statistics" in module_spec: + _check_statistics_spec(module_spec["statistics"]) def _check_config_spec(config_data): # config data is a list of items represented by dicts that contain @@ -263,7 +295,46 @@ def _check_item_spec(config_item): if type(map_item) != dict: raise ModuleSpecError("map_item_spec element is not a dict") _check_item_spec(map_item) + if 'item_format' in config_item and 'item_default' in config_item: + item_format = config_item["item_format"] + item_default = config_item["item_default"] + if not _check_format(item_default, item_format): + raise ModuleSpecError( + "Wrong format for " + str(item_default) + " in " + str(item_name)) +def _check_statistics_spec(statistics): + # statistics is a list of items represented by dicts that contain + # things like "item_name", depending on the type they can have + # specific subitems + """Checks a list that contains the statistics part of the + specification. Raises a ModuleSpecError if there is a + problem.""" + if type(statistics) != list: + raise ModuleSpecError("statistics is of type " + str(type(statistics)) + + ", not a list of items") + for stat_item in statistics: + _check_item_spec(stat_item) + # Additionally checks if there are 'item_title' and + # 'item_description' + for item in [ 'item_title', 'item_description' ]: + if item not in stat_item: + raise ModuleSpecError("no " + item + " in statistics item") + +def _check_format(value, format_name): + """Check if specified value and format are correct. Return True if + is is correct.""" + # TODO: should be added other format types if necessary + time_formats = { 'date-time' : "%Y-%m-%dT%H:%M:%SZ", + 'date' : "%Y-%m-%d", + 'time' : "%H:%M:%S" } + for fmt in time_formats: + if format_name == fmt: + try: + time.strptime(value, time_formats[fmt]) + return True + except (ValueError, TypeError): + break + return False def _validate_type(spec, value, errors): """Returns true if the value is of the correct type given the @@ -300,6 +371,18 @@ def _validate_type(spec, value, errors): else: return True +def _validate_format(spec, value, errors): + """Returns true if the value is of the correct format given the + specification. And also return true if no 'item_format'""" + if "item_format" in spec: + item_format = spec['item_format'] + if not _check_format(value, item_format): + if errors != None: + errors.append("format type of " + str(value) + + " should be " + item_format) + return False + return True + def _validate_item(spec, full, data, errors): if not _validate_type(spec, data, errors): return False @@ -308,6 +391,8 @@ def _validate_item(spec, full, data, errors): for data_el in data: if not _validate_type(list_spec, data_el, errors): return False + if not _validate_format(list_spec, data_el, errors): + return False if list_spec['item_type'] == "map": if not _validate_item(list_spec, full, data_el, errors): return False @@ -322,6 +407,8 @@ def _validate_item(spec, full, data, errors): return False if not _validate_item(named_set_spec, full, data_el, errors): return False + elif not _validate_format(spec, data, errors): + return False return True def _validate_spec(spec, full, data, errors): From e443a325b31edefe9cd4da71e10497db6544468c Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:46:57 +0900 Subject: [PATCH 459/974] [trac929] add unittests for the functions: - validate_format - check_format - validate_format --- .../isc/config/tests/module_spec_test.py | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/lib/python/isc/config/tests/module_spec_test.py b/src/lib/python/isc/config/tests/module_spec_test.py index be862c5012..567cfd4945 100644 --- a/src/lib/python/isc/config/tests/module_spec_test.py +++ b/src/lib/python/isc/config/tests/module_spec_test.py @@ -81,6 +81,11 @@ class TestModuleSpec(unittest.TestCase): self.assertRaises(ModuleSpecError, self.read_spec_file, "spec20.spec") self.assertRaises(ModuleSpecError, self.read_spec_file, "spec21.spec") self.assertRaises(ModuleSpecError, self.read_spec_file, "spec26.spec") + self.assertRaises(ModuleSpecError, self.read_spec_file, "spec34.spec") + self.assertRaises(ModuleSpecError, self.read_spec_file, "spec35.spec") + self.assertRaises(ModuleSpecError, self.read_spec_file, "spec36.spec") + self.assertRaises(ModuleSpecError, self.read_spec_file, "spec37.spec") + self.assertRaises(ModuleSpecError, self.read_spec_file, "spec38.spec") def validate_data(self, specfile_name, datafile_name): dd = self.read_spec_file(specfile_name); @@ -123,6 +128,17 @@ class TestModuleSpec(unittest.TestCase): self.assertEqual(False, self.validate_command_params("spec27.spec", "data22_8.data", 'cmd1')) self.assertEqual(False, self.validate_command_params("spec27.spec", "data22_8.data", 'cmd2')) + def test_statistics_validation(self): + def _validate_stat(specfile_name, datafile_name): + dd = self.read_spec_file(specfile_name); + data_file = open(self.spec_file(datafile_name)) + data_str = data_file.read() + data = isc.cc.data.parse_value_str(data_str) + return dd.validate_statistics(True, data, []) + self.assertFalse(self.read_spec_file("spec1.spec").validate_statistics(True, None, None)); + self.assertTrue(_validate_stat("spec33.spec", "data33_1.data")) + self.assertFalse(_validate_stat("spec33.spec", "data33_2.data")) + def test_init(self): self.assertRaises(ModuleSpecError, ModuleSpec, 1) module_spec = isc.config.module_spec_from_file(self.spec_file("spec1.spec"), False) @@ -269,6 +285,74 @@ class TestModuleSpec(unittest.TestCase): } ) + self.assertRaises(ModuleSpecError, isc.config.module_spec._check_item_spec, + { 'item_name': "a_datetime", + 'item_type': "string", + 'item_optional': False, + 'item_default': 1, + 'item_format': "date-time" + } + ) + + self.assertRaises(ModuleSpecError, isc.config.module_spec._check_item_spec, + { 'item_name': "a_date", + 'item_type': "string", + 'item_optional': False, + 'item_default': 1, + 'item_format': "date" + } + ) + + self.assertRaises(ModuleSpecError, isc.config.module_spec._check_item_spec, + { 'item_name': "a_time", + 'item_type': "string", + 'item_optional': False, + 'item_default': 1, + 'item_format': "time" + } + ) + + self.assertRaises(ModuleSpecError, isc.config.module_spec._check_item_spec, + { 'item_name': "a_datetime", + 'item_type': "string", + 'item_optional': False, + 'item_default': "2011-05-27T19:42:57Z", + 'item_format': "dummy-format" + } + ) + + self.assertRaises(ModuleSpecError, isc.config.module_spec._check_item_spec, + { 'item_name': "a_date", + 'item_type': "string", + 'item_optional': False, + 'item_default': "2011-05-27", + 'item_format': "dummy-format" + } + ) + + self.assertRaises(ModuleSpecError, isc.config.module_spec._check_item_spec, + { 'item_name': "a_time", + 'item_type': "string", + 'item_optional': False, + 'item_default': "19:42:57Z", + 'item_format': "dummy-format" + } + ) + + def test_check_format(self): + self.assertTrue(isc.config.module_spec._check_format('2011-05-27T19:42:57Z', 'date-time')) + self.assertTrue(isc.config.module_spec._check_format('2011-05-27', 'date')) + self.assertTrue(isc.config.module_spec._check_format('19:42:57', 'time')) + self.assertFalse(isc.config.module_spec._check_format('2011-05-27T19:42:57Z', 'dummy')) + self.assertFalse(isc.config.module_spec._check_format('2011-05-27', 'dummy')) + self.assertFalse(isc.config.module_spec._check_format('19:42:57', 'dummy')) + self.assertFalse(isc.config.module_spec._check_format('2011-13-99T99:99:99Z', 'date-time')) + self.assertFalse(isc.config.module_spec._check_format('2011-13-99', 'date')) + self.assertFalse(isc.config.module_spec._check_format('99:99:99', 'time')) + self.assertFalse(isc.config.module_spec._check_format('', 'date-time')) + self.assertFalse(isc.config.module_spec._check_format(None, 'date-time')) + self.assertFalse(isc.config.module_spec._check_format(None, None)) + def test_validate_type(self): errors = [] self.assertEqual(True, isc.config.module_spec._validate_type({ 'item_type': 'integer' }, 1, errors)) @@ -306,6 +390,25 @@ class TestModuleSpec(unittest.TestCase): self.assertEqual(False, isc.config.module_spec._validate_type({ 'item_type': 'map' }, 1, errors)) self.assertEqual(['1 should be a map'], errors) + def test_validate_format(self): + errors = [] + self.assertEqual(True, isc.config.module_spec._validate_format({ 'item_format': 'date-time' }, "2011-05-27T19:42:57Z", errors)) + self.assertEqual(False, isc.config.module_spec._validate_format({ 'item_format': 'date-time' }, "a", None)) + self.assertEqual(False, isc.config.module_spec._validate_format({ 'item_format': 'date-time' }, "a", errors)) + self.assertEqual(['format type of a should be date-time'], errors) + + errors = [] + self.assertEqual(True, isc.config.module_spec._validate_format({ 'item_format': 'date' }, "2011-05-27", errors)) + self.assertEqual(False, isc.config.module_spec._validate_format({ 'item_format': 'date' }, "a", None)) + self.assertEqual(False, isc.config.module_spec._validate_format({ 'item_format': 'date' }, "a", errors)) + self.assertEqual(['format type of a should be date'], errors) + + errors = [] + self.assertEqual(True, isc.config.module_spec._validate_format({ 'item_format': 'time' }, "19:42:57", errors)) + self.assertEqual(False, isc.config.module_spec._validate_format({ 'item_format': 'time' }, "a", None)) + self.assertEqual(False, isc.config.module_spec._validate_format({ 'item_format': 'time' }, "a", errors)) + self.assertEqual(['format type of a should be time'], errors) + def test_validate_spec(self): spec = { 'item_name': "an_item", 'item_type': "string", From c4ef641d07c7ddfd6b86d6b5ae944ab9a30d6990 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:47:09 +0900 Subject: [PATCH 460/974] [trac929] add unittest of "get_statistics_spec" --- .../python/isc/config/tests/cfgmgr_test.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py index 0a9e2d3e44..eacc425dd5 100644 --- a/src/lib/python/isc/config/tests/cfgmgr_test.py +++ b/src/lib/python/isc/config/tests/cfgmgr_test.py @@ -219,6 +219,25 @@ class TestConfigManager(unittest.TestCase): commands_spec = self.cm.get_commands_spec('Spec2') self.assertEqual(commands_spec['Spec2'], module_spec.get_commands_spec()) + def test_get_statistics_spec(self): + statistics_spec = self.cm.get_statistics_spec() + self.assertEqual(statistics_spec, {}) + module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec") + 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) + statistics_spec = self.cm.get_statistics_spec() + self.assertEqual(statistics_spec, { 'Spec1': None }) + self.cm.remove_module_spec('Spec1') + module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec2.spec") + 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) + statistics_spec = self.cm.get_statistics_spec() + self.assertEqual(statistics_spec['Spec2'], module_spec.get_statistics_spec()) + statistics_spec = self.cm.get_statistics_spec('Spec2') + self.assertEqual(statistics_spec['Spec2'], module_spec.get_statistics_spec()) + def test_read_config(self): self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION}) self.cm.read_config() @@ -241,6 +260,7 @@ class TestConfigManager(unittest.TestCase): self._handle_msg_helper("", { 'result': [ 1, 'Unknown message format: ']}) self._handle_msg_helper({ "command": [ "badcommand" ] }, { 'result': [ 1, "Unknown command: badcommand"]}) self._handle_msg_helper({ "command": [ "get_commands_spec" ] }, { 'result': [ 0, {} ]}) + self._handle_msg_helper({ "command": [ "get_statistics_spec" ] }, { 'result': [ 0, {} ]}) self._handle_msg_helper({ "command": [ "get_module_spec" ] }, { 'result': [ 0, {} ]}) self._handle_msg_helper({ "command": [ "get_module_spec", { "module_name": "Spec2" } ] }, { 'result': [ 0, {} ]}) #self._handle_msg_helper({ "command": [ "get_module_spec", { "module_name": "nosuchmodule" } ] }, @@ -329,6 +349,7 @@ class TestConfigManager(unittest.TestCase): { "module_name" : "Spec2" } ] }, { 'result': [ 0, self.spec.get_full_spec() ] }) self._handle_msg_helper({ "command": [ "get_commands_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_commands_spec() } ]}) + self._handle_msg_helper({ "command": [ "get_statistics_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_statistics_spec() } ]}) # re-add this once we have new way to propagate spec changes (1 instead of the current 2 messages) #self.assertEqual(len(self.fake_session.message_queue), 2) # the name here is actually wrong (and hardcoded), but needed in the current version @@ -450,6 +471,7 @@ 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": [ "shutdown" ] }, "ConfigManager") self.cm.run() pass From d5ded106a85afaf695e59941bd382bca4811fe46 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 19 Jul 2011 20:55:39 +0900 Subject: [PATCH 461/974] [trac929] addition and modification as a variant of the statistics part of module_spec.py - add check_format which checks whether the given element is a valid statistics specification - modify check_data_specification to add check of statistics specification - add getStatisticsSpec() which returns statistics specification - add two of validateStatistics which check whether specified data is valid for statistics specification - modify validateItem to add check of item_format in specification update the year of copyright --- src/lib/config/module_spec.cc | 84 ++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc index 306c7954f4..eed6b72430 100644 --- a/src/lib/config/module_spec.cc +++ b/src/lib/config/module_spec.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium. +// Copyright (C) 2010, 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 @@ -87,6 +87,54 @@ check_config_item_list(ConstElementPtr spec) { } } +// checks whether the given element is a valid statistics specification +// returns false if the specification is bad +bool +check_format(ConstElementPtr value, ConstElementPtr format_name) { + typedef std::map format_types; + format_types time_formats; + // TODO: should be added other format types if necessary + time_formats.insert( + format_types::value_type("date-time", "%Y-%m-%dT%H:%M:%SZ") ); + time_formats.insert( + format_types::value_type("date", "%Y-%m-%d") ); + time_formats.insert( + format_types::value_type("time", "%H:%M:%S") ); + BOOST_FOREACH (const format_types::value_type& f, time_formats) { + if (format_name->stringValue() == f.first) { + struct tm tm; + return (strptime(value->stringValue().c_str(), + f.second.c_str(), &tm) != NULL); + } + } + return (false); +} + +void check_statistics_item_list(ConstElementPtr spec); + +void +check_statistics_item_list(ConstElementPtr spec) { + if (spec->getType() != Element::list) { + throw ModuleSpecError("statistics is not a list of elements"); + } + BOOST_FOREACH(ConstElementPtr item, spec->listValue()) { + check_config_item(item); + // additional checks for statistics + check_leaf_item(item, "item_title", Element::string, true); + check_leaf_item(item, "item_description", Element::string, true); + check_leaf_item(item, "item_format", Element::string, false); + // checks name of item_format and validation of item_default + if (item->contains("item_format") + && item->contains("item_default")) { + if(!check_format(item->get("item_default"), + item->get("item_format"))) { + throw ModuleSpecError( + "item_default not valid type of item_format"); + } + } + } +} + void check_command(ConstElementPtr spec) { check_leaf_item(spec, "command_name", Element::string, true); @@ -116,6 +164,9 @@ check_data_specification(ConstElementPtr spec) { if (spec->contains("commands")) { check_command_list(spec->get("commands")); } + if (spec->contains("statistics")) { + check_statistics_item_list(spec->get("statistics")); + } } // checks whether the given element is a valid module specification @@ -165,6 +216,15 @@ ModuleSpec::getConfigSpec() const { } } +ConstElementPtr +ModuleSpec::getStatisticsSpec() const { + if (module_specification->contains("statistics")) { + return (module_specification->get("statistics")); + } else { + return (ElementPtr()); + } +} + const std::string ModuleSpec::getModuleName() const { return (module_specification->get("module_name")->stringValue()); @@ -185,6 +245,12 @@ ModuleSpec::validateConfig(ConstElementPtr data, const bool full) const { return (validateSpecList(spec, data, full, ElementPtr())); } +bool +ModuleSpec::validateStatistics(ConstElementPtr data, const bool full) const { + ConstElementPtr spec = module_specification->find("statistics"); + return (validateSpecList(spec, data, full, ElementPtr())); +} + bool ModuleSpec::validateCommand(const std::string& command, ConstElementPtr args, @@ -223,6 +289,14 @@ ModuleSpec::validateConfig(ConstElementPtr data, const bool full, return (validateSpecList(spec, data, full, errors)); } +bool +ModuleSpec::validateStatistics(ConstElementPtr data, const bool full, + ElementPtr errors) const +{ + ConstElementPtr spec = module_specification->find("statistics"); + return (validateSpecList(spec, data, full, errors)); +} + ModuleSpec moduleSpecFromFile(const std::string& file_name, const bool check) throw(JSONError, ModuleSpecError) @@ -343,6 +417,14 @@ ModuleSpec::validateItem(ConstElementPtr spec, ConstElementPtr data, } } } + if (spec->contains("item_format")) { + if (!check_format(data, spec->get("item_format"))) { + if (errors) { + errors->add(Element::create("Format mismatch")); + } + return (false); + } + } return (true); } From 7cf7ec751e4f776dbb60cd290cea4fb217173cdb Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 19 Jul 2011 20:57:32 +0900 Subject: [PATCH 462/974] [trac929] add some methods for statistics specification - getStatisticsSpec() - validateStatistics() - validateStatistics() update the year of copyright --- src/lib/config/module_spec.h | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/lib/config/module_spec.h b/src/lib/config/module_spec.h index ab6e273edd..ce3762f203 100644 --- a/src/lib/config/module_spec.h +++ b/src/lib/config/module_spec.h @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium. +// Copyright (C) 2010, 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 @@ -71,6 +71,12 @@ namespace isc { namespace config { /// part of the specification isc::data::ConstElementPtr getConfigSpec() const; + /// Returns the statistics part of the specification as an + /// ElementPtr + /// \return ElementPtr Shared pointer to the statistics + /// part of the specification + isc::data::ConstElementPtr getStatisticsSpec() const; + /// Returns the full module specification as an ElementPtr /// \return ElementPtr Shared pointer to the specification isc::data::ConstElementPtr getFullSpec() const { @@ -95,6 +101,17 @@ namespace isc { namespace config { bool validateConfig(isc::data::ConstElementPtr data, const bool full = false) const; + // returns true if the given element conforms to this data + // statistics specification + /// Validates the given statistics data for this specification. + /// \param data The base \c Element of the data to check + /// \param full If true, all non-optional statistics parameters + /// must be specified. + /// \return true if the data conforms to the specification, + /// false otherwise. + bool validateStatistics(isc::data::ConstElementPtr data, + const bool full = false) const; + /// Validates the arguments for the given command /// /// This checks the command and argument against the @@ -142,6 +159,10 @@ namespace isc { namespace config { bool validateConfig(isc::data::ConstElementPtr data, const bool full, isc::data::ElementPtr errors) const; + /// errors must be of type ListElement + bool validateStatistics(isc::data::ConstElementPtr data, const bool full, + isc::data::ElementPtr errors) const; + private: bool validateItem(isc::data::ConstElementPtr spec, isc::data::ConstElementPtr data, From 7bda7762ab9243404bbd0964908b3365cd052969 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 19 Jul 2011 21:02:12 +0900 Subject: [PATCH 463/974] [trac929] add unit tests of the methods using some dummy spec files with the dummy data of statistics specification - getStatisticsSpec() - validateStatistics() - validateStatistics() - check_format() add include of boost_foreach update the year of copyright --- src/lib/config/tests/module_spec_unittests.cc | 145 +++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/src/lib/config/tests/module_spec_unittests.cc b/src/lib/config/tests/module_spec_unittests.cc index d642af8286..315a78df06 100644 --- a/src/lib/config/tests/module_spec_unittests.cc +++ b/src/lib/config/tests/module_spec_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2009, 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -18,6 +18,8 @@ #include +#include + #include using namespace isc::data; @@ -57,6 +59,7 @@ TEST(ModuleSpec, ReadingSpecfiles) { dd = moduleSpecFromFile(specfile("spec2.spec")); EXPECT_EQ("[ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ]", dd.getCommandsSpec()->str()); + EXPECT_EQ("[ { \"item_default\": \"1970-01-01T00:00:00Z\", \"item_description\": \"A dummy date time\", \"item_format\": \"date-time\", \"item_name\": \"dummy_time\", \"item_optional\": false, \"item_title\": \"Dummy Time\", \"item_type\": \"string\" } ]", dd.getStatisticsSpec()->str()); EXPECT_EQ("Spec2", dd.getModuleName()); EXPECT_EQ("", dd.getModuleDescription()); @@ -64,6 +67,11 @@ TEST(ModuleSpec, ReadingSpecfiles) { EXPECT_EQ("Spec25", dd.getModuleName()); EXPECT_EQ("Just an empty module", dd.getModuleDescription()); EXPECT_THROW(moduleSpecFromFile(specfile("spec26.spec")), ModuleSpecError); + EXPECT_THROW(moduleSpecFromFile(specfile("spec34.spec")), ModuleSpecError); + EXPECT_THROW(moduleSpecFromFile(specfile("spec35.spec")), ModuleSpecError); + EXPECT_THROW(moduleSpecFromFile(specfile("spec36.spec")), ModuleSpecError); + EXPECT_THROW(moduleSpecFromFile(specfile("spec37.spec")), ModuleSpecError); + EXPECT_THROW(moduleSpecFromFile(specfile("spec38.spec")), ModuleSpecError); std::ifstream file; file.open(specfile("spec1.spec").c_str()); @@ -71,6 +79,7 @@ TEST(ModuleSpec, ReadingSpecfiles) { EXPECT_EQ(dd.getFullSpec()->get("module_name") ->stringValue(), "Spec1"); EXPECT_TRUE(isNull(dd.getCommandsSpec())); + EXPECT_TRUE(isNull(dd.getStatisticsSpec())); std::ifstream file2; file2.open(specfile("spec8.spec").c_str()); @@ -114,6 +123,12 @@ TEST(ModuleSpec, SpecfileConfigData) { "commands is not a list of elements"); } +TEST(ModuleSpec, SpecfileStatistics) { + moduleSpecError("spec36.spec", "item_default not valid type of item_format"); + moduleSpecError("spec37.spec", "statistics is not a list of elements"); + moduleSpecError("spec38.spec", "item_default not valid type of item_format"); +} + TEST(ModuleSpec, SpecfileCommands) { moduleSpecError("spec17.spec", "command_name missing in { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\" }"); @@ -136,6 +151,17 @@ dataTest(const ModuleSpec& dd, const std::string& data_file_name) { return (dd.validateConfig(data)); } +bool +statisticsTest(const ModuleSpec& dd, const std::string& data_file_name) { + std::ifstream data_file; + + data_file.open(specfile(data_file_name).c_str()); + ConstElementPtr data = Element::fromJSON(data_file, data_file_name); + data_file.close(); + + return (dd.validateStatistics(data)); +} + bool dataTestWithErrors(const ModuleSpec& dd, const std::string& data_file_name, ElementPtr errors) @@ -149,6 +175,19 @@ dataTestWithErrors(const ModuleSpec& dd, const std::string& data_file_name, return (dd.validateConfig(data, true, errors)); } +bool +statisticsTestWithErrors(const ModuleSpec& dd, const std::string& data_file_name, + ElementPtr errors) +{ + std::ifstream data_file; + + data_file.open(specfile(data_file_name).c_str()); + ConstElementPtr data = Element::fromJSON(data_file, data_file_name); + data_file.close(); + + return (dd.validateStatistics(data, true, errors)); +} + TEST(ModuleSpec, DataValidation) { ModuleSpec dd = moduleSpecFromFile(specfile("spec22.spec")); @@ -175,6 +214,17 @@ TEST(ModuleSpec, DataValidation) { EXPECT_EQ("[ \"Unknown item value_does_not_exist\" ]", errors->str()); } +TEST(ModuleSpec, StatisticsValidation) { + ModuleSpec dd = moduleSpecFromFile(specfile("spec33.spec")); + + EXPECT_TRUE(statisticsTest(dd, "data33_1.data")); + EXPECT_FALSE(statisticsTest(dd, "data33_2.data")); + + ElementPtr errors = Element::createList(); + EXPECT_FALSE(statisticsTestWithErrors(dd, "data33_2.data", errors)); + EXPECT_EQ("[ \"Format mismatch\", \"Format mismatch\", \"Format mismatch\" ]", errors->str()); +} + TEST(ModuleSpec, CommandValidation) { ModuleSpec dd = moduleSpecFromFile(specfile("spec2.spec")); ConstElementPtr arg = Element::fromJSON("{}"); @@ -220,3 +270,96 @@ TEST(ModuleSpec, NamedSetValidation) { EXPECT_FALSE(dataTest(dd, "data32_2.data")); EXPECT_FALSE(dataTest(dd, "data32_3.data")); } + +TEST(ModuleSpec, CheckFormat) { + + const std::string json_begin = "{ \"module_spec\": { \"module_name\": \"Foo\", \"statistics\": [ { \"item_name\": \"dummy_time\", \"item_type\": \"string\", \"item_optional\": true, \"item_title\": \"Dummy Time\", \"item_description\": \"A dummy date time\""; + const std::string json_end = " } ] } }"; + std::string item_default; + std::string item_format; + std::vector specs; + ConstElementPtr el; + + specs.clear(); + item_default = "\"item_default\": \"2011-05-27T19:42:57Z\","; + item_format = "\"item_format\": \"date-time\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"2011-05-27\","; + item_format = "\"item_format\": \"date\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"19:42:57Z\","; + item_format = "\"item_format\": \"time\""; + specs.push_back("," + item_default + item_format); + + item_format = "\"item_format\": \"date-time\""; + specs.push_back("," + item_format); + item_default = ""; + item_format = "\"item_format\": \"date\""; + specs.push_back("," + item_format); + item_default = ""; + item_format = "\"item_format\": \"time\""; + specs.push_back("," + item_format); + + item_default = "\"item_default\": \"a\""; + specs.push_back("," + item_default); + item_default = "\"item_default\": \"b\""; + specs.push_back("," + item_default); + item_default = "\"item_default\": \"c\""; + specs.push_back("," + item_default); + + item_format = "\"item_format\": \"dummy\""; + specs.push_back("," + item_format); + + specs.push_back(""); + + BOOST_FOREACH(std::string s, specs) { + el = Element::fromJSON(json_begin + s + json_end)->get("module_spec"); + EXPECT_NO_THROW(ModuleSpec(el, true)); + } + + specs.clear(); + item_default = "\"item_default\": \"2011-05-27T19:42:57Z\","; + item_format = "\"item_format\": \"dummy\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"2011-05-27\","; + item_format = "\"item_format\": \"dummy\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"19:42:57Z\","; + item_format = "\"item_format\": \"dummy\""; + specs.push_back("," + item_default + item_format); + + item_default = "\"item_default\": \"2011-13-99T99:99:99Z\","; + item_format = "\"item_format\": \"date-time\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"2011-13-99\","; + item_format = "\"item_format\": \"date\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"99:99:99Z\","; + item_format = "\"item_format\": \"time\""; + specs.push_back("," + item_default + item_format); + + item_default = "\"item_default\": \"1\","; + item_format = "\"item_format\": \"date-time\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"1\","; + item_format = "\"item_format\": \"date\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"1\","; + item_format = "\"item_format\": \"time\""; + specs.push_back("," + item_default + item_format); + + item_default = "\"item_default\": \"\","; + item_format = "\"item_format\": \"date-time\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"\","; + item_format = "\"item_format\": \"date\""; + specs.push_back("," + item_default + item_format); + item_default = "\"item_default\": \"\","; + item_format = "\"item_format\": \"time\""; + specs.push_back("," + item_default + item_format); + + BOOST_FOREACH(std::string s, specs) { + el = Element::fromJSON(json_begin + s + json_end)->get("module_spec"); + EXPECT_THROW(ModuleSpec(el, true), ModuleSpecError); + } +} From 9b6993b6f6507fab1bc8956f727cca60c8c9243a Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 26 Jul 2011 18:04:43 +0900 Subject: [PATCH 464/974] [trac929] changed into the exact way of checking whether the value is "None" or not in the "if" branch as pointed out in the reviewing. --- src/lib/python/isc/config/module_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/config/module_spec.py b/src/lib/python/isc/config/module_spec.py index 976be79dec..7f5aa00441 100644 --- a/src/lib/python/isc/config/module_spec.py +++ b/src/lib/python/isc/config/module_spec.py @@ -130,7 +130,7 @@ class ModuleSpec: non-default values). Also it checks 'item_format' in case of time""" stat_spec = self.get_statistics_spec() - if stat_spec: + if stat_spec != None: return _validate_spec_list(stat_spec, full, stat, errors) else: # no spec, always bad From 0f787178301c7cbf59fc7c516ebe920a33e22429 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 27 Jul 2011 18:32:06 +0200 Subject: [PATCH 465/974] [trac929] replace != None with is not None --- src/lib/python/isc/config/module_spec.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/python/isc/config/module_spec.py b/src/lib/python/isc/config/module_spec.py index 7f5aa00441..d120080094 100644 --- a/src/lib/python/isc/config/module_spec.py +++ b/src/lib/python/isc/config/module_spec.py @@ -92,7 +92,7 @@ class ModuleSpec: return _validate_spec_list(data_def, full, data, errors) else: # no spec, always bad - if errors != None: + if errors is not None: errors.append("No config_data specification") return False @@ -130,11 +130,11 @@ class ModuleSpec: non-default values). Also it checks 'item_format' in case of time""" stat_spec = self.get_statistics_spec() - if stat_spec != None: + if stat_spec is not None: return _validate_spec_list(stat_spec, full, stat, errors) else: # no spec, always bad - if errors != None: + if errors is not None: errors.append("No statistics specification") return False @@ -341,27 +341,27 @@ def _validate_type(spec, value, errors): specification""" data_type = spec['item_type'] if data_type == "integer" and type(value) != int: - if errors != None: + if errors is not None: errors.append(str(value) + " should be an integer") return False elif data_type == "real" and type(value) != float: - if errors != None: + if errors is not None: errors.append(str(value) + " should be a real") return False elif data_type == "boolean" and type(value) != bool: - if errors != None: + if errors is not None: errors.append(str(value) + " should be a boolean") return False elif data_type == "string" and type(value) != str: - if errors != None: + if errors is not None: errors.append(str(value) + " should be a string") return False elif data_type == "list" and type(value) != list: - if errors != None: + if errors is not None: errors.append(str(value) + " should be a list") return False elif data_type == "map" and type(value) != dict: - if errors != None: + if errors is not None: errors.append(str(value) + " should be a map") return False elif data_type == "named_set" and type(value) != dict: @@ -377,7 +377,7 @@ def _validate_format(spec, value, errors): if "item_format" in spec: item_format = spec['item_format'] if not _check_format(value, item_format): - if errors != None: + if errors is not None: errors.append("format type of " + str(value) + " should be " + item_format) return False @@ -420,7 +420,7 @@ def _validate_spec(spec, full, data, errors): elif item_name in data: return _validate_item(spec, full, data[item_name], errors) elif full and not item_optional: - if errors != None: + if errors is not None: errors.append("non-optional item " + item_name + " missing") return False else: @@ -445,7 +445,7 @@ def _validate_spec_list(module_spec, full, data, errors): if spec_item["item_name"] == item_name: found = True if not found and item_name != "version": - if errors != None: + if errors is not None: errors.append("unknown item " + item_name) validated = False return validated From 1ddc6158f7544c95742757654863379fff847771 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 29 Jul 2011 19:59:08 +0900 Subject: [PATCH 466/974] [trac929] fix the invalid spec file for module_spec.py remove format_type undefined in module_spec.py from stats-schema.spec --- src/bin/stats/stats-schema.spec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bin/stats/stats-schema.spec b/src/bin/stats/stats-schema.spec index 37e9c1ae9a..52528657e8 100644 --- a/src/bin/stats/stats-schema.spec +++ b/src/bin/stats/stats-schema.spec @@ -54,8 +54,7 @@ "item_optional": false, "item_default": 0.0, "item_title": "stats.Timestamp", - "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)", - "item_format": "second" + "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)" }, { "item_name": "stats.lname", From 87e410c0061df72fe69fb47c7456ae54c609b219 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 29 Jul 2011 20:07:23 +0900 Subject: [PATCH 467/974] [trac929] implement some methods checking config_data into the mock of module_spec, but actually just copy such methods from the original module_spec into the mock. --- src/bin/stats/tests/isc/config/ccsession.py | 89 +++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/bin/stats/tests/isc/config/ccsession.py b/src/bin/stats/tests/isc/config/ccsession.py index a4e9c37c1f..50f7c1b163 100644 --- a/src/bin/stats/tests/isc/config/ccsession.py +++ b/src/bin/stats/tests/isc/config/ccsession.py @@ -23,6 +23,7 @@ external module. import json import os +import time from isc.cc.session import Session COMMAND_CONFIG_UPDATE = "config_update" @@ -72,6 +73,9 @@ class ModuleSpecError(Exception): class ModuleSpec: def __init__(self, module_spec, check = True): + # check only confi_data for testing + if check and "config_data" in module_spec: + _check_config_spec(module_spec["config_data"]) self._module_spec = module_spec def get_config_spec(self): @@ -83,6 +87,91 @@ class ModuleSpec: def get_module_name(self): return self._module_spec['module_name'] +def _check_config_spec(config_data): + # config data is a list of items represented by dicts that contain + # things like "item_name", depending on the type they can have + # specific subitems + """Checks a list that contains the configuration part of the + specification. Raises a ModuleSpecError if there is a + problem.""" + if type(config_data) != list: + raise ModuleSpecError("config_data is of type " + str(type(config_data)) + ", not a list of items") + for config_item in config_data: + _check_item_spec(config_item) + +def _check_item_spec(config_item): + """Checks the dict that defines one config item + (i.e. containing "item_name", "item_type", etc. + Raises a ModuleSpecError if there is an error""" + if type(config_item) != dict: + raise ModuleSpecError("item spec not a dict") + if "item_name" not in config_item: + raise ModuleSpecError("no item_name in config item") + if type(config_item["item_name"]) != str: + raise ModuleSpecError("item_name is not a string: " + str(config_item["item_name"])) + item_name = config_item["item_name"] + if "item_type" not in config_item: + raise ModuleSpecError("no item_type in config item") + item_type = config_item["item_type"] + if type(item_type) != str: + raise ModuleSpecError("item_type in " + item_name + " is not a string: " + str(type(item_type))) + if item_type not in ["integer", "real", "boolean", "string", "list", "map", "any"]: + raise ModuleSpecError("unknown item_type in " + item_name + ": " + item_type) + if "item_optional" in config_item: + if type(config_item["item_optional"]) != bool: + raise ModuleSpecError("item_default in " + item_name + " is not a boolean") + if not config_item["item_optional"] and "item_default" not in config_item: + raise ModuleSpecError("no default value for non-optional item " + item_name) + else: + raise ModuleSpecError("item_optional not in item " + item_name) + if "item_default" in config_item: + item_default = config_item["item_default"] + if (item_type == "integer" and type(item_default) != int) or \ + (item_type == "real" and type(item_default) != float) or \ + (item_type == "boolean" and type(item_default) != bool) or \ + (item_type == "string" and type(item_default) != str) or \ + (item_type == "list" and type(item_default) != list) or \ + (item_type == "map" and type(item_default) != dict): + raise ModuleSpecError("Wrong type for item_default in " + item_name) + # TODO: once we have check_type, run the item default through that with the list|map_item_spec + if item_type == "list": + if "list_item_spec" not in config_item: + raise ModuleSpecError("no list_item_spec in list item " + item_name) + if type(config_item["list_item_spec"]) != dict: + raise ModuleSpecError("list_item_spec in " + item_name + " is not a dict") + _check_item_spec(config_item["list_item_spec"]) + if item_type == "map": + if "map_item_spec" not in config_item: + raise ModuleSpecError("no map_item_sepc in map item " + item_name) + if type(config_item["map_item_spec"]) != list: + raise ModuleSpecError("map_item_spec in " + item_name + " is not a list") + for map_item in config_item["map_item_spec"]: + if type(map_item) != dict: + raise ModuleSpecError("map_item_spec element is not a dict") + _check_item_spec(map_item) + if 'item_format' in config_item and 'item_default' in config_item: + item_format = config_item["item_format"] + item_default = config_item["item_default"] + if not _check_format(item_default, item_format): + raise ModuleSpecError( + "Wrong format for " + str(item_default) + " in " + str(item_name)) + +def _check_format(value, format_name): + """Check if specified value and format are correct. Return True if + is is correct.""" + # TODO: should be added other format types if necessary + time_formats = { 'date-time' : "%Y-%m-%dT%H:%M:%SZ", + 'date' : "%Y-%m-%d", + 'time' : "%H:%M:%S" } + for fmt in time_formats: + if format_name == fmt: + try: + time.strptime(value, time_formats[fmt]) + return True + except (ValueError, TypeError): + break + return False + class ModuleCCSessionError(Exception): pass From 93a7f7d1495795b731242e270b6dc76b1ad6b0dc Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 5 Aug 2011 14:22:28 +0900 Subject: [PATCH 468/974] [trac929] add more strict check for date and time format (add reverse check) --- src/lib/config/module_spec.cc | 9 ++++++++- src/lib/config/tests/module_spec_unittests.cc | 2 +- src/lib/python/isc/config/module_spec.py | 6 ++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc index eed6b72430..27cf993905 100644 --- a/src/lib/config/module_spec.cc +++ b/src/lib/config/module_spec.cc @@ -103,8 +103,15 @@ check_format(ConstElementPtr value, ConstElementPtr format_name) { BOOST_FOREACH (const format_types::value_type& f, time_formats) { if (format_name->stringValue() == f.first) { struct tm tm; + char buf[255] = ""; + memset(&tm, 0, sizeof(tm)); + // reverse check return (strptime(value->stringValue().c_str(), - f.second.c_str(), &tm) != NULL); + f.second.c_str(), &tm) != NULL + && strftime(buf, sizeof(buf), + f.second.c_str(), &tm) != 0 + && strcmp(value->stringValue().c_str(), + buf) == 0); } } return (false); diff --git a/src/lib/config/tests/module_spec_unittests.cc b/src/lib/config/tests/module_spec_unittests.cc index 315a78df06..cfd0ff5216 100644 --- a/src/lib/config/tests/module_spec_unittests.cc +++ b/src/lib/config/tests/module_spec_unittests.cc @@ -287,7 +287,7 @@ TEST(ModuleSpec, CheckFormat) { item_default = "\"item_default\": \"2011-05-27\","; item_format = "\"item_format\": \"date\""; specs.push_back("," + item_default + item_format); - item_default = "\"item_default\": \"19:42:57Z\","; + item_default = "\"item_default\": \"19:42:57\","; item_format = "\"item_format\": \"time\""; specs.push_back("," + item_default + item_format); diff --git a/src/lib/python/isc/config/module_spec.py b/src/lib/python/isc/config/module_spec.py index d120080094..b79f928237 100644 --- a/src/lib/python/isc/config/module_spec.py +++ b/src/lib/python/isc/config/module_spec.py @@ -330,8 +330,10 @@ def _check_format(value, format_name): for fmt in time_formats: if format_name == fmt: try: - time.strptime(value, time_formats[fmt]) - return True + # reverse check + return value == time.strftime( + time_formats[fmt], + time.strptime(value, time_formats[fmt])) except (ValueError, TypeError): break return False From 3e0a0e157bc2a1ca7ad9efb566755ec61eedd180 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 9 Aug 2011 15:53:56 +0900 Subject: [PATCH 469/974] [trac929] consideration for buffer overflow - use std::vector instead of char[] - use strncmp() instead of strcmp() - shorten length of char array for the buffer (not directly related to buffer overflow) add more unittests for some wrong type formats into both c++ and python codes (unittests for the previous change git e9620e0d9dd3d967bcfb99562f13848c70538a44) - date-time-type format not ending with "Z" - date-type format ending with "T" - time-type format ending with "Z" --- src/lib/config/module_spec.cc | 8 ++++---- src/lib/config/tests/module_spec_unittests.cc | 13 +++++++++++++ src/lib/python/isc/config/tests/module_spec_test.py | 6 ++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc index 27cf993905..bebe695023 100644 --- a/src/lib/config/module_spec.cc +++ b/src/lib/config/module_spec.cc @@ -103,15 +103,15 @@ check_format(ConstElementPtr value, ConstElementPtr format_name) { BOOST_FOREACH (const format_types::value_type& f, time_formats) { if (format_name->stringValue() == f.first) { struct tm tm; - char buf[255] = ""; + std::vector buf(32); memset(&tm, 0, sizeof(tm)); // reverse check return (strptime(value->stringValue().c_str(), f.second.c_str(), &tm) != NULL - && strftime(buf, sizeof(buf), + && strftime(&buf[0], buf.size(), f.second.c_str(), &tm) != 0 - && strcmp(value->stringValue().c_str(), - buf) == 0); + && strncmp(value->stringValue().c_str(), + &buf[0], buf.size()) == 0); } } return (false); diff --git a/src/lib/config/tests/module_spec_unittests.cc b/src/lib/config/tests/module_spec_unittests.cc index cfd0ff5216..b2ca7b45f4 100644 --- a/src/lib/config/tests/module_spec_unittests.cc +++ b/src/lib/config/tests/module_spec_unittests.cc @@ -358,6 +358,19 @@ TEST(ModuleSpec, CheckFormat) { item_format = "\"item_format\": \"time\""; specs.push_back("," + item_default + item_format); + // wrong date-time-type format not ending with "Z" + item_default = "\"item_default\": \"2011-05-27T19:42:57\","; + item_format = "\"item_format\": \"date-time\""; + specs.push_back("," + item_default + item_format); + // wrong date-type format ending with "T" + item_default = "\"item_default\": \"2011-05-27T\","; + item_format = "\"item_format\": \"date\""; + specs.push_back("," + item_default + item_format); + // wrong time-type format ending with "Z" + item_default = "\"item_default\": \"19:42:57Z\","; + item_format = "\"item_format\": \"time\""; + specs.push_back("," + item_default + item_format); + BOOST_FOREACH(std::string s, specs) { el = Element::fromJSON(json_begin + s + json_end)->get("module_spec"); EXPECT_THROW(ModuleSpec(el, true), ModuleSpecError); diff --git a/src/lib/python/isc/config/tests/module_spec_test.py b/src/lib/python/isc/config/tests/module_spec_test.py index 567cfd4945..fc53d23221 100644 --- a/src/lib/python/isc/config/tests/module_spec_test.py +++ b/src/lib/python/isc/config/tests/module_spec_test.py @@ -352,6 +352,12 @@ class TestModuleSpec(unittest.TestCase): self.assertFalse(isc.config.module_spec._check_format('', 'date-time')) self.assertFalse(isc.config.module_spec._check_format(None, 'date-time')) self.assertFalse(isc.config.module_spec._check_format(None, None)) + # wrong date-time-type format not ending with "Z" + self.assertFalse(isc.config.module_spec._check_format('2011-05-27T19:42:57', 'date-time')) + # wrong date-type format ending with "T" + self.assertFalse(isc.config.module_spec._check_format('2011-05-27T', 'date')) + # wrong time-type format ending with "Z" + self.assertFalse(isc.config.module_spec._check_format('19:42:57Z', 'time')) def test_validate_type(self): errors = [] From 326885a3f98c49a848a67dc48db693b8bcc7b508 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:55:55 +0900 Subject: [PATCH 470/974] [trac930] remove unneeded specfile "stats-schema.spec" --- src/bin/stats/Makefile.am | 4 +- src/bin/stats/stats-schema.spec | 86 --------------------------------- 2 files changed, 2 insertions(+), 88 deletions(-) delete mode 100644 src/bin/stats/stats-schema.spec diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index e830f65d60..49cadad4c9 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -5,7 +5,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@ pkglibexec_SCRIPTS = b10-stats b10-stats-httpd b10_statsdir = $(pkgdatadir) -b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec +b10_stats_DATA = stats.spec stats-httpd.spec b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl pyexec_DATA = stats_messages.py stats_httpd_messages.py @@ -16,7 +16,7 @@ CLEANFILES += stats_httpd_messages.py stats_httpd_messages.pyc man_MANS = b10-stats.8 b10-stats-httpd.8 EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml -EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec +EXTRA_DIST += stats.spec stats-httpd.spec EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl EXTRA_DIST += stats_messages.mes stats_httpd_messages.mes diff --git a/src/bin/stats/stats-schema.spec b/src/bin/stats/stats-schema.spec deleted file mode 100644 index 52528657e8..0000000000 --- a/src/bin/stats/stats-schema.spec +++ /dev/null @@ -1,86 +0,0 @@ -{ - "module_spec": { - "module_name": "Stats", - "module_description": "Statistics data schema", - "config_data": [ - { - "item_name": "report_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "Report time", - "item_description": "A date time when stats module reports", - "item_format": "date-time" - }, - { - "item_name": "bind10.boot_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "bind10.BootTime", - "item_description": "A date time when bind10 process starts initially", - "item_format": "date-time" - }, - { - "item_name": "stats.boot_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "stats.BootTime", - "item_description": "A date time when the stats module starts initially or when the stats module restarts", - "item_format": "date-time" - }, - { - "item_name": "stats.start_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "stats.StartTime", - "item_description": "A date time when the stats module starts collecting data or resetting values last time", - "item_format": "date-time" - }, - { - "item_name": "stats.last_update_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "stats.LastUpdateTime", - "item_description": "The latest date time when the stats module receives from other modules like auth server or boss process and so on", - "item_format": "date-time" - }, - { - "item_name": "stats.timestamp", - "item_type": "real", - "item_optional": false, - "item_default": 0.0, - "item_title": "stats.Timestamp", - "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)" - }, - { - "item_name": "stats.lname", - "item_type": "string", - "item_optional": false, - "item_default": "", - "item_title": "stats.LocalName", - "item_description": "A localname of stats module given via CC protocol" - }, - { - "item_name": "auth.queries.tcp", - "item_type": "integer", - "item_optional": false, - "item_default": 0, - "item_title": "auth.queries.tcp", - "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" - }, - { - "item_name": "auth.queries.udp", - "item_type": "integer", - "item_optional": false, - "item_default": 0, - "item_title": "auth.queries.udp", - "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially" - } - ], - "commands": [] - } -} From 1768e822df82943f075ebed023b72d225b3b0216 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:57:41 +0900 Subject: [PATCH 471/974] [trac930] remove unneeded mockups, fake modules and dummy data --- configure.ac | 7 - src/bin/stats/tests/fake_select.py | 43 ---- src/bin/stats/tests/fake_socket.py | 70 ------ src/bin/stats/tests/fake_time.py | 47 ---- src/bin/stats/tests/http/Makefile.am | 6 - src/bin/stats/tests/http/__init__.py | 0 src/bin/stats/tests/http/server.py | 96 ------- src/bin/stats/tests/isc/Makefile.am | 8 - src/bin/stats/tests/isc/__init__.py | 0 src/bin/stats/tests/isc/cc/Makefile.am | 7 - src/bin/stats/tests/isc/cc/__init__.py | 1 - src/bin/stats/tests/isc/cc/session.py | 148 ----------- src/bin/stats/tests/isc/config/Makefile.am | 7 - src/bin/stats/tests/isc/config/__init__.py | 1 - src/bin/stats/tests/isc/config/ccsession.py | 249 ------------------- src/bin/stats/tests/isc/log/Makefile.am | 7 - src/bin/stats/tests/isc/log/__init__.py | 33 --- src/bin/stats/tests/isc/util/Makefile.am | 7 - src/bin/stats/tests/isc/util/__init__.py | 0 src/bin/stats/tests/isc/util/process.py | 21 -- src/bin/stats/tests/testdata/Makefile.am | 1 - src/bin/stats/tests/testdata/stats_test.spec | 19 -- 22 files changed, 778 deletions(-) delete mode 100644 src/bin/stats/tests/fake_select.py delete mode 100644 src/bin/stats/tests/fake_socket.py delete mode 100644 src/bin/stats/tests/fake_time.py delete mode 100644 src/bin/stats/tests/http/Makefile.am delete mode 100644 src/bin/stats/tests/http/__init__.py delete mode 100644 src/bin/stats/tests/http/server.py delete mode 100644 src/bin/stats/tests/isc/Makefile.am delete mode 100644 src/bin/stats/tests/isc/__init__.py delete mode 100644 src/bin/stats/tests/isc/cc/Makefile.am delete mode 100644 src/bin/stats/tests/isc/cc/__init__.py delete mode 100644 src/bin/stats/tests/isc/cc/session.py delete mode 100644 src/bin/stats/tests/isc/config/Makefile.am delete mode 100644 src/bin/stats/tests/isc/config/__init__.py delete mode 100644 src/bin/stats/tests/isc/config/ccsession.py delete mode 100644 src/bin/stats/tests/isc/log/Makefile.am delete mode 100644 src/bin/stats/tests/isc/log/__init__.py delete mode 100644 src/bin/stats/tests/isc/util/Makefile.am delete mode 100644 src/bin/stats/tests/isc/util/__init__.py delete mode 100644 src/bin/stats/tests/isc/util/process.py delete mode 100644 src/bin/stats/tests/testdata/Makefile.am delete mode 100644 src/bin/stats/tests/testdata/stats_test.spec diff --git a/configure.ac b/configure.ac index 6e129b6093..ee990eb412 100644 --- a/configure.ac +++ b/configure.ac @@ -801,13 +801,6 @@ AC_CONFIG_FILES([Makefile src/bin/zonemgr/tests/Makefile src/bin/stats/Makefile src/bin/stats/tests/Makefile - src/bin/stats/tests/isc/Makefile - src/bin/stats/tests/isc/cc/Makefile - src/bin/stats/tests/isc/config/Makefile - src/bin/stats/tests/isc/util/Makefile - src/bin/stats/tests/isc/log/Makefile - src/bin/stats/tests/testdata/Makefile - src/bin/stats/tests/http/Makefile src/bin/usermgr/Makefile src/bin/tests/Makefile src/lib/Makefile diff --git a/src/bin/stats/tests/fake_select.py b/src/bin/stats/tests/fake_select.py deleted file mode 100644 index ca0ca82619..0000000000 --- a/src/bin/stats/tests/fake_select.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 mock-up module of select - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import fake_socket -import errno - -class error(Exception): - pass - -def select(rlst, wlst, xlst, timeout): - if type(timeout) != int and type(timeout) != float: - raise TypeError("Error: %s must be integer or float" - % timeout.__class__.__name__) - for s in rlst + wlst + xlst: - if type(s) != fake_socket.socket: - raise TypeError("Error: %s must be a dummy socket" - % s.__class__.__name__) - s._called = s._called + 1 - if s._called > 3: - raise error("Something is happened!") - elif s._called > 2: - raise error(errno.EINTR) - return (rlst, wlst, xlst) diff --git a/src/bin/stats/tests/fake_socket.py b/src/bin/stats/tests/fake_socket.py deleted file mode 100644 index 4e3a4581a5..0000000000 --- a/src/bin/stats/tests/fake_socket.py +++ /dev/null @@ -1,70 +0,0 @@ -# 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 mock-up module of socket - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import re - -AF_INET = 'AF_INET' -AF_INET6 = 'AF_INET6' -_ADDRFAMILY = AF_INET -has_ipv6 = True -_CLOSED = False - -class gaierror(Exception): - pass - -class error(Exception): - pass - -class socket: - - def __init__(self, family=None): - if family is None: - self.address_family = _ADDRFAMILY - else: - self.address_family = family - self._closed = _CLOSED - if self._closed: - raise error('socket is already closed!') - self._called = 0 - - def close(self): - self._closed = True - - def fileno(self): - return id(self) - - def bind(self, server_class): - (self.server_address, self.server_port) = server_class - if self.address_family not in set([AF_INET, AF_INET6]): - raise error("Address family not supported by protocol: %s" % self.address_family) - if self.address_family == AF_INET6 and not has_ipv6: - raise error("Address family not supported in this machine: %s has_ipv6: %s" - % (self.address_family, str(has_ipv6))) - if self.address_family == AF_INET and re.search(':', self.server_address) is not None: - raise gaierror("Address family for hostname not supported : %s %s" % (self.server_address, self.address_family)) - if self.address_family == AF_INET6 and re.search(':', self.server_address) is None: - raise error("Cannot assign requested address : %s" % str(self.server_address)) - if type(self.server_port) is not int: - raise TypeError("an integer is required: %s" % str(self.server_port)) - if self.server_port < 0 or self.server_port > 65535: - raise OverflowError("port number must be 0-65535.: %s" % str(self.server_port)) diff --git a/src/bin/stats/tests/fake_time.py b/src/bin/stats/tests/fake_time.py deleted file mode 100644 index 65e02371d6..0000000000 --- a/src/bin/stats/tests/fake_time.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) 2010 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. - -__version__ = "$Revision$" - -# This is a dummy time class against a Python standard time class. -# It is just testing use only. -# Other methods which time class has is not implemented. -# (This class isn't orderloaded for time class.) - -# These variables are constant. These are example. -_TEST_TIME_SECS = 1283364938.229088 -_TEST_TIME_STRF = '2010-09-01T18:15:38Z' - -def time(): - """ - This is a dummy time() method against time.time() - """ - # return float constant value - return _TEST_TIME_SECS - -def gmtime(): - """ - This is a dummy gmtime() method against time.gmtime() - """ - # always return nothing - return None - -def strftime(*arg): - """ - This is a dummy gmtime() method against time.gmtime() - """ - return _TEST_TIME_STRF - - diff --git a/src/bin/stats/tests/http/Makefile.am b/src/bin/stats/tests/http/Makefile.am deleted file mode 100644 index 79263a98b4..0000000000 --- a/src/bin/stats/tests/http/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -EXTRA_DIST = __init__.py server.py -CLEANFILES = __init__.pyc server.pyc -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/http/__init__.py b/src/bin/stats/tests/http/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/bin/stats/tests/http/server.py b/src/bin/stats/tests/http/server.py deleted file mode 100644 index 70ed6faa30..0000000000 --- a/src/bin/stats/tests/http/server.py +++ /dev/null @@ -1,96 +0,0 @@ -# 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 mock-up module of http.server - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import fake_socket - -class DummyHttpResponse: - def __init__(self, path): - self.path = path - self.headers={} - self.log = "" - - def _write_log(self, msg): - self.log = self.log + msg - -class HTTPServer: - """ - A mock-up class of http.server.HTTPServer - """ - address_family = fake_socket.AF_INET - def __init__(self, server_class, handler_class): - self.socket = fake_socket.socket(self.address_family) - self.server_class = server_class - self.socket.bind(self.server_class) - self._handler = handler_class(None, None, self) - - def handle_request(self): - pass - - def server_close(self): - self.socket.close() - -class BaseHTTPRequestHandler: - """ - A mock-up class of http.server.BaseHTTPRequestHandler - """ - - def __init__(self, request, client_address, server): - self.path = "/path/to" - self.headers = {} - self.server = server - self.response = DummyHttpResponse(path=self.path) - self.response.write = self._write - self.wfile = self.response - - def send_response(self, code=0): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.code = code - - def send_header(self, key, value): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.headers[key] = value - - def end_headers(self): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.wrote_headers = True - - def send_error(self, code, message=None): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.code = code - self.response.body = message - - def address_string(self): - return 'dummyhost' - - def log_date_time_string(self): - return '[DD/MM/YYYY HH:MI:SS]' - - def _write(self, obj): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.body = obj.decode() - diff --git a/src/bin/stats/tests/isc/Makefile.am b/src/bin/stats/tests/isc/Makefile.am deleted file mode 100644 index d31395d404..0000000000 --- a/src/bin/stats/tests/isc/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -SUBDIRS = cc config util log -EXTRA_DIST = __init__.py -CLEANFILES = __init__.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/__init__.py b/src/bin/stats/tests/isc/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/bin/stats/tests/isc/cc/Makefile.am b/src/bin/stats/tests/isc/cc/Makefile.am deleted file mode 100644 index 67323b5f1b..0000000000 --- a/src/bin/stats/tests/isc/cc/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -EXTRA_DIST = __init__.py session.py -CLEANFILES = __init__.pyc session.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/cc/__init__.py b/src/bin/stats/tests/isc/cc/__init__.py deleted file mode 100644 index 9a3eaf6185..0000000000 --- a/src/bin/stats/tests/isc/cc/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from isc.cc.session import * diff --git a/src/bin/stats/tests/isc/cc/session.py b/src/bin/stats/tests/isc/cc/session.py deleted file mode 100644 index e16d6a9abc..0000000000 --- a/src/bin/stats/tests/isc/cc/session.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (C) 2010,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 mock-up module of isc.cc.session - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import sys -import fake_socket - -# set a dummy lname -_TEST_LNAME = '123abc@xxxx' - -class Queue(): - def __init__(self, msg=None, env={}): - self.msg = msg - self.env = env - - def dump(self): - return { 'msg': self.msg, 'env': self.env } - -class SessionError(Exception): - pass - -class SessionTimeout(Exception): - pass - -class Session: - def __init__(self, socket_file=None, verbose=False): - self._lname = _TEST_LNAME - self.message_queue = [] - self.old_message_queue = [] - try: - self._socket = fake_socket.socket() - except fake_socket.error as se: - raise SessionError(se) - self.verbose = verbose - - @property - def lname(self): - return self._lname - - def close(self): - self._socket.close() - - def _clear_queues(self): - while len(self.message_queue) > 0: - self.dequeue() - - def _next_sequence(self, que=None): - return len(self.message_queue) - - def enqueue(self, msg=None, env={}): - if self._socket._closed: - raise SessionError("Session has been closed.") - seq = self._next_sequence() - env.update({"seq": 0}) # fixed here - que = Queue(msg=msg, env=env) - self.message_queue.append(que) - if self.verbose: - sys.stdout.write("[Session] enqueue: " + str(que.dump()) + "\n") - return seq - - def dequeue(self): - if self._socket._closed: - raise SessionError("Session has been closed.") - que = None - try: - que = self.message_queue.pop(0) # always pop at index 0 - self.old_message_queue.append(que) - except IndexError: - que = Queue() - if self.verbose: - sys.stdout.write("[Session] dequeue: " + str(que.dump()) + "\n") - return que - - def get_queue(self, seq=None): - if self._socket._closed: - raise SessionError("Session has been closed.") - if seq is None: - seq = len(self.message_queue) - 1 - que = None - try: - que = self.message_queue[seq] - except IndexError: - raise IndexError - que = Queue() - if self.verbose: - sys.stdout.write("[Session] get_queue: " + str(que.dump()) + "\n") - return que - - def group_sendmsg(self, msg, group, instance="*", to="*"): - return self.enqueue(msg=msg, env={ - "type": "send", - "from": self._lname, - "to": to, - "group": group, - "instance": instance }) - - def group_recvmsg(self, nonblock=True, seq=0): - que = self.dequeue() - return que.msg, que.env - - def group_reply(self, routing, msg): - return self.enqueue(msg=msg, env={ - "type": "send", - "from": self._lname, - "to": routing["from"], - "group": routing["group"], - "instance": routing["instance"], - "reply": routing["seq"] }) - - def get_message(self, group, to='*'): - if self._socket._closed: - raise SessionError("Session has been closed.") - que = Queue() - for q in self.message_queue: - if q.env['group'] == group: - self.message_queue.remove(q) - self.old_message_queue.append(q) - que = q - if self.verbose: - sys.stdout.write("[Session] get_message: " + str(que.dump()) + "\n") - return q.msg - - def group_subscribe(self, group, instance = "*"): - if self._socket._closed: - raise SessionError("Session has been closed.") - - def group_unsubscribe(self, group, instance = "*"): - if self._socket._closed: - raise SessionError("Session has been closed.") diff --git a/src/bin/stats/tests/isc/config/Makefile.am b/src/bin/stats/tests/isc/config/Makefile.am deleted file mode 100644 index ffbecdae03..0000000000 --- a/src/bin/stats/tests/isc/config/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -EXTRA_DIST = __init__.py ccsession.py -CLEANFILES = __init__.pyc ccsession.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/config/__init__.py b/src/bin/stats/tests/isc/config/__init__.py deleted file mode 100644 index 4c49e956aa..0000000000 --- a/src/bin/stats/tests/isc/config/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from isc.config.ccsession import * diff --git a/src/bin/stats/tests/isc/config/ccsession.py b/src/bin/stats/tests/isc/config/ccsession.py deleted file mode 100644 index 50f7c1b163..0000000000 --- a/src/bin/stats/tests/isc/config/ccsession.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (C) 2010,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 mock-up module of isc.cc.session - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import json -import os -import time -from isc.cc.session import Session - -COMMAND_CONFIG_UPDATE = "config_update" - -def parse_answer(msg): - assert 'result' in msg - try: - return msg['result'][0], msg['result'][1] - except IndexError: - return msg['result'][0], None - -def create_answer(rcode, arg = None): - if arg is None: - return { 'result': [ rcode ] } - else: - return { 'result': [ rcode, arg ] } - -def parse_command(msg): - assert 'command' in msg - try: - return msg['command'][0], msg['command'][1] - except IndexError: - return msg['command'][0], None - -def create_command(command_name, params = None): - if params is None: - return {"command": [command_name]} - else: - return {"command": [command_name, params]} - -def module_spec_from_file(spec_file, check = True): - try: - file = open(spec_file) - json_str = file.read() - module_spec = json.loads(json_str) - file.close() - return ModuleSpec(module_spec['module_spec'], check) - except IOError as ioe: - raise ModuleSpecError("JSON read error: " + str(ioe)) - except ValueError as ve: - raise ModuleSpecError("JSON parse error: " + str(ve)) - except KeyError as err: - raise ModuleSpecError("Data definition has no module_spec element") - -class ModuleSpecError(Exception): - pass - -class ModuleSpec: - def __init__(self, module_spec, check = True): - # check only confi_data for testing - if check and "config_data" in module_spec: - _check_config_spec(module_spec["config_data"]) - self._module_spec = module_spec - - def get_config_spec(self): - return self._module_spec['config_data'] - - def get_commands_spec(self): - return self._module_spec['commands'] - - def get_module_name(self): - return self._module_spec['module_name'] - -def _check_config_spec(config_data): - # config data is a list of items represented by dicts that contain - # things like "item_name", depending on the type they can have - # specific subitems - """Checks a list that contains the configuration part of the - specification. Raises a ModuleSpecError if there is a - problem.""" - if type(config_data) != list: - raise ModuleSpecError("config_data is of type " + str(type(config_data)) + ", not a list of items") - for config_item in config_data: - _check_item_spec(config_item) - -def _check_item_spec(config_item): - """Checks the dict that defines one config item - (i.e. containing "item_name", "item_type", etc. - Raises a ModuleSpecError if there is an error""" - if type(config_item) != dict: - raise ModuleSpecError("item spec not a dict") - if "item_name" not in config_item: - raise ModuleSpecError("no item_name in config item") - if type(config_item["item_name"]) != str: - raise ModuleSpecError("item_name is not a string: " + str(config_item["item_name"])) - item_name = config_item["item_name"] - if "item_type" not in config_item: - raise ModuleSpecError("no item_type in config item") - item_type = config_item["item_type"] - if type(item_type) != str: - raise ModuleSpecError("item_type in " + item_name + " is not a string: " + str(type(item_type))) - if item_type not in ["integer", "real", "boolean", "string", "list", "map", "any"]: - raise ModuleSpecError("unknown item_type in " + item_name + ": " + item_type) - if "item_optional" in config_item: - if type(config_item["item_optional"]) != bool: - raise ModuleSpecError("item_default in " + item_name + " is not a boolean") - if not config_item["item_optional"] and "item_default" not in config_item: - raise ModuleSpecError("no default value for non-optional item " + item_name) - else: - raise ModuleSpecError("item_optional not in item " + item_name) - if "item_default" in config_item: - item_default = config_item["item_default"] - if (item_type == "integer" and type(item_default) != int) or \ - (item_type == "real" and type(item_default) != float) or \ - (item_type == "boolean" and type(item_default) != bool) or \ - (item_type == "string" and type(item_default) != str) or \ - (item_type == "list" and type(item_default) != list) or \ - (item_type == "map" and type(item_default) != dict): - raise ModuleSpecError("Wrong type for item_default in " + item_name) - # TODO: once we have check_type, run the item default through that with the list|map_item_spec - if item_type == "list": - if "list_item_spec" not in config_item: - raise ModuleSpecError("no list_item_spec in list item " + item_name) - if type(config_item["list_item_spec"]) != dict: - raise ModuleSpecError("list_item_spec in " + item_name + " is not a dict") - _check_item_spec(config_item["list_item_spec"]) - if item_type == "map": - if "map_item_spec" not in config_item: - raise ModuleSpecError("no map_item_sepc in map item " + item_name) - if type(config_item["map_item_spec"]) != list: - raise ModuleSpecError("map_item_spec in " + item_name + " is not a list") - for map_item in config_item["map_item_spec"]: - if type(map_item) != dict: - raise ModuleSpecError("map_item_spec element is not a dict") - _check_item_spec(map_item) - if 'item_format' in config_item and 'item_default' in config_item: - item_format = config_item["item_format"] - item_default = config_item["item_default"] - if not _check_format(item_default, item_format): - raise ModuleSpecError( - "Wrong format for " + str(item_default) + " in " + str(item_name)) - -def _check_format(value, format_name): - """Check if specified value and format are correct. Return True if - is is correct.""" - # TODO: should be added other format types if necessary - time_formats = { 'date-time' : "%Y-%m-%dT%H:%M:%SZ", - 'date' : "%Y-%m-%d", - 'time' : "%H:%M:%S" } - for fmt in time_formats: - if format_name == fmt: - try: - time.strptime(value, time_formats[fmt]) - return True - except (ValueError, TypeError): - break - return False - -class ModuleCCSessionError(Exception): - pass - -class DataNotFoundError(Exception): - pass - -class ConfigData: - def __init__(self, specification): - self.specification = specification - - def get_value(self, identifier): - """Returns a tuple where the first item is the value at the - given identifier, and the second item is absolutely False - even if the value is an unset default or not. Raises an - DataNotFoundError if the identifier is not found in the - specification file. - *** NOTE *** - There are some differences from the original method. This - method never handles local settings like the original - method. But these different behaviors aren't so big issues - for a mock-up method of stats_httpd because stats_httpd - calls this method at only first.""" - for config_map in self.get_module_spec().get_config_spec(): - if config_map['item_name'] == identifier: - if 'item_default' in config_map: - return config_map['item_default'], False - raise DataNotFoundError("item_name %s is not found in the specfile" % identifier) - - def get_module_spec(self): - return self.specification - -class ModuleCCSession(ConfigData): - def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None): - module_spec = module_spec_from_file(spec_file_name) - ConfigData.__init__(self, module_spec) - self._module_name = module_spec.get_module_name() - self.set_config_handler(config_handler) - self.set_command_handler(command_handler) - if not cc_session: - self._session = Session(verbose=True) - else: - self._session = cc_session - - def start(self): - pass - - def close(self): - self._session.close() - - def check_command(self, nonblock=True): - msg, env = self._session.group_recvmsg(nonblock) - if not msg or 'result' in msg: - return - cmd, arg = parse_command(msg) - answer = None - if cmd == COMMAND_CONFIG_UPDATE and self._config_handler: - answer = self._config_handler(arg) - elif env['group'] == self._module_name and self._command_handler: - answer = self._command_handler(cmd, arg) - if answer: - self._session.group_reply(env, answer) - - def set_config_handler(self, config_handler): - self._config_handler = config_handler - # should we run this right now since we've changed the handler? - - def set_command_handler(self, command_handler): - self._command_handler = command_handler - - def get_module_spec(self): - return self.specification - - def get_socket(self): - return self._session._socket - diff --git a/src/bin/stats/tests/isc/log/Makefile.am b/src/bin/stats/tests/isc/log/Makefile.am deleted file mode 100644 index 457b9de1c2..0000000000 --- a/src/bin/stats/tests/isc/log/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -EXTRA_DIST = __init__.py -CLEANFILES = __init__.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/log/__init__.py b/src/bin/stats/tests/isc/log/__init__.py deleted file mode 100644 index 641cf790c1..0000000000 --- a/src/bin/stats/tests/isc/log/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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 file is not installed. The log.so is installed into the right place. -# It is only to find it in the .libs directory when we run as a test or -# from the build directory. -# But as nobody gives us the builddir explicitly (and we can't use generation -# from .in file, as it would put us into the builddir and we wouldn't be found) -# we guess from current directory. Any idea for something better? This should -# be enough for the tests, but would it work for B10_FROM_SOURCE as well? -# Should we look there? Or define something in bind10_config? - -import os -import sys - -for base in sys.path[:]: - loglibdir = os.path.join(base, 'isc/log/.libs') - if os.path.exists(loglibdir): - sys.path.insert(0, loglibdir) - -from log import * diff --git a/src/bin/stats/tests/isc/util/Makefile.am b/src/bin/stats/tests/isc/util/Makefile.am deleted file mode 100644 index 9c74354ca3..0000000000 --- a/src/bin/stats/tests/isc/util/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -EXTRA_DIST = __init__.py process.py -CLEANFILES = __init__.pyc process.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/util/__init__.py b/src/bin/stats/tests/isc/util/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/bin/stats/tests/isc/util/process.py b/src/bin/stats/tests/isc/util/process.py deleted file mode 100644 index 0f764c1872..0000000000 --- a/src/bin/stats/tests/isc/util/process.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (C) 2010 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 dummy function of isc.util.process.rename() -""" - -def rename(name=None): - pass diff --git a/src/bin/stats/tests/testdata/Makefile.am b/src/bin/stats/tests/testdata/Makefile.am deleted file mode 100644 index 1b8df6d736..0000000000 --- a/src/bin/stats/tests/testdata/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -EXTRA_DIST = stats_test.spec diff --git a/src/bin/stats/tests/testdata/stats_test.spec b/src/bin/stats/tests/testdata/stats_test.spec deleted file mode 100644 index 8136756440..0000000000 --- a/src/bin/stats/tests/testdata/stats_test.spec +++ /dev/null @@ -1,19 +0,0 @@ -{ - "module_spec": { - "module_name": "Stats", - "module_description": "Stats daemon", - "config_data": [], - "commands": [ - { - "command_name": "status", - "command_description": "identify whether stats module is alive or not", - "command_args": [] - }, - { - "command_name": "the_dummy", - "command_description": "this is for testing", - "command_args": [] - } - ] - } -} From 1aa728ddf691657611680385c920e3a7bd5fee12 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:00:30 +0900 Subject: [PATCH 472/974] [trac930] add utilities and mock-up modules for unittests of statistics modules and change some environ variables (PYTHONPATH, CONFIG_TESTDATA_PATH) in Makefile test_utilies.py internally calls msgq, cfgmgr and some mock modules with threads for as real situation as possible. --- src/bin/stats/tests/Makefile.am | 8 +- src/bin/stats/tests/test_utils.py | 293 ++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 src/bin/stats/tests/test_utils.py diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index dad6c48bbc..19f6117334 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -1,8 +1,7 @@ -SUBDIRS = isc http testdata PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ PYTESTS = b10-stats_test.py b10-stats-httpd_test.py -EXTRA_DIST = $(PYTESTS) fake_time.py fake_socket.py fake_select.py -CLEANFILES = fake_time.pyc fake_socket.pyc fake_select.pyc +EXTRA_DIST = $(PYTESTS) test_utils.py +CLEANFILES = test_utils.pyc # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. @@ -21,8 +20,9 @@ endif for pytest in $(PYTESTS) ; do \ 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/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \ + env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests:$(abs_top_builddir)/src/bin/msgq:$(abs_top_builddir)/src/lib/python/isc/config \ B10_FROM_SOURCE=$(abs_top_srcdir) \ + CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/tests/testdata \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py new file mode 100644 index 0000000000..bd23182d2c --- /dev/null +++ b/src/bin/stats/tests/test_utils.py @@ -0,0 +1,293 @@ +""" +Utilities and mock modules for unittests of statistics modules + +""" +import os +import io +import time +import sys +import threading +import tempfile + +import msgq +import isc.config.cfgmgr +import stats +import stats_httpd + +# TODO: consider appropriate timeout seconds +TIMEOUT_SEC = 0.01 + +def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC*2): + if not session: + cc_session = isc.cc.Session() + else: + cc_session = session + orig_timeout = cc_session.get_timeout() + cc_session.set_timeout(timeout * 1000) + command = isc.config.ccsession.create_command(command_name, params) + seq = cc_session.group_sendmsg(command, module_name) + try: + (answer, env) = cc_session.group_recvmsg(nonblock, seq) + if answer: + return isc.config.ccsession.parse_answer(answer) + except isc.cc.SessionTimeout: + pass + finally: + if not session: + cc_session.close() + else: + cc_session.set_timeout(orig_timeout) + +def send_shutdown(module_name): + return send_command("shutdown", module_name) + +class ThreadingServerManager: + def __init__(self, server_class, verbose): + self.server_class = server_class + self.server_class_name = server_class.__name__ + self.verbose = verbose + self.server = self.server_class(self.verbose) + self.server._thread = threading.Thread( + name=self.server_class_name, target=self.server.run) + self.server._thread.daemon = True + + def run(self): + self.server._thread.start() + self.server._started.wait() + + def shutdown(self): + self.server.shutdown() + self.server._thread.join(TIMEOUT_SEC) + +class MockMsgq: + def __init__(self, verbose): + self.verbose = verbose + self._started = threading.Event() + self.msgq = msgq.MsgQ(None, verbose) + result = self.msgq.setup() + if result: + sys.exit("Error on Msgq startup: %s" % result) + + def run(self): + self._started.set() + try: + self.msgq.run() + except Exception: + pass + finally: + self.shutdown() + + def shutdown(self): + self.msgq.shutdown() + +class MockCfgmgr: + def __init__(self, verbose): + self._started = threading.Event() + self.cfgmgr = isc.config.cfgmgr.ConfigManager( + os.environ['CONFIG_TESTDATA_PATH'], "b10-config.db") + self.cfgmgr.read_config() + + def run(self): + self._started.set() + try: + self.cfgmgr.run() + finally: + self.shutdown() + + def shutdown(self): + self.cfgmgr.running = False + +class MockBoss: + spec_str = """\ +{ + "module_spec": { + "module_name": "Boss", + "module_description": "Mock Master process", + "config_data": [], + "commands": [ + { + "command_name": "sendstats", + "command_description": "Send data to a statistics module at once", + "command_args": [] + } + ], + "statistics": [ + { + "item_name": "boot_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Boot time", + "item_description": "A date time when bind10 process starts initially", + "item_format": "date-time" + } + ] + } +} +""" + _BASETIME = (2011, 6, 22, 8, 14, 8, 2, 173, 0) + + def __init__(self, verbose): + self.verbose = verbose + self._started = threading.Event() + self.running = False + self.spec_file = io.StringIO(self.spec_str) + # create ModuleCCSession object + self.mccs = isc.config.ModuleCCSession( + self.spec_file, + self.config_handler, + self.command_handler) + self.spec_file.close() + self.cc_session = self.mccs._session + self.got_command_name = '' + + def run(self): + self.mccs.start() + self.running = True + self._started.set() + while self.running: + self.mccs.check_command(False) + + def shutdown(self): + self.running = False + + def config_handler(self, new_config): + return isc.config.create_answer(0) + + def command_handler(self, command, *args, **kwargs): + self.got_command_name = command + if command == 'sendstats': + params = { "owner": "Boss", + "data": { + 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', self._BASETIME) + } + } + return send_command("set", "Stats", params=params, session=self.cc_session) + return isc.config.create_answer(1, "Unknown Command") + +class MockAuth: + spec_str = """\ +{ + "module_spec": { + "module_name": "Auth", + "module_description": "Mock Authoritative service", + "config_data": [], + "commands": [ + { + "command_name": "sendstats", + "command_description": "Send data to a statistics module at once", + "command_args": [] + } + ], + "statistics": [ + { + "item_name": "queries.tcp", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "Queries TCP ", + "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" + }, + { + "item_name": "queries.udp", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "Queries UDP", + "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially" + } + ] + } +} +""" + def __init__(self, verbose): + self.verbose = verbose + self._started = threading.Event() + self.running = False + self.spec_file = io.StringIO(self.spec_str) + # create ModuleCCSession object + self.mccs = isc.config.ModuleCCSession( + self.spec_file, + self.config_handler, + self.command_handler) + self.spec_file.close() + self.cc_session = self.mccs._session + self.got_command_name = '' + self.queries_tcp = 3 + self.queries_udp = 2 + + def run(self): + self.mccs.start() + self.running = True + self._started.set() + while self.running: + self.mccs.check_command(False) + + def shutdown(self): + self.running = False + + def config_handler(self, new_config): + return isc.config.create_answer(0) + + def command_handler(self, command, *args, **kwargs): + self.got_command_name = command + if command == 'sendstats': + params = { "owner": "Auth", + "data": { 'queries.tcp': self.queries_tcp, + 'queries.udp': self.queries_udp } } + return send_command("set", "Stats", params=params, session=self.cc_session) + return isc.config.create_answer(1, "Unknown Command") + +class MyStats(stats.Stats): + def __init__(self, verbose): + self._started = threading.Event() + stats.Stats.__init__(self, verbose) + + def run(self): + self._started.set() + stats.Stats.start(self) + + def shutdown(self): + send_shutdown("Stats") + +class MyStatsHttpd(stats_httpd.StatsHttpd): + def __init__(self, verbose): + self._started = threading.Event() + stats_httpd.StatsHttpd.__init__(self, verbose) + + def run(self): + self._started.set() + stats_httpd.StatsHttpd.start(self) + + def shutdown(self): + send_shutdown("StatsHttpd") + +class BaseModules: + def __init__(self, verbose): + self.verbose = verbose + self.class_name = BaseModules.__name__ + + # Change value of BIND10_MSGQ_SOCKET_FILE in environment variables + os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='unix_socket.') + # MockMsgq + self.msgq = ThreadingServerManager(MockMsgq, self.verbose) + self.msgq.run() + # MockCfgmgr + self.cfgmgr = ThreadingServerManager(MockCfgmgr, self.verbose) + self.cfgmgr.run() + # MockBoss + self.boss = ThreadingServerManager(MockBoss, self.verbose) + self.boss.run() + # MockAuth + self.auth = ThreadingServerManager(MockAuth, self.verbose) + self.auth.run() + + def shutdown(self): + # MockAuth + self.auth.shutdown() + # MockBoss + self.boss.shutdown() + # MockCfgmgr + self.cfgmgr.shutdown() + # MockMsgq + self.msgq.shutdown() From d4078d52343247b07c47370b497927a3a47a4f9a Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:12:09 +0900 Subject: [PATCH 473/974] [trac930] remove descriptions about "stats-schema.spec" and add description about new features because stats module can be requested to show statistics data schema. --- src/bin/stats/b10-stats-httpd.8 | 6 +----- src/bin/stats/b10-stats-httpd.xml | 8 +------- src/bin/stats/b10-stats.8 | 4 ---- src/bin/stats/b10-stats.xml | 6 ------ 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/bin/stats/b10-stats-httpd.8 b/src/bin/stats/b10-stats-httpd.8 index ed4aafa6c6..1206e1d791 100644 --- a/src/bin/stats/b10-stats-httpd.8 +++ b/src/bin/stats/b10-stats-httpd.8 @@ -36,7 +36,7 @@ b10-stats-httpd \- BIND 10 HTTP server for HTTP/XML interface of statistics .PP \fBb10\-stats\-httpd\fR -is a standalone HTTP server\&. It is intended for HTTP/XML interface for statistics module\&. This server process runs as a process separated from the process of the BIND 10 Stats daemon (\fBb10\-stats\fR)\&. The server is initially executed by the BIND 10 boss process (\fBbind10\fR) and eventually exited by it\&. The server is intended to be server requests by HTTP clients like web browsers and third\-party modules\&. When the server is asked, it requests BIND 10 statistics data from +is a standalone HTTP server\&. It is intended for HTTP/XML interface for statistics module\&. This server process runs as a process separated from the process of the BIND 10 Stats daemon (\fBb10\-stats\fR)\&. The server is initially executed by the BIND 10 boss process (\fBbind10\fR) and eventually exited by it\&. The server is intended to be server requests by HTTP clients like web browsers and third\-party modules\&. When the server is asked, it requests BIND 10 statistics data or its schema from \fBb10\-stats\fR, and it sends the data back in Python dictionary format and the server converts it into XML format\&. The server sends it to the HTTP client\&. The server can send three types of document, which are XML (Extensible Markup Language), XSD (XML Schema definition) and XSL (Extensible Stylesheet Language)\&. The XML document is the statistics data of BIND 10, The XSD document is the data schema of it, and The XSL document is the style sheet to be showed for the web browsers\&. There is different URL for each document\&. But please note that you would be redirected to the URL of XML document if you request the URL of the root document\&. For example, you would be redirected to http://127\&.0\&.0\&.1:8000/bind10/statistics/xml if you request http://127\&.0\&.0\&.1:8000/\&. Please see the manual and the spec file of \fBb10\-stats\fR for more details about the items of BIND 10 statistics\&. The server uses CC session in communication with @@ -66,10 +66,6 @@ bindctl(1)\&. Please see the manual of bindctl(1) about how to configure the settings\&. .PP -/usr/local/share/bind10\-devel/stats\-schema\&.spec -\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via -bindctl(1)\&. -.PP /usr/local/share/bind10\-devel/stats\-httpd\-xml\&.tpl \(em the template file of XML document\&. diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml index 34c704f509..3636d9d543 100644 --- a/src/bin/stats/b10-stats-httpd.xml +++ b/src/bin/stats/b10-stats-httpd.xml @@ -57,7 +57,7 @@ by the BIND 10 boss process (bind10) and eventually exited by it. The server is intended to be server requests by HTTP clients like web browsers and third-party modules. When the server is - asked, it requests BIND 10 statistics data from + asked, it requests BIND 10 statistics data or its schema from b10-stats, and it sends the data back in Python dictionary format and the server converts it into XML format. The server sends it to the HTTP client. The server can send three types of document, @@ -112,12 +112,6 @@ of bindctl1 about how to configure the settings.
- /usr/local/share/bind10-devel/stats-schema.spec - - — This is a spec file for data schema of - of BIND 10 statistics. This schema cannot be configured - via bindctl1. - /usr/local/share/bind10-devel/stats-httpd-xml.tpl diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8 index f69e4d37fa..2c75cbcc0e 100644 --- a/src/bin/stats/b10-stats.8 +++ b/src/bin/stats/b10-stats.8 @@ -66,10 +66,6 @@ switches to verbose mode\&. It sends verbose messages to STDOUT\&. \fBb10\-stats\fR\&. It contains commands for \fBb10\-stats\fR\&. They can be invoked via bindctl(1)\&. -.PP -/usr/local/share/bind10\-devel/stats\-schema\&.spec -\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via -bindctl(1)\&. .SH "SEE ALSO" .PP diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index f0c472dd29..bd2400a2d5 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -95,12 +95,6 @@ invoked via bindctl1. - /usr/local/share/bind10-devel/stats-schema.spec - - — This is a spec file for data schema of - of BIND 10 statistics. This schema cannot be configured - via bindctl1. - From 9261da8717a433cf20218af08d3642fbeffb7d4b Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:13:17 +0900 Subject: [PATCH 474/974] [trac930] add a column "Owner" in the table tag --- src/bin/stats/stats-httpd-xsl.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/stats/stats-httpd-xsl.tpl b/src/bin/stats/stats-httpd-xsl.tpl index 01ffdc681b..a1f6406a5a 100644 --- a/src/bin/stats/stats-httpd-xsl.tpl +++ b/src/bin/stats/stats-httpd-xsl.tpl @@ -44,6 +44,7 @@ td.title {

BIND 10 Statistics

+ From c19a295eb4125b4d2a391de65972271002412258 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:18:38 +0900 Subject: [PATCH 475/974] [trac930] remove description about removing statistics data by stats module update example format in bindctl when show command of stats module is invoked --- doc/guide/bind10-guide.html | 30 ++++++++++++++++++------------ doc/guide/bind10-guide.xml | 32 ++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html index 5754cf001e..4415d42550 100644 --- a/doc/guide/bind10-guide.html +++ b/doc/guide/bind10-guide.html @@ -664,24 +664,30 @@ This may be a temporary setting until then.

- This stats daemon provides commands to identify if it is running, - show specified or all statistics data, set values, remove data, - and reset data. + This stats daemon provides commands to identify if it is + running, show specified or all statistics data, show specified + or all statistics data schema, and set specified statistics + data. For example, using bindctl:

 > Stats show
 {
-    "auth.queries.tcp": 1749,
-    "auth.queries.udp": 867868,
-    "bind10.boot_time": "2011-01-20T16:59:03Z",
-    "report_time": "2011-01-20T17:04:06Z",
-    "stats.boot_time": "2011-01-20T16:59:05Z",
-    "stats.last_update_time": "2011-01-20T17:04:05Z",
-    "stats.lname": "4d3869d9_a@jreed.example.net",
-    "stats.start_time": "2011-01-20T16:59:05Z",
-    "stats.timestamp": 1295543046.823504
+    "Auth": {
+        "queries.tcp": 1749,
+        "queries.udp": 867868
+    },
+    "Boss": {
+        "boot_time": "2011-01-20T16:59:03Z"
+    },
+    "Stats": {
+        "boot_time": "2011-01-20T16:59:05Z",
+        "last_update_time": "2011-01-20T17:04:05Z",
+        "lname": "4d3869d9_a@jreed.example.net",
+        "report_time": "2011-01-20T17:04:06Z",
+        "timestamp": 1295543046.823504
+    }
 }
        

Chapter 14. Logging

diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index ef66f3d3fb..4883bb0a29 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1453,24 +1453,32 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - This stats daemon provides commands to identify if it is running, - show specified or all statistics data, set values, remove data, - and reset data. + + This stats daemon provides commands to identify if it is + running, show specified or all statistics data, show specified + or all statistics data schema, and set specified statistics + data. For example, using bindctl: + > Stats show { - "auth.queries.tcp": 1749, - "auth.queries.udp": 867868, - "bind10.boot_time": "2011-01-20T16:59:03Z", - "report_time": "2011-01-20T17:04:06Z", - "stats.boot_time": "2011-01-20T16:59:05Z", - "stats.last_update_time": "2011-01-20T17:04:05Z", - "stats.lname": "4d3869d9_a@jreed.example.net", - "stats.start_time": "2011-01-20T16:59:05Z", - "stats.timestamp": 1295543046.823504 + "Auth": { + "queries.tcp": 1749, + "queries.udp": 867868 + }, + "Boss": { + "boot_time": "2011-01-20T16:59:03Z" + }, + "Stats": { + "boot_time": "2011-01-20T16:59:05Z", + "last_update_time": "2011-01-20T17:04:05Z", + "lname": "4d3869d9_a@jreed.example.net", + "report_time": "2011-01-20T17:04:06Z", + "timestamp": 1295543046.823504 + } } From 0b235902f38d611606d44661506f32baf266fdda Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:21:49 +0900 Subject: [PATCH 476/974] [trac930] update argument name and argument format of set command in auth module and boss module and also update related unittests of their modules --- src/bin/auth/statistics.cc | 7 ++++--- src/bin/auth/tests/statistics_unittest.cc | 8 +++++--- src/bin/bind10/bind10_src.py.in | 7 ++++--- src/bin/bind10/tests/bind10_test.py.in | 5 +++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc index 76e50074fc..444fb8b35b 100644 --- a/src/bin/auth/statistics.cc +++ b/src/bin/auth/statistics.cc @@ -67,10 +67,11 @@ AuthCountersImpl::submitStatistics() const { } std::stringstream statistics_string; statistics_string << "{\"command\": [\"set\"," - << "{ \"stats_data\": " - << "{ \"auth.queries.udp\": " + << "{ \"owner\": \"Auth\"," + << " \"data\":" + << "{ \"queries.udp\": " << counters_.at(AuthCounters::COUNTER_UDP_QUERY) - << ", \"auth.queries.tcp\": " + << ", \"queries.tcp\": " << counters_.at(AuthCounters::COUNTER_TCP_QUERY) << " }" << "}" diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc index 9a3dded837..cd2755b110 100644 --- a/src/bin/auth/tests/statistics_unittest.cc +++ b/src/bin/auth/tests/statistics_unittest.cc @@ -201,12 +201,14 @@ TEST_F(AuthCountersTest, submitStatistics) { // Command is "set". EXPECT_EQ("set", statistics_session_.sent_msg->get("command") ->get(0)->stringValue()); + EXPECT_EQ("Auth", statistics_session_.sent_msg->get("command") + ->get(1)->get("owner")->stringValue()); ConstElementPtr statistics_data = statistics_session_.sent_msg ->get("command")->get(1) - ->get("stats_data"); + ->get("data"); // UDP query counter is 2 and TCP query counter is 1. - EXPECT_EQ(2, statistics_data->get("auth.queries.udp")->intValue()); - EXPECT_EQ(1, statistics_data->get("auth.queries.tcp")->intValue()); + EXPECT_EQ(2, statistics_data->get("queries.udp")->intValue()); + EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue()); } } diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index b497f7c922..5189802c27 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -85,7 +85,7 @@ isc.util.process.rename(sys.argv[0]) # number, and the overall BIND 10 version number (set in configure.ac). VERSION = "bind10 20110223 (BIND 10 @PACKAGE_VERSION@)" -# This is for bind10.boottime of stats module +# This is for boot_time of Boss _BASETIME = time.gmtime() class RestartSchedule: @@ -319,8 +319,9 @@ class BoB: elif command == "sendstats": # send statistics data to the stats daemon immediately cmd = isc.config.ccsession.create_command( - 'set', { "stats_data": { - 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + 'set', { "owner": "Boss", + "data": { + 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) }}) seq = self.cc_session.group_sendmsg(cmd, 'Stats') self.cc_session.group_recvmsg(True, seq) diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 077190c865..dc1d6603c4 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -153,8 +153,9 @@ class TestBoB(unittest.TestCase): self.assertEqual(bob.cc_session.group, "Stats") self.assertEqual(bob.cc_session.msg, isc.config.ccsession.create_command( - 'set', { "stats_data": { - 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + "set", { "owner": "Boss", + "data": { + "boot_time": time.strftime("%Y-%m-%dT%H:%M:%SZ", _BASETIME) }})) # "ping" command self.assertEqual(bob.command_handler("ping", None), From e7b4337aeaa760947e8e7906e64077ad7aaadc66 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:33:59 +0900 Subject: [PATCH 477/974] [trac930] update spec file of stats module - update description of status command, shutdown command and show command - change argument of show command (Owner module name of statistics data can be specified) - change argument of set command (Owner module name of statistics data is always required) - add showschema command which shows statistics data schema of each module specified) - disabled reset command and remove command --- src/bin/stats/stats.spec | 75 +++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/bin/stats/stats.spec b/src/bin/stats/stats.spec index 635eb486a1..e716b62279 100644 --- a/src/bin/stats/stats.spec +++ b/src/bin/stats/stats.spec @@ -6,18 +6,51 @@ "commands": [ { "command_name": "status", - "command_description": "identify whether stats module is alive or not", + "command_description": "Show status of the stats daemon", + "command_args": [] + }, + { + "command_name": "shutdown", + "command_description": "Shut down the stats module", "command_args": [] }, { "command_name": "show", - "command_description": "show the specified/all statistics data", + "command_description": "Show the specified/all statistics data", "command_args": [ { - "item_name": "stats_item_name", + "item_name": "owner", "item_type": "string", "item_optional": true, - "item_default": "" + "item_default": "", + "item_description": "module name of the owner of the statistics data" + }, + { + "item_name": "name", + "item_type": "string", + "item_optional": true, + "item_default": "", + "item_description": "statistics item name of the owner" + } + ] + }, + { + "command_name": "showschema", + "command_description": "show the specified/all statistics shema", + "command_args": [ + { + "item_name": "owner", + "item_type": "string", + "item_optional": true, + "item_default": "", + "item_description": "module name of the owner of the statistics data" + }, + { + "item_name": "name", + "item_type": "string", + "item_optional": true, + "item_default": "", + "item_description": "statistics item name of the owner" } ] }, @@ -26,35 +59,21 @@ "command_description": "set the value of specified name in statistics data", "command_args": [ { - "item_name": "stats_data", + "item_name": "owner", + "item_type": "string", + "item_optional": false, + "item_default": "", + "item_description": "module name of the owner of the statistics data" + }, + { + "item_name": "data", "item_type": "map", "item_optional": false, "item_default": {}, + "item_description": "statistics data set of the owner", "map_item_spec": [] } ] - }, - { - "command_name": "remove", - "command_description": "remove the specified name from statistics data", - "command_args": [ - { - "item_name": "stats_item_name", - "item_type": "string", - "item_optional": false, - "item_default": "" - } - ] - }, - { - "command_name": "reset", - "command_description": "reset all statistics data to default values except for several constant names", - "command_args": [] - }, - { - "command_name": "shutdown", - "command_description": "Shut down the stats module", - "command_args": [] } ], "statistics": [ @@ -100,7 +119,7 @@ "item_default": "", "item_title": "Local Name", "item_description": "A localname of stats module given via CC protocol" - } + } ] } } From daa1d6dd07292142d3dec5928583b0ab1da89adf Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 19:40:15 +0900 Subject: [PATCH 478/974] [trac930] - remove "stats-schema.spec" setting and getting statistics data schema via this spec file - add "version" item in DEFAULT_CONFIG - get the address family by socket.getaddrinfo function with specified server_address in advance, and create HttpServer object once, in stead of creating double HttpServer objects for IPv6 and IPv4 in the prior code (It is aimed for avoiding to fail to close the once opened sockets.) - open HTTP port in start method - avoid calling config_handler recursively in the except statement - create XML, XSD, XSL documents after getting statistics data and schema from remote stats module via CC session - definitely close once opened template file object --- src/bin/stats/stats_httpd.py.in | 227 +++++++++++++++++--------------- 1 file changed, 120 insertions(+), 107 deletions(-) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index 74298cf288..cc9c6045c2 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -57,7 +57,6 @@ else: BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd.spec" -SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec" XML_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xml.tpl" XSD_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsd.tpl" XSL_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsl.tpl" @@ -69,7 +68,7 @@ XSD_URL_PATH = '/bind10/statistics/xsd' XSL_URL_PATH = '/bind10/statistics/xsl' # TODO: This should be considered later. XSD_NAMESPACE = 'http://bind10.isc.org' + XSD_URL_PATH -DEFAULT_CONFIG = dict(listen_on=[('127.0.0.1', 8000)]) +DEFAULT_CONFIG = dict(version=0, listen_on=[('127.0.0.1', 8000)]) # Assign this process name isc.util.process.rename() @@ -161,8 +160,6 @@ class StatsHttpd: self.httpd = [] self.open_mccs() self.load_config() - self.load_templates() - self.open_httpd() def open_mccs(self): """Opens a ModuleCCSession object""" @@ -171,10 +168,6 @@ class StatsHttpd: self.mccs = isc.config.ModuleCCSession( SPECFILE_LOCATION, self.config_handler, self.command_handler) self.cc_session = self.mccs._session - # read spec file of stats module and subscribe 'Stats' - self.stats_module_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION) - self.stats_config_spec = self.stats_module_spec.get_config_spec() - self.stats_module_name = self.stats_module_spec.get_module_name() def close_mccs(self): """Closes a ModuleCCSession object""" @@ -208,45 +201,41 @@ class StatsHttpd: for addr in self.http_addrs: self.httpd.append(self._open_httpd(addr)) - def _open_httpd(self, server_address, address_family=None): + def _open_httpd(self, server_address): + httpd = None try: - # try IPv6 at first - if address_family is not None: - HttpServer.address_family = address_family - elif socket.has_ipv6: - HttpServer.address_family = socket.AF_INET6 + # get address family for the server_address before + # creating HttpServer object + address_family = socket.getaddrinfo(*server_address)[0][0] + HttpServer.address_family = address_family httpd = HttpServer( server_address, HttpHandler, self.xml_handler, self.xsd_handler, self.xsl_handler, self.write_log) - except (socket.gaierror, socket.error, - OverflowError, TypeError) as err: - # try IPv4 next - if HttpServer.address_family == socket.AF_INET6: - httpd = self._open_httpd(server_address, socket.AF_INET) - else: - raise HttpServerError( - "Invalid address %s, port %s: %s: %s" % - (server_address[0], server_address[1], - err.__class__.__name__, err)) - else: logger.info(STATHTTPD_STARTED, server_address[0], server_address[1]) - return httpd + return httpd + except (socket.gaierror, socket.error, + OverflowError, TypeError) as err: + if httpd: + httpd.server_close() + raise HttpServerError( + "Invalid address %s, port %s: %s: %s" % + (server_address[0], server_address[1], + err.__class__.__name__, err)) def close_httpd(self): """Closes sockets for HTTP""" - if len(self.httpd) == 0: - return - for ht in self.httpd: + while len(self.httpd)>0: + ht = self.httpd.pop() logger.info(STATHTTPD_CLOSING, ht.server_address[0], ht.server_address[1]) ht.server_close() - self.httpd = [] def start(self): """Starts StatsHttpd objects to run. Waiting for client requests by using select.select functions""" + self.open_httpd() self.mccs.start() self.running = True while self.running: @@ -310,7 +299,8 @@ class StatsHttpd: except HttpServerError as err: logger.error(STATHTTPD_SERVER_ERROR, err) # restore old config - self.config_handler(old_config) + self.load_config(old_config) + self.open_httpd() return isc.config.ccsession.create_answer( 1, "[b10-stats-httpd] %s" % err) else: @@ -341,8 +331,7 @@ class StatsHttpd: the data which obtains from it""" try: seq = self.cc_session.group_sendmsg( - isc.config.ccsession.create_command('show'), - self.stats_module_name) + isc.config.ccsession.create_command('show'), 'Stats') (answer, env) = self.cc_session.group_recvmsg(False, seq) if answer: (rcode, value) = isc.config.ccsession.parse_answer(answer) @@ -357,34 +346,82 @@ class StatsHttpd: raise StatsHttpdError("Stats module: %s" % str(value)) def get_stats_spec(self): - """Just returns spec data""" - return self.stats_config_spec + """Requests statistics data to the Stats daemon and returns + the data which obtains from it""" + try: + seq = self.cc_session.group_sendmsg( + isc.config.ccsession.create_command('showschema'), 'Stats') + (answer, env) = self.cc_session.group_recvmsg(False, seq) + if answer: + (rcode, value) = isc.config.ccsession.parse_answer(answer) + if rcode == 0: + return value + else: + raise StatsHttpdError("Stats module: %s" % str(value)) + except (isc.cc.session.SessionTimeout, + isc.cc.session.SessionError) as err: + raise StatsHttpdError("%s: %s" % + (err.__class__.__name__, err)) - def load_templates(self): - """Setup the bodies of XSD and XSL documents to be responds to - HTTP clients. Before that it also creates XML tag structures by - using xml.etree.ElementTree.Element class and substitutes - concrete strings with parameters embed in the string.Template - object.""" + def xml_handler(self): + """Handler which requests to Stats daemon to obtain statistics + data and returns the body of XML document""" + xml_list=[] + for (mod, spec) in self.get_stats_data().items(): + if not spec: continue + elem1 = xml.etree.ElementTree.Element(str(mod)) + for (k, v) in spec.items(): + elem2 = xml.etree.ElementTree.Element(str(k)) + elem2.text = str(v) + elem1.append(elem2) + # The coding conversion is tricky. xml..tostring() of Python 3.2 + # returns bytes (not string) regardless of the coding, while + # tostring() of Python 3.1 returns a string. To support both + # cases transparently, we first make sure tostring() returns + # bytes by specifying utf-8 and then convert the result to a + # plain string (code below assume it). + xml_list.append( + str(xml.etree.ElementTree.tostring(elem1, encoding='utf-8'), + encoding='us-ascii')) + xml_string = "".join(xml_list) + self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute( + xml_string=xml_string, + xsd_namespace=XSD_NAMESPACE, + xsd_url_path=XSD_URL_PATH, + xsl_url_path=XSL_URL_PATH) + assert self.xml_body is not None + return self.xml_body + + def xsd_handler(self): + """Handler which just returns the body of XSD document""" # for XSD xsd_root = xml.etree.ElementTree.Element("all") # started with "all" tag - for item in self.get_stats_spec(): - element = xml.etree.ElementTree.Element( - "element", - dict( name=item["item_name"], - type=item["item_type"] if item["item_type"].lower() != 'real' else 'float', - minOccurs="1", - maxOccurs="1" ), - ) - annotation = xml.etree.ElementTree.Element("annotation") - appinfo = xml.etree.ElementTree.Element("appinfo") - documentation = xml.etree.ElementTree.Element("documentation") - appinfo.text = item["item_title"] - documentation.text = item["item_description"] - annotation.append(appinfo) - annotation.append(documentation) - element.append(annotation) - xsd_root.append(element) + for (mod, spec) in self.get_stats_spec().items(): + if not spec: continue + alltag = xml.etree.ElementTree.Element("all") + for item in spec: + element = xml.etree.ElementTree.Element( + "element", + dict( name=item["item_name"], + type=item["item_type"] if item["item_type"].lower() != 'real' else 'float', + minOccurs="1", + maxOccurs="1" ), + ) + annotation = xml.etree.ElementTree.Element("annotation") + appinfo = xml.etree.ElementTree.Element("appinfo") + documentation = xml.etree.ElementTree.Element("documentation") + appinfo.text = item["item_title"] + documentation.text = item["item_description"] + annotation.append(appinfo) + annotation.append(documentation) + element.append(annotation) + alltag.append(element) + + complextype = xml.etree.ElementTree.Element("complexType") + complextype.append(alltag) + mod_element = xml.etree.ElementTree.Element("element", { "name" : mod }) + mod_element.append(complextype) + xsd_root.append(mod_element) # The coding conversion is tricky. xml..tostring() of Python 3.2 # returns bytes (not string) regardless of the coding, while # tostring() of Python 3.1 returns a string. To support both @@ -398,25 +435,33 @@ class StatsHttpd: xsd_namespace=XSD_NAMESPACE ) assert self.xsd_body is not None + return self.xsd_body + def xsl_handler(self): + """Handler which just returns the body of XSL document""" # for XSL xsd_root = xml.etree.ElementTree.Element( "xsl:template", dict(match="*")) # started with xml:template tag - for item in self.get_stats_spec(): - tr = xml.etree.ElementTree.Element("tr") - td1 = xml.etree.ElementTree.Element( - "td", { "class" : "title", - "title" : item["item_description"] }) - td1.text = item["item_title"] - td2 = xml.etree.ElementTree.Element("td") - xsl_valueof = xml.etree.ElementTree.Element( - "xsl:value-of", - dict(select=item["item_name"])) - td2.append(xsl_valueof) - tr.append(td1) - tr.append(td2) - xsd_root.append(tr) + for (mod, spec) in self.get_stats_spec().items(): + if not spec: continue + for item in spec: + tr = xml.etree.ElementTree.Element("tr") + td0 = xml.etree.ElementTree.Element("td") + td0.text = str(mod) + td1 = xml.etree.ElementTree.Element( + "td", { "class" : "title", + "title" : item["item_description"] }) + td1.text = item["item_title"] + td2 = xml.etree.ElementTree.Element("td") + xsl_valueof = xml.etree.ElementTree.Element( + "xsl:value-of", + dict(select=mod+'/'+item["item_name"])) + td2.append(xsl_valueof) + tr.append(td0) + tr.append(td1) + tr.append(td2) + xsd_root.append(tr) # The coding conversion is tricky. xml..tostring() of Python 3.2 # returns bytes (not string) regardless of the coding, while # tostring() of Python 3.1 returns a string. To support both @@ -429,47 +474,15 @@ class StatsHttpd: xsl_string=xsl_string, xsd_namespace=XSD_NAMESPACE) assert self.xsl_body is not None - - def xml_handler(self): - """Handler which requests to Stats daemon to obtain statistics - data and returns the body of XML document""" - xml_list=[] - for (k, v) in self.get_stats_data().items(): - (k, v) = (str(k), str(v)) - elem = xml.etree.ElementTree.Element(k) - elem.text = v - # The coding conversion is tricky. xml..tostring() of Python 3.2 - # returns bytes (not string) regardless of the coding, while - # tostring() of Python 3.1 returns a string. To support both - # cases transparently, we first make sure tostring() returns - # bytes by specifying utf-8 and then convert the result to a - # plain string (code below assume it). - xml_list.append( - str(xml.etree.ElementTree.tostring(elem, encoding='utf-8'), - encoding='us-ascii')) - xml_string = "".join(xml_list) - self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute( - xml_string=xml_string, - xsd_namespace=XSD_NAMESPACE, - xsd_url_path=XSD_URL_PATH, - xsl_url_path=XSL_URL_PATH) - assert self.xml_body is not None - return self.xml_body - - def xsd_handler(self): - """Handler which just returns the body of XSD document""" - return self.xsd_body - - def xsl_handler(self): - """Handler which just returns the body of XSL document""" return self.xsl_body def open_template(self, file_name): """It opens a template file, and it loads all lines to a string variable and returns string. Template object includes the variable. Limitation of a file size isn't needed there.""" - lines = "".join( - open(file_name, 'r').readlines()) + f = open(file_name, 'r') + lines = "".join(f.readlines()) + f.close() assert lines is not None return string.Template(lines) From c074f6e0b72c3facf6b325b17dea1ca13a2788cc Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 19:56:24 +0900 Subject: [PATCH 479/974] [trac930] modify Stats - remove unneeded subject and listener classes - add StatsError for handling errors in Stats - add some new methods (update_modules, update_statistics_data and get_statistics_data) - modify implementations of existent commands(show and set) according changes stats.spec - remove reset and remove command because stats module couldn't manage other modules' statistics data schema - add implementation of strict validation of each statistics data (If the validation is failed, it puts out the error.) - stats module shows its PID when status command invoked - add new command showschema invokable via bindctl - set command requires arguments of owner module name and statistics item name - show and showschema commands accepts arguments of owner module name and statistics item name - exits at exit code 1 if got runtime errors - has boot time in _BASETIME --- src/bin/stats/stats.py.in | 666 ++++++++++++++++++-------------------- 1 file changed, 310 insertions(+), 356 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index ce3d9f4612..3faa3059a0 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -15,16 +15,17 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +Statistics daemon in BIND 10 + +""" import sys; sys.path.append ('@@PYTHONPATH@@') import os -import signal -import select from time import time, strftime, gmtime from optparse import OptionParser, OptionValueError -from collections import defaultdict -from isc.config.ccsession import ModuleCCSession, create_answer -from isc.cc import Session, SessionError +import isc +import isc.util.process import isc.log from stats_messages import * @@ -35,352 +36,24 @@ logger = isc.log.Logger("stats") # have #1074 DBG_STATS_MESSAGING = 30 +# This is for boot_time of Stats +_BASETIME = gmtime() + # for setproctitle -import isc.util.process isc.util.process.rename() # If B10_FROM_SOURCE is set in the environment, we use data files # from a directory relative to that, otherwise we use the ones # installed on the system if "B10_FROM_SOURCE" in os.environ: - BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + os.sep + "stats.spec" else: PREFIX = "@prefix@" DATAROOTDIR = "@datarootdir@" - BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" - BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) -SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats.spec" -SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec" - -class Singleton(type): - """ - A abstract class of singleton pattern - """ - # Because of singleton pattern: - # At the beginning of coding, one UNIX domain socket is needed - # for config manager, another socket is needed for stats module, - # then stats module might need two sockets. So I adopted the - # singleton pattern because I avoid creating multiple sockets in - # one stats module. But in the initial version stats module - # reports only via bindctl, so just one socket is needed. To use - # the singleton pattern is not important now. :( - - def __init__(self, *args, **kwargs): - type.__init__(self, *args, **kwargs) - self._instances = {} - - def __call__(self, *args, **kwargs): - if args not in self._instances: - self._instances[args]={} - kw = tuple(kwargs.items()) - if kw not in self._instances[args]: - self._instances[args][kw] = type.__call__(self, *args, **kwargs) - return self._instances[args][kw] - -class Callback(): - """ - A Callback handler class - """ - def __init__(self, name=None, callback=None, args=(), kwargs={}): - self.name = name - self.callback = callback - self.args = args - self.kwargs = kwargs - - def __call__(self, *args, **kwargs): - if not args: - args = self.args - if not kwargs: - kwargs = self.kwargs - if self.callback: - return self.callback(*args, **kwargs) - -class Subject(): - """ - A abstract subject class of observer pattern - """ - # Because of observer pattern: - # In the initial release, I'm also sure that observer pattern - # isn't definitely needed because the interface between gathering - # and reporting statistics data is single. However in the future - # release, the interfaces may be multiple, that is, multiple - # listeners may be needed. For example, one interface, which - # stats module has, is for between ''config manager'' and stats - # module, another interface is for between ''HTTP server'' and - # stats module, and one more interface is for between ''SNMP - # server'' and stats module. So by considering that stats module - # needs multiple interfaces in the future release, I adopted the - # observer pattern in stats module. But I don't have concrete - # ideas in case of multiple listener currently. - - def __init__(self): - self._listeners = [] - - def attach(self, listener): - if not listener in self._listeners: - self._listeners.append(listener) - - def detach(self, listener): - try: - self._listeners.remove(listener) - except ValueError: - pass - - def notify(self, event, modifier=None): - for listener in self._listeners: - if modifier != listener: - listener.update(event) - -class Listener(): - """ - A abstract listener class of observer pattern - """ - def __init__(self, subject): - self.subject = subject - self.subject.attach(self) - self.events = {} - - def update(self, name): - if name in self.events: - callback = self.events[name] - return callback() - - def add_event(self, event): - self.events[event.name]=event - -class SessionSubject(Subject, metaclass=Singleton): - """ - A concrete subject class which creates CC session object - """ - def __init__(self, session=None): - Subject.__init__(self) - self.session=session - self.running = False - - def start(self): - self.running = True - self.notify('start') - - def stop(self): - self.running = False - self.notify('stop') - - def check(self): - self.notify('check') - -class CCSessionListener(Listener): - """ - A concrete listener class which creates SessionSubject object and - ModuleCCSession object - """ - def __init__(self, subject): - Listener.__init__(self, subject) - self.session = subject.session - self.boot_time = get_datetime() - - # create ModuleCCSession object - self.cc_session = ModuleCCSession(SPECFILE_LOCATION, - self.config_handler, - self.command_handler, - self.session) - - self.session = self.subject.session = self.cc_session._session - - # initialize internal data - self.stats_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION).get_config_spec() - self.stats_data = self.initialize_data(self.stats_spec) - - # add event handler invoked via SessionSubject object - self.add_event(Callback('start', self.start)) - self.add_event(Callback('stop', self.stop)) - self.add_event(Callback('check', self.check)) - # don't add 'command_' suffix to the special commands in - # order to prevent executing internal command via bindctl - - # get commands spec - self.commands_spec = self.cc_session.get_module_spec().get_commands_spec() - - # add event handler related command_handler of ModuleCCSession - # invoked via bindctl - for cmd in self.commands_spec: - try: - # add prefix "command_" - name = "command_" + cmd["command_name"] - callback = getattr(self, name) - kwargs = self.initialize_data(cmd["command_args"]) - self.add_event(Callback(name=name, callback=callback, args=(), kwargs=kwargs)) - except AttributeError as ae: - logger.error(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) - - def start(self): - """ - start the cc chanel - """ - # set initial value - self.stats_data['stats.boot_time'] = self.boot_time - self.stats_data['stats.start_time'] = get_datetime() - self.stats_data['stats.last_update_time'] = get_datetime() - self.stats_data['stats.lname'] = self.session.lname - self.cc_session.start() - # request Bob to send statistics data - logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) - cmd = isc.config.ccsession.create_command("sendstats", None) - seq = self.session.group_sendmsg(cmd, 'Boss') - self.session.group_recvmsg(True, seq) - - def stop(self): - """ - stop the cc chanel - """ - return self.cc_session.close() - - def check(self): - """ - check the cc chanel - """ - return self.cc_session.check_command(False) - - def config_handler(self, new_config): - """ - handle a configure from the cc channel - """ - logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_NEW_CONFIG, - new_config) - - # do nothing currently - return create_answer(0) - - def command_handler(self, command, *args, **kwargs): - """ - handle commands from the cc channel - """ - # add 'command_' suffix in order to executing command via bindctl - name = 'command_' + command - - if name in self.events: - event = self.events[name] - return event(*args, **kwargs) - else: - return self.command_unknown(command, args) - - def command_shutdown(self, args): - """ - handle shutdown command - """ - logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND) - self.subject.running = False - return create_answer(0) - - def command_set(self, args, stats_data={}): - """ - handle set command - """ - # 'args' must be dictionary type - self.stats_data.update(args['stats_data']) - - # overwrite "stats.LastUpdateTime" - self.stats_data['stats.last_update_time'] = get_datetime() - - return create_answer(0) - - def command_remove(self, args, stats_item_name=''): - """ - handle remove command - """ - - # 'args' must be dictionary type - if args and args['stats_item_name'] in self.stats_data: - stats_item_name = args['stats_item_name'] - - logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_REMOVE_COMMAND, - stats_item_name) - - # just remove one item - self.stats_data.pop(stats_item_name) - - return create_answer(0) - - def command_show(self, args, stats_item_name=''): - """ - handle show command - """ - - # always overwrite 'report_time' and 'stats.timestamp' - # if "show" command invoked - self.stats_data['report_time'] = get_datetime() - self.stats_data['stats.timestamp'] = get_timestamp() - - # if with args - if args and args['stats_item_name'] in self.stats_data: - stats_item_name = args['stats_item_name'] - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_SHOW_NAME_COMMAND, - stats_item_name) - return create_answer(0, {stats_item_name: self.stats_data[stats_item_name]}) - - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_SHOW_ALL_COMMAND) - return create_answer(0, self.stats_data) - - def command_reset(self, args): - """ - handle reset command - """ - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_RESET_COMMAND) - - # re-initialize internal variables - self.stats_data = self.initialize_data(self.stats_spec) - - # reset initial value - self.stats_data['stats.boot_time'] = self.boot_time - self.stats_data['stats.start_time'] = get_datetime() - self.stats_data['stats.last_update_time'] = get_datetime() - self.stats_data['stats.lname'] = self.session.lname - - return create_answer(0) - - def command_status(self, args): - """ - handle status command - """ - logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND) - # just return "I'm alive." - return create_answer(0, "I'm alive.") - - def command_unknown(self, command, args): - """ - handle an unknown command - """ - logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command) - return create_answer(1, "Unknown command: '"+str(command)+"'") - - - def initialize_data(self, spec): - """ - initialize stats data - """ - def __get_init_val(spec): - if spec['item_type'] == 'null': - return None - elif spec['item_type'] == 'boolean': - return bool(spec.get('item_default', False)) - elif spec['item_type'] == 'string': - return str(spec.get('item_default', '')) - elif spec['item_type'] in set(['number', 'integer']): - return int(spec.get('item_default', 0)) - elif spec['item_type'] in set(['float', 'double', 'real']): - return float(spec.get('item_default', 0.0)) - elif spec['item_type'] in set(['list', 'array']): - return spec.get('item_default', - [ __get_init_val(s) for s in spec['list_item_spec'] ]) - elif spec['item_type'] in set(['map', 'object']): - return spec.get('item_default', - dict([ (s['item_name'], __get_init_val(s)) for s in spec['map_item_spec'] ]) ) - else: - return spec.get('item_default') - return dict([ (s['item_name'], __get_init_val(s)) for s in spec ]) + SPECFILE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + os.sep + "stats.spec" + SPECFILE_LOCATION = SPECFILE_LOCATION.replace("${datarootdir}", DATAROOTDIR)\ + .replace("${prefix}", PREFIX) def get_timestamp(): """ @@ -388,33 +61,314 @@ def get_timestamp(): """ return time() -def get_datetime(): +def get_datetime(gmt=None): """ get current datetime """ - return strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + if not gmt: gmt = gmtime() + return strftime("%Y-%m-%dT%H:%M:%SZ", gmt) -def main(session=None): +def parse_spec(spec): + """ + parse spec type data + """ + def _parse_spec(spec): + item_type = spec['item_type'] + if item_type == "integer": + return int(spec.get('item_default', 0)) + elif item_type == "real": + return float(spec.get('item_default', 0.0)) + elif item_type == "boolean": + return bool(spec.get('item_default', False)) + elif item_type == "string": + return str(spec.get('item_default', "")) + elif item_type == "list": + return spec.get( + "item_default", + [ _parse_spec(s) for s in spec["list_item_spec"] ]) + elif item_type == "map": + return spec.get( + "item_default", + dict([ (s["item_name"], _parse_spec(s)) for s in spec["map_item_spec"] ]) ) + else: + return spec.get("item_default", None) + return dict([ (s['item_name'], _parse_spec(s)) for s in spec ]) + +class Callback(): + """ + A Callback handler class + """ + def __init__(self, command=None, args=(), kwargs={}): + self.command = command + self.args = args + self.kwargs = kwargs + + def __call__(self, *args, **kwargs): + if not args: args = self.args + if not kwargs: kwargs = self.kwargs + if self.command: return self.command(*args, **kwargs) + +class StatsError(Exception): + """Exception class for Stats class""" + pass + +class Stats: + """ + Main class of stats module + """ + def __init__(self): + self.running = False + # create ModuleCCSession object + self.mccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, + self.config_handler, + self.command_handler) + self.cc_session = self.mccs._session + # get module spec + self.module_name = self.mccs.get_module_spec().get_module_name() + self.modules = {} + self.statistics_data = {} + # get commands spec + self.commands_spec = self.mccs.get_module_spec().get_commands_spec() + # add event handler related command_handler of ModuleCCSession + self.callbacks = {} + for cmd in self.commands_spec: + # add prefix "command_" + name = "command_" + cmd["command_name"] + try: + callback = getattr(self, name) + kwargs = parse_spec(cmd["command_args"]) + self.callbacks[name] = Callback(command=callback, kwargs=kwargs) + except AttributeError: + raise StatsError(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) + self.mccs.start() + + def start(self): + """ + Start stats module + """ + self.running = True + # TODO: should be added into new logging interface + # if self.verbose: + # sys.stdout.write("[b10-stats] starting\n") + + # request Bob to send statistics data + logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) + cmd = isc.config.ccsession.create_command("sendstats", None) + seq = self.cc_session.group_sendmsg(cmd, 'Boss') + self.cc_session.group_recvmsg(True, seq) + + # initialized Statistics data + errors = self.update_statistics_data( + self.module_name, + lname=self.cc_session.lname, + boot_time=get_datetime(_BASETIME) + ) + if errors: + raise StatsError("stats spec file is incorrect") + + while self.running: + self.mccs.check_command(False) + + def config_handler(self, new_config): + """ + handle a configure from the cc channel + """ + logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_NEW_CONFIG, + new_config) + # do nothing currently + return isc.config.create_answer(0) + + def command_handler(self, command, kwargs): + """ + handle commands from the cc channel + """ + name = 'command_' + command + if name in self.callbacks: + callback = self.callbacks[name] + if kwargs: + return callback(**kwargs) + else: + return callback() + else: + logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command) + return isc.config.create_answer(1, "Unknown command: '"+str(command)+"'") + + def update_modules(self): + """ + update information of each module + """ + modules = {} + seq = self.cc_session.group_sendmsg( + isc.config.ccsession.create_command( + isc.config.ccsession.COMMAND_GET_STATISTICS_SPEC), + 'ConfigManager') + (answer, env) = self.cc_session.group_recvmsg(False, seq) + if answer: + (rcode, value) = isc.config.ccsession.parse_answer(answer) + if rcode == 0: + for mod in value: + spec = { "module_name" : mod, + "statistics" : [] } + if value[mod] and type(value[mod]) is list: + spec["statistics"] = value[mod] + modules[mod] = isc.config.module_spec.ModuleSpec(spec) + modules[self.module_name] = self.mccs.get_module_spec() + self.modules = modules + + def get_statistics_data(self, owner=None, name=None): + """ + return statistics data which stats module has of each module + """ + self.update_statistics_data() + if owner and name: + try: + return self.statistics_data[owner][name] + except KeyError: + pass + elif owner: + try: + return self.statistics_data[owner] + except KeyError: + pass + elif name: + pass + else: + return self.statistics_data + + def update_statistics_data(self, owner=None, **data): + """ + change statistics date of specified module into specified data + """ + self.update_modules() + statistics_data = {} + for (name, module) in self.modules.items(): + value = parse_spec(module.get_statistics_spec()) + if module.validate_statistics(True, value): + statistics_data[name] = value + for (name, value) in self.statistics_data.items(): + if name in statistics_data: + statistics_data[name].update(value) + else: + statistics_data[name] = value + self.statistics_data = statistics_data + if owner and data: + errors = [] + try: + if self.modules[owner].validate_statistics(False, data, errors): + self.statistics_data[owner].update(data) + return + except KeyError: + errors.append('unknown module name') + return errors + + def command_status(self): + """ + handle status command + """ + logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND) + return isc.config.create_answer( + 0, "Stats is up. (PID " + str(os.getpid()) + ")") + + def command_shutdown(self): + """ + handle shutdown command + """ + logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND) + self.running = False + return isc.config.create_answer(0) + + def command_show(self, owner=None, name=None): + """ + handle show command + """ + if (owner or name): + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOW_NAME_COMMAND, + str(owner)+", "+str(name)) + else: + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOW_ALL_COMMAND) + if owner and not name: + return isc.config.create_answer(1, "item name is not specified") + errors = self.update_statistics_data( + self.module_name, + timestamp=get_timestamp(), + report_time=get_datetime() + ) + if errors: raise StatsError("stats spec file is incorrect") + ret = self.get_statistics_data(owner, name) + if ret: + return isc.config.create_answer(0, ret) + else: + return isc.config.create_answer( + 1, "specified module name and/or item name are incorrect") + + def command_showschema(self, owner=None, name=None): + """ + handle show command + """ + # TODO: should be added into new logging interface + # if self.verbose: + # sys.stdout.write("[b10-stats] 'showschema' command received\n") + self.update_modules() + schema = {} + schema_byname = {} + for mod in self.modules: + spec = self.modules[mod].get_statistics_spec() + schema_byname[mod] = {} + if spec: + schema[mod] = spec + for item in spec: + schema_byname[mod][item['item_name']] = item + if owner: + try: + if name: + return isc.config.create_answer(0, schema_byname[owner][name]) + else: + return isc.config.create_answer(0, schema[owner]) + except KeyError: + pass + else: + if name: + return isc.config.create_answer(1, "module name is not specified") + else: + return isc.config.create_answer(0, schema) + return isc.config.create_answer( + 1, "specified module name and/or item name are incorrect") + + def command_set(self, owner, data): + """ + handle set command + """ + errors = self.update_statistics_data(owner, **data) + if errors: + return isc.config.create_answer( + 1, + "specified module name and/or statistics data are incorrect: " + + ", ".join(errors)) + errors = self.update_statistics_data( + self.module_name, last_update_time=get_datetime() ) + if errors: + raise StatsError("stats spec file is incorrect") + return isc.config.create_answer(0) + +if __name__ == "__main__": try: parser = OptionParser() - parser.add_option("-v", "--verbose", dest="verbose", action="store_true", - help="display more about what is going on") + parser.add_option( + "-v", "--verbose", dest="verbose", action="store_true", + help="display more about what is going on") (options, args) = parser.parse_args() if options.verbose: isc.log.init("b10-stats", "DEBUG", 99) - subject = SessionSubject(session=session) - listener = CCSessionListener(subject) - subject.start() - while subject.running: - subject.check() - subject.stop() - + stats = Stats() + stats.start() except OptionValueError as ove: logger.fatal(STATS_BAD_OPTION_VALUE, ove) except SessionError as se: logger.fatal(STATS_CC_SESSION_ERROR, se) + # TODO: should be added into new logging interface + except StatsError as se: + sys.exit("[b10-stats] %s" % se) except KeyboardInterrupt as kie: logger.info(STATS_STOPPED_BY_KEYBOARD) - -if __name__ == "__main__": - main() From c06463cf96ea7401325a208af8ba457e661d1cec Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 20:08:22 +0900 Subject: [PATCH 480/974] [trac930] refurbish the unittests for new stats module, new stats httpd module and new mockups and utilities in test_utils.py --- src/bin/stats/tests/b10-stats-httpd_test.py | 593 ++++++---- src/bin/stats/tests/b10-stats_test.py | 1079 ++++++++----------- src/bin/stats/tests/test_utils.py | 37 +- 3 files changed, 862 insertions(+), 847 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 6d72dc2f38..ae07aa9f27 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -15,145 +15,259 @@ import unittest import os -import http.server -import string -import fake_select import imp -import sys -import fake_socket - -import isc.cc +import socket +import errno +import select +import string +import time +import threading +import http.client +import xml.etree.ElementTree +import isc import stats_httpd -stats_httpd.socket = fake_socket -stats_httpd.select = fake_select +import stats +from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC DUMMY_DATA = { - "auth.queries.tcp": 10000, - "auth.queries.udp": 12000, - "bind10.boot_time": "2011-03-04T11:59:05Z", - "report_time": "2011-03-04T11:59:19Z", - "stats.boot_time": "2011-03-04T11:59:06Z", - "stats.last_update_time": "2011-03-04T11:59:07Z", - "stats.lname": "4d70d40a_c@host", - "stats.start_time": "2011-03-04T11:59:06Z", - "stats.timestamp": 1299239959.560846 + 'Boss' : { + "boot_time": "2011-03-04T11:59:06Z" + }, + 'Auth' : { + "queries.tcp": 2, + "queries.udp": 3 + }, + 'Stats' : { + "report_time": "2011-03-04T11:59:19Z", + "boot_time": "2011-03-04T11:59:06Z", + "last_update_time": "2011-03-04T11:59:07Z", + "lname": "4d70d40a_c@host", + "timestamp": 1299239959.560846 + } } -def push_answer(stats_httpd): - stats_httpd.cc_session.group_sendmsg( - { 'result': - [ 0, DUMMY_DATA ] }, "Stats") - -def pull_query(stats_httpd): - (msg, env) = stats_httpd.cc_session.group_recvmsg() - if 'result' in msg: - (ret, arg) = isc.config.ccsession.parse_answer(msg) - else: - (ret, arg) = isc.config.ccsession.parse_command(msg) - return (ret, arg, env) - class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" def setUp(self): - self.stats_httpd = stats_httpd.StatsHttpd() - self.assertTrue(type(self.stats_httpd.httpd) is list) - self.httpd = self.stats_httpd.httpd + self.base = BaseModules() + self.stats_server = ThreadingServerManager(MyStats) + self.stats = self.stats_server.server + self.stats_server.run() + + def tearDown(self): + self.stats_server.shutdown() + self.base.shutdown() def test_do_GET(self): - for ht in self.httpd: - self._test_do_GET(ht._handler) - - def _test_do_GET(self, handler): + (address, port) = ('127.0.0.1', 65450) + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.stats_httpd = statshttpd_server.server + self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + self.assertTrue(type(self.stats_httpd.httpd) is list) + self.assertEqual(len(self.stats_httpd.httpd), 0) + statshttpd_server.run() + time.sleep(TIMEOUT_SEC*5) + client = http.client.HTTPConnection(address, port) + client._http_vsn_str = 'HTTP/1.0\n' + client.connect() # URL is '/bind10/statistics/xml' - handler.path = stats_httpd.XML_URL_PATH - push_answer(self.stats_httpd) - handler.do_GET() - (ret, arg, env) = pull_query(self.stats_httpd) - self.assertEqual(ret, "show") - self.assertIsNone(arg) - self.assertTrue('group' in env) - self.assertEqual(env['group'], 'Stats') - self.assertEqual(handler.response.code, 200) - self.assertEqual(handler.response.headers["Content-type"], "text/xml") - self.assertTrue(handler.response.headers["Content-Length"] > 0) - self.assertTrue(handler.response.wrote_headers) - self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) - self.assertTrue(handler.response.body.find(stats_httpd.XSD_URL_PATH)>0) - for (k, v) in DUMMY_DATA.items(): - self.assertTrue(handler.response.body.find(str(k))>0) - self.assertTrue(handler.response.body.find(str(v))>0) + client.putrequest('GET', stats_httpd.XML_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.getheader("Content-type"), "text/xml") + self.assertTrue(int(response.getheader("Content-Length")) > 0) + self.assertEqual(response.status, 200) + root = xml.etree.ElementTree.parse(response).getroot() + self.assertTrue(root.tag.find('stats_data') > 0) + for (k,v) in root.attrib.items(): + if k.find('schemaLocation') > 0: + self.assertEqual(v, stats_httpd.XSD_NAMESPACE + ' ' + stats_httpd.XSD_URL_PATH) + for mod in DUMMY_DATA: + for (item, value) in DUMMY_DATA[mod].items(): + self.assertIsNotNone(root.find(mod + '/' + item)) # URL is '/bind10/statitics/xsd' - handler.path = stats_httpd.XSD_URL_PATH - handler.do_GET() - self.assertEqual(handler.response.code, 200) - self.assertEqual(handler.response.headers["Content-type"], "text/xml") - self.assertTrue(handler.response.headers["Content-Length"] > 0) - self.assertTrue(handler.response.wrote_headers) - self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) - for (k, v) in DUMMY_DATA.items(): - self.assertTrue(handler.response.body.find(str(k))>0) + client.putrequest('GET', stats_httpd.XSD_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.getheader("Content-type"), "text/xml") + self.assertTrue(int(response.getheader("Content-Length")) > 0) + self.assertEqual(response.status, 200) + root = xml.etree.ElementTree.parse(response).getroot() + url_xmlschema = '{http://www.w3.org/2001/XMLSchema}' + tags = [ url_xmlschema + t for t in [ 'element', 'complexType', 'all', 'element' ] ] + xsdpath = '/'.join(tags) + self.assertTrue(root.tag.find('schema') > 0) + self.assertTrue(hasattr(root, 'attrib')) + self.assertTrue('targetNamespace' in root.attrib) + self.assertEqual(root.attrib['targetNamespace'], + stats_httpd.XSD_NAMESPACE) + for elm in root.findall(xsdpath): + self.assertIsNotNone(elm.attrib['name']) + self.assertTrue(elm.attrib['name'] in DUMMY_DATA) # URL is '/bind10/statitics/xsl' - handler.path = stats_httpd.XSL_URL_PATH - handler.do_GET() - self.assertEqual(handler.response.code, 200) - self.assertEqual(handler.response.headers["Content-type"], "text/xml") - self.assertTrue(handler.response.headers["Content-Length"] > 0) - self.assertTrue(handler.response.wrote_headers) - self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) - for (k, v) in DUMMY_DATA.items(): - self.assertTrue(handler.response.body.find(str(k))>0) + client.putrequest('GET', stats_httpd.XSL_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.getheader("Content-type"), "text/xml") + self.assertTrue(int(response.getheader("Content-Length")) > 0) + self.assertEqual(response.status, 200) + root = xml.etree.ElementTree.parse(response).getroot() + url_trans = '{http://www.w3.org/1999/XSL/Transform}' + url_xhtml = '{http://www.w3.org/1999/xhtml}' + xslpath = url_trans + 'template/' + url_xhtml + 'tr' + self.assertEqual(root.tag, url_trans + 'stylesheet') + for tr in root.findall(xslpath): + tds = tr.findall(url_xhtml + 'td') + self.assertIsNotNone(tds) + self.assertEqual(type(tds), list) + self.assertTrue(len(tds) > 2) + self.assertTrue(hasattr(tds[0], 'text')) + self.assertTrue(tds[0].text in DUMMY_DATA) + valueof = tds[2].find(url_trans + 'value-of') + self.assertIsNotNone(valueof) + self.assertTrue(hasattr(valueof, 'attrib')) + self.assertIsNotNone(valueof.attrib) + self.assertTrue('select' in valueof.attrib) + self.assertTrue(valueof.attrib['select'] in \ + [ tds[0].text+'/'+item for item in DUMMY_DATA[tds[0].text].keys() ]) # 302 redirect - handler.path = '/' - handler.headers = {'Host': 'my.host.domain'} - handler.do_GET() - self.assertEqual(handler.response.code, 302) - self.assertEqual(handler.response.headers["Location"], - "http://my.host.domain%s" % stats_httpd.XML_URL_PATH) + client._http_vsn_str = 'HTTP/1.1' + client.putrequest('GET', '/') + client.putheader('Host', address) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 302) + self.assertEqual(response.getheader('Location'), + "http://%s:%d%s" % (address, port, stats_httpd.XML_URL_PATH)) - # 404 NotFound - handler.path = '/path/to/foo/bar' - handler.headers = {} - handler.do_GET() - self.assertEqual(handler.response.code, 404) + # # 404 NotFound + client._http_vsn_str = 'HTTP/1.0' + client.putrequest('GET', '/path/to/foo/bar') + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 404) + client.close() + statshttpd_server.shutdown() + + def test_do_GET_failed1(self): # failure case(connection with Stats is down) - handler.path = stats_httpd.XML_URL_PATH - push_answer(self.stats_httpd) - self.assertFalse(self.stats_httpd.cc_session._socket._closed) - self.stats_httpd.cc_session._socket._closed = True - handler.do_GET() - self.stats_httpd.cc_session._socket._closed = False - self.assertEqual(handler.response.code, 500) - self.stats_httpd.cc_session._clear_queues() + (address, port) = ('127.0.0.1', 65451) + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + statshttpd = statshttpd_server.server + statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + statshttpd_server.run() + self.assertTrue(self.stats_server.server.running) + self.stats_server.shutdown() + time.sleep(TIMEOUT_SEC*2) + self.assertFalse(self.stats_server.server.running) + statshttpd.cc_session.set_timeout(milliseconds=TIMEOUT_SEC/1000) + client = http.client.HTTPConnection(address, port) + client.connect() - # failure case(Stats module returns err) - handler.path = stats_httpd.XML_URL_PATH - self.stats_httpd.cc_session.group_sendmsg( - { 'result': [ 1, "I have an error." ] }, "Stats") - self.assertFalse(self.stats_httpd.cc_session._socket._closed) - self.stats_httpd.cc_session._socket._closed = False - handler.do_GET() - self.assertEqual(handler.response.code, 500) - self.stats_httpd.cc_session._clear_queues() + # request XML + client.putrequest('GET', stats_httpd.XML_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + # request XSD + client.putrequest('GET', stats_httpd.XSD_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + # request XSL + client.putrequest('GET', stats_httpd.XSL_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + client.close() + statshttpd_server.shutdown() + + def test_do_GET_failed2(self): + # failure case(connection with Stats is down) + (address, port) = ('127.0.0.1', 65452) + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.stats_httpd = statshttpd_server.server + self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + statshttpd_server.run() + self.stats.mccs.set_command_handler( + lambda cmd, args: \ + isc.config.ccsession.create_answer(1, "I have an error.") + ) + time.sleep(TIMEOUT_SEC*5) + client = http.client.HTTPConnection(address, port) + client.connect() + + # request XML + client.putrequest('GET', stats_httpd.XML_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + # request XSD + client.putrequest('GET', stats_httpd.XSD_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + # request XSL + client.putrequest('GET', stats_httpd.XSL_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + client.close() + statshttpd_server.shutdown() def test_do_HEAD(self): - for ht in self.httpd: - self._test_do_HEAD(ht._handler) + (address, port) = ('127.0.0.1', 65453) + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.stats_httpd = statshttpd_server.server + self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + statshttpd_server.run() + time.sleep(TIMEOUT_SEC*5) + client = http.client.HTTPConnection(address, port) + client.connect() + client.putrequest('HEAD', stats_httpd.XML_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 200) - def _test_do_HEAD(self, handler): - handler.path = '/path/to/foo/bar' - handler.do_HEAD() - self.assertEqual(handler.response.code, 404) + client.putrequest('HEAD', '/path/to/foo/bar') + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 404) + client.close() + statshttpd_server.shutdown() + + def test_log_message(self): + class MyHttpHandler(stats_httpd.HttpHandler): + def __init__(self): + class _Dummy_class_(): pass + self.address_string = lambda : 'dummyhost' + self.log_date_time_string = lambda : \ + 'DD/MM/YYYY HH:MI:SS' + self.server = _Dummy_class_() + self.server.log_writer = self.log_writer + def log_writer(self, line): + self.logged_line = line + self.handler = MyHttpHandler() + self.handler.log_message("%s %d", 'ABCDEFG', 12345) + self.assertEqual(self.handler.logged_line, + "[b10-stats-httpd] dummyhost - - " + + "[DD/MM/YYYY HH:MI:SS] ABCDEFG 12345\n") class TestHttpServerError(unittest.TestCase): """Tests for HttpServerError exception""" - def test_raises(self): try: raise stats_httpd.HttpServerError('Nothing') @@ -162,17 +276,16 @@ class TestHttpServerError(unittest.TestCase): class TestHttpServer(unittest.TestCase): """Tests for HttpServer class""" + def setUp(self): + self.base = BaseModules() + + def tearDown(self): + self.base.shutdown() def test_httpserver(self): - self.stats_httpd = stats_httpd.StatsHttpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(ht.server_address in self.stats_httpd.http_addrs) - self.assertEqual(ht.xml_handler, self.stats_httpd.xml_handler) - self.assertEqual(ht.xsd_handler, self.stats_httpd.xsd_handler) - self.assertEqual(ht.xsl_handler, self.stats_httpd.xsl_handler) - self.assertEqual(ht.log_writer, self.stats_httpd.write_log) - self.assertTrue(isinstance(ht._handler, stats_httpd.HttpHandler)) - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + statshttpd = stats_httpd.StatsHttpd() + self.assertEqual(type(statshttpd.httpd), list) + self.assertEqual(len(statshttpd.httpd), 0) class TestStatsHttpdError(unittest.TestCase): """Tests for StatsHttpdError exception""" @@ -187,130 +300,176 @@ class TestStatsHttpd(unittest.TestCase): """Tests for StatsHttpd class""" def setUp(self): - fake_socket._CLOSED = False - fake_socket.has_ipv6 = True + self.base = BaseModules() + self.stats_server = ThreadingServerManager(MyStats) + self.stats = self.stats_server.server + self.stats_server.run() self.stats_httpd = stats_httpd.StatsHttpd() + # checking IPv6 enabled on this platform + self.ipv6_enabled = True + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.bind(("::1",8000)) + sock.close() + except socket.error: + self.ipv6_enabled = False + def tearDown(self): self.stats_httpd.stop() + self.stats_server.shutdown() + self.base.shutdown() def test_init(self): - self.assertFalse(self.stats_httpd.mccs.get_socket()._closed) - self.assertEqual(self.stats_httpd.mccs.get_socket().fileno(), - id(self.stats_httpd.mccs.get_socket())) - for ht in self.stats_httpd.httpd: - self.assertFalse(ht.socket._closed) - self.assertEqual(ht.socket.fileno(), id(ht.socket)) - fake_socket._CLOSED = True - self.assertRaises(isc.cc.session.SessionError, - stats_httpd.StatsHttpd) - fake_socket._CLOSED = False + self.assertEqual(self.stats_httpd.running, False) + self.assertEqual(self.stats_httpd.poll_intval, 0.5) + self.assertEqual(self.stats_httpd.httpd, []) + self.assertEqual(type(self.stats_httpd.mccs), isc.config.ModuleCCSession) + self.assertEqual(type(self.stats_httpd.cc_session), isc.cc.Session) + self.assertEqual(len(self.stats_httpd.config), 2) + self.assertTrue('listen_on' in self.stats_httpd.config) + self.assertEqual(len(self.stats_httpd.config['listen_on']), 1) + self.assertTrue('address' in self.stats_httpd.config['listen_on'][0]) + self.assertTrue('port' in self.stats_httpd.config['listen_on'][0]) + self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + + def test_openclose_mccs(self): + statshttpd = stats_httpd.StatsHttpd() + statshttpd.close_mccs() + self.assertEqual(statshttpd.mccs, None) + statshttpd.open_mccs() + self.assertIsNotNone(statshttpd.mccs) + statshttpd.mccs = None + self.assertEqual(statshttpd.mccs, None) + self.assertEqual(statshttpd.close_mccs(), None) def test_mccs(self): - self.stats_httpd.open_mccs() + self.assertIsNotNone(self.stats_httpd.mccs.get_socket()) self.assertTrue( - isinstance(self.stats_httpd.mccs.get_socket(), fake_socket.socket)) + isinstance(self.stats_httpd.mccs.get_socket(), socket.socket)) self.assertTrue( isinstance(self.stats_httpd.cc_session, isc.cc.session.Session)) - self.assertTrue( - isinstance(self.stats_httpd.stats_module_spec, isc.config.ModuleSpec)) - for cfg in self.stats_httpd.stats_config_spec: - self.assertTrue('item_name' in cfg) - self.assertTrue(cfg['item_name'] in DUMMY_DATA) - self.assertTrue(len(self.stats_httpd.stats_config_spec), len(DUMMY_DATA)) - - def test_load_config(self): - self.stats_httpd.load_config() - self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + self.statistics_spec = self.stats_httpd.get_stats_spec() + for mod in DUMMY_DATA: + self.assertTrue(mod in self.statistics_spec) + for cfg in self.statistics_spec[mod]: + self.assertTrue('item_name' in cfg) + self.assertTrue(cfg['item_name'] in DUMMY_DATA[mod]) + self.assertTrue(len(self.statistics_spec[mod]), len(DUMMY_DATA[mod])) + self.stats_httpd.close_mccs() + self.assertIsNone(self.stats_httpd.mccs) def test_httpd(self): # dual stack (addresses is ipv4 and ipv6) - fake_socket.has_ipv6 = True - self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) - self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ] - self.assertTrue( - stats_httpd.HttpServer.address_family in set([fake_socket.AF_INET, fake_socket.AF_INET6])) - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) - self.stats_httpd.close_httpd() + if self.ipv6_enabled: + self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ] + self.assertTrue( + stats_httpd.HttpServer.address_family in set([socket.AF_INET, socket.AF_INET6])) + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.close_httpd() # dual stack (address is ipv6) - fake_socket.has_ipv6 = True - self.stats_httpd.http_addrs = [ ('::1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) - self.stats_httpd.close_httpd() - + if self.ipv6_enabled: + self.stats_httpd.http_addrs = [ ('::1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.close_httpd() + # dual stack (address is ipv4) - fake_socket.has_ipv6 = True - self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) - self.stats_httpd.close_httpd() + if self.ipv6_enabled: + self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.close_httpd() # only-ipv4 single stack - fake_socket.has_ipv6 = False - self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) - self.stats_httpd.close_httpd() - + if not self.ipv6_enabled: + self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.close_httpd() + # only-ipv4 single stack (force set ipv6 ) - fake_socket.has_ipv6 = False - self.stats_httpd.http_addrs = [ ('::1', 8000) ] - self.assertRaises(stats_httpd.HttpServerError, - self.stats_httpd.open_httpd) - + if not self.ipv6_enabled: + self.stats_httpd.http_addrs = [ ('::1', 8000) ] + self.assertRaises(stats_httpd.HttpServerError, + self.stats_httpd.open_httpd) + # hostname self.stats_httpd.http_addrs = [ ('localhost', 8000) ] self.stats_httpd.open_httpd() for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.close_httpd() - + self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + self.assertEqual(type(self.stats_httpd.httpd), list) + self.assertEqual(len(self.stats_httpd.httpd), 0) self.stats_httpd.close_httpd() # over flow of port number self.stats_httpd.http_addrs = [ ('', 80000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + # negative self.stats_httpd.http_addrs = [ ('', -8000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + # alphabet self.stats_httpd.http_addrs = [ ('', 'ABCDE') ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - def test_start(self): - self.stats_httpd.cc_session.group_sendmsg( - { 'command': [ "shutdown" ] }, "StatsHttpd") - self.stats_httpd.start() - self.stats_httpd = stats_httpd.StatsHttpd() - self.assertRaises( - fake_select.error, self.stats_httpd.start) + # Address already in use + self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.statshttpd_server.server.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) + self.statshttpd_server.run() + time.sleep(TIMEOUT_SEC) + self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) + self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + self.statshttpd_server.shutdown() - def test_stop(self): - # success case - fake_socket._CLOSED = False - self.stats_httpd.stop() + def test_running(self): self.assertFalse(self.stats_httpd.running) - self.assertIsNone(self.stats_httpd.mccs) - for ht in self.stats_httpd.httpd: - self.assertTrue(ht.socket._closed) - self.assertTrue(self.stats_httpd.cc_session._socket._closed) + self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.stats_httpd = self.statshttpd_server.server + self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65455 }]}) + self.statshttpd_server.run() + time.sleep(TIMEOUT_SEC*2) + self.assertTrue(self.stats_httpd.running) + self.statshttpd_server.shutdown() + self.assertFalse(self.stats_httpd.running) + # failure case - self.stats_httpd.cc_session._socket._closed = False - self.stats_httpd.open_mccs() - self.stats_httpd.cc_session._socket._closed = True - self.stats_httpd.stop() # No excetion raises - self.stats_httpd.cc_session._socket._closed = False + self.stats_httpd = stats_httpd.StatsHttpd() + self.stats_httpd.cc_session.close() + self.assertRaises( + isc.cc.session.SessionError, self.stats_httpd.start) + + def test_select_failure(self): + def raise_select_except(*args): + raise select.error('dummy error') + def raise_select_except_with_errno(*args): + raise select.error(errno.EINTR) + (address, port) = ('127.0.0.1', 65456) + stats_httpd.select.select = raise_select_except + statshttpd = stats_httpd.StatsHttpd() + statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + self.assertRaises(select.error, statshttpd.start) + statshttpd.stop() + stats_httpd.select.select = raise_select_except_with_errno + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + statshttpd = statshttpd_server.server + statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + statshttpd_server.run() + time.sleep(TIMEOUT_SEC*2) + statshttpd_server.shutdown() def test_open_template(self): # successful conditions @@ -363,38 +522,40 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual( self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)), isc.config.ccsession.create_answer( - 1, "Unknown known config: _UNKNOWN_KEY_")) + 1, "Unknown known config: _UNKNOWN_KEY_")) + self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="::2",port=8000)])), + dict(listen_on=[dict(address="127.0.0.2",port=8000)])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "::2") + self.assertTrue(addr["address"] == "127.0.0.2") self.assertTrue(addr["port"] == 8000) - self.assertEqual( - self.stats_httpd.config_handler( - dict(listen_on=[dict(address="::1",port=80)])), - isc.config.ccsession.create_answer(0)) - self.assertTrue("listen_on" in self.stats_httpd.config) - for addr in self.stats_httpd.config["listen_on"]: - self.assertTrue("address" in addr) - self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "::1") - self.assertTrue(addr["port"] == 80) + if self.ipv6_enabled: + self.assertEqual( + self.stats_httpd.config_handler( + dict(listen_on=[dict(address="::1",port=8000)])), + isc.config.ccsession.create_answer(0)) + self.assertTrue("listen_on" in self.stats_httpd.config) + for addr in self.stats_httpd.config["listen_on"]: + self.assertTrue("address" in addr) + self.assertTrue("port" in addr) + self.assertTrue(addr["address"] == "::1") + self.assertTrue(addr["port"] == 8000) self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="1.2.3.4",port=54321)])), + dict(listen_on=[dict(address="127.0.0.1",port=54321)])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "1.2.3.4") + self.assertTrue(addr["address"] == "127.0.0.1") self.assertTrue(addr["port"] == 54321) (ret, arg) = isc.config.ccsession.parse_answer( self.stats_httpd.config_handler( @@ -500,8 +661,6 @@ class TestStatsHttpd(unittest.TestCase): imp.reload(stats_httpd) os.environ["B10_FROM_SOURCE"] = tmppath imp.reload(stats_httpd) - stats_httpd.socket = fake_socket - stats_httpd.select = fake_select if __name__ == "__main__": unittest.main() diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index a42c81d136..b013c7a8bc 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -13,632 +13,496 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -# Tests for the stats module -# -import os -import sys -import time import unittest +import os +import threading +import io +import time import imp -from isc.cc.session import Session, SessionError -from isc.config.ccsession import ModuleCCSession, ModuleCCSessionError -from fake_time import time, strftime, gmtime -import stats -stats.time = time -stats.strftime = strftime -stats.gmtime = gmtime -from stats import SessionSubject, CCSessionListener, get_timestamp, get_datetime -from fake_time import _TEST_TIME_SECS, _TEST_TIME_STRF -if "B10_FROM_SOURCE" in os.environ: - TEST_SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] +\ - "/src/bin/stats/tests/testdata/stats_test.spec" -else: - TEST_SPECFILE_LOCATION = "./testdata/stats_test.spec" +import stats +import isc.cc.session +from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, TIMEOUT_SEC + +class TestUtilties(unittest.TestCase): + items = [ + { 'item_name': 'test_int1', 'item_type': 'integer', 'item_default': 12345 }, + { 'item_name': 'test_real1', 'item_type': 'real', 'item_default': 12345.6789 }, + { 'item_name': 'test_bool1', 'item_type': 'boolean', 'item_default': True }, + { 'item_name': 'test_str1', 'item_type': 'string', 'item_default': 'ABCD' }, + { 'item_name': 'test_list1', 'item_type': 'list', 'item_default': [1,2,3], + 'list_item_spec' : [ { 'item_name': 'one', 'item_type': 'integer' }, + { 'item_name': 'two', 'item_type': 'integer' }, + { 'item_name': 'three', 'item_type': 'integer' } ] }, + { 'item_name': 'test_map1', 'item_type': 'map', 'item_default': {'a':1,'b':2,'c':3}, + 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'integer'}, + { 'item_name': 'b', 'item_type': 'integer'}, + { 'item_name': 'c', 'item_type': 'integer'} ] }, + { 'item_name': 'test_int2', 'item_type': 'integer' }, + { 'item_name': 'test_real2', 'item_type': 'real' }, + { 'item_name': 'test_bool2', 'item_type': 'boolean' }, + { 'item_name': 'test_str2', 'item_type': 'string' }, + { 'item_name': 'test_list2', 'item_type': 'list', + 'list_item_spec' : [ { 'item_name': 'one', 'item_type': 'integer' }, + { 'item_name': 'two', 'item_type': 'integer' }, + { 'item_name': 'three', 'item_type': 'integer' } ] }, + { 'item_name': 'test_map2', 'item_type': 'map', + 'map_item_spec' : [ { 'item_name': 'A', 'item_type': 'integer'}, + { 'item_name': 'B', 'item_type': 'integer'}, + { 'item_name': 'C', 'item_type': 'integer'} ] }, + { 'item_name': 'test_none', 'item_type': 'none' } + ] + + def test_parse_spec(self): + self.assertEqual( + stats.parse_spec(self.items), { + 'test_int1' : 12345 , + 'test_real1' : 12345.6789 , + 'test_bool1' : True , + 'test_str1' : 'ABCD' , + 'test_list1' : [1,2,3] , + 'test_map1' : {'a':1,'b':2,'c':3}, + 'test_int2' : 0 , + 'test_real2' : 0.0, + 'test_bool2' : False, + 'test_str2' : "", + 'test_list2' : [0,0,0], + 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 }, + 'test_none' : None }) + self.assertRaises(TypeError, stats.parse_spec, None) + self.assertRaises(KeyError, stats.parse_spec, [{'item_name':'Foo'}]) + + def test_get_timestamp(self): + self.assertEqual(stats.get_timestamp(), 1308730448.965706) + + def test_get_datetime(self): + stats.time = lambda : 1308730448.965706 + stats.gmtime = lambda : (2011, 6, 22, 8, 14, 8, 2, 173, 0) + self.assertEqual(stats.get_datetime(), '2011-06-22T08:14:08Z') + self.assertNotEqual(stats.get_datetime( + (2011, 6, 22, 8, 23, 40, 2, 173, 0)), '2011-06-22T08:14:08Z') + +class TestCallback(unittest.TestCase): + def setUp(self): + self.dummy_func = lambda *x, **y : (x, y) + self.dummy_args = (1,2,3) + self.dummy_kwargs = {'a':1,'b':2,'c':3} + self.cback1 = stats.Callback( + command=self.dummy_func, + args=self.dummy_args, + kwargs=self.dummy_kwargs + ) + self.cback2 = stats.Callback( + args=self.dummy_args, + kwargs=self.dummy_kwargs + ) + self.cback3 = stats.Callback( + command=self.dummy_func, + kwargs=self.dummy_kwargs + ) + self.cback4 = stats.Callback( + command=self.dummy_func, + args=self.dummy_args + ) + + def tearDown(self): + pass + + def test_init(self): + self.assertEqual((self.cback1.command, self.cback1.args, self.cback1.kwargs), + (self.dummy_func, self.dummy_args, self.dummy_kwargs)) + self.assertEqual((self.cback2.command, self.cback2.args, self.cback2.kwargs), + (None, self.dummy_args, self.dummy_kwargs)) + self.assertEqual((self.cback3.command, self.cback3.args, self.cback3.kwargs), + (self.dummy_func, (), self.dummy_kwargs)) + self.assertEqual((self.cback4.command, self.cback4.args, self.cback4.kwargs), + (self.dummy_func, self.dummy_args, {})) + + def test_call(self): + self.assertEqual(self.cback1(), (self.dummy_args, self.dummy_kwargs)) + self.assertEqual(self.cback1(100, 200), ((100, 200), self.dummy_kwargs)) + self.assertEqual(self.cback1(a=100, b=200), (self.dummy_args, {'a':100, 'b':200})) + self.assertEqual(self.cback2(), None) + self.assertEqual(self.cback3(), ((), self.dummy_kwargs)) + self.assertEqual(self.cback3(100, 200), ((100, 200), self.dummy_kwargs)) + self.assertEqual(self.cback3(a=100, b=200), ((), {'a':100, 'b':200})) + self.assertEqual(self.cback4(), (self.dummy_args, {})) + self.assertEqual(self.cback4(100, 200), ((100, 200), {})) + self.assertEqual(self.cback4(a=100, b=200), (self.dummy_args, {'a':100, 'b':200})) class TestStats(unittest.TestCase): - def setUp(self): - self.session = Session() - self.subject = SessionSubject(session=self.session) - self.listener = CCSessionListener(self.subject) - self.stats_spec = self.listener.cc_session.get_module_spec().get_config_spec() - self.module_name = self.listener.cc_session.get_module_spec().get_module_name() - self.stats_data = { - 'report_time' : get_datetime(), - 'bind10.boot_time' : "1970-01-01T00:00:00Z", - 'stats.timestamp' : get_timestamp(), - 'stats.lname' : self.session.lname, - 'auth.queries.tcp': 0, - 'auth.queries.udp': 0, - "stats.boot_time": get_datetime(), - "stats.start_time": get_datetime(), - "stats.last_update_time": get_datetime() - } - # check starting - self.assertFalse(self.subject.running) - self.subject.start() - self.assertTrue(self.subject.running) - self.assertEqual(len(self.session.message_queue), 0) - self.assertEqual(self.module_name, 'Stats') + self.base = BaseModules() + self.stats = stats.Stats() + self.assertTrue("B10_FROM_SOURCE" in os.environ) + self.assertEqual(stats.SPECFILE_LOCATION, \ + os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + \ + os.sep + "stats.spec") def tearDown(self): - # check closing - self.subject.stop() - self.assertFalse(self.subject.running) - self.subject.detach(self.listener) - self.listener.stop() - self.session.close() + self.base.shutdown() - def test_local_func(self): - """ - Test for local function - - """ - # test for result_ok - self.assertEqual(type(result_ok()), dict) - self.assertEqual(result_ok(), {'result': [0]}) - self.assertEqual(result_ok(1), {'result': [1]}) - self.assertEqual(result_ok(0,'OK'), {'result': [0, 'OK']}) - self.assertEqual(result_ok(1,'Not good'), {'result': [1, 'Not good']}) - self.assertEqual(result_ok(None,"It's None"), {'result': [None, "It's None"]}) - self.assertNotEqual(result_ok(), {'RESULT': [0]}) + def test_init(self): + self.assertEqual(self.stats.module_name, 'Stats') + self.assertFalse(self.stats.running) + self.assertTrue('command_show' in self.stats.callbacks) + self.assertTrue('command_status' in self.stats.callbacks) + self.assertTrue('command_shutdown' in self.stats.callbacks) + self.assertTrue('command_show' in self.stats.callbacks) + self.assertTrue('command_showschema' in self.stats.callbacks) + self.assertTrue('command_set' in self.stats.callbacks) - # test for get_timestamp - self.assertEqual(get_timestamp(), _TEST_TIME_SECS) + def test_init_undefcmd(self): + spec_str = """\ +{ + "module_spec": { + "module_name": "Stats", + "module_description": "Stats daemon", + "config_data": [], + "commands": [ + { + "command_name": "_undef_command_", + "command_description": "a undefined command in stats", + "command_args": [] + } + ], + "statistics": [] + } +} +""" + orig_spec_location = stats.SPECFILE_LOCATION + stats.SPECFILE_LOCATION = io.StringIO(spec_str) + self.assertRaises(stats.StatsError, stats.Stats) + stats.SPECFILE_LOCATION = orig_spec_location - # test for get_datetime - self.assertEqual(get_datetime(), _TEST_TIME_STRF) + def test_start(self): + statsserver = ThreadingServerManager(MyStats) + stats = statsserver.server + self.assertFalse(stats.running) + statsserver.run() + time.sleep(TIMEOUT_SEC) + self.assertTrue(stats.running) + statsserver.shutdown() + self.assertFalse(stats.running) - def test_show_command(self): - """ - Test for show command - - """ - # test show command without arg - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - # ignore under 0.9 seconds - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) + def test_start_with_err(self): + statsd = stats.Stats() + statsd.update_statistics_data = lambda x,**y: [1] + self.assertRaises(stats.StatsError, statsd.start) - # test show command with arg - self.session.group_sendmsg({"command": [ "show", {"stats_item_name": "stats.lname"}]}, "Stats") - self.assertEqual(len(self.subject.session.message_queue), 1) - self.subject.check() - result_data = self.subject.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'stats.lname': self.stats_data['stats.lname']}), - result_data) - self.assertEqual(len(self.subject.session.message_queue), 0) + def test_config_handler(self): + self.assertEqual(self.stats.config_handler({'foo':'bar'}), + isc.config.create_answer(0)) - # test show command with arg which has wrong name - self.session.group_sendmsg({"command": [ "show", {"stats_item_name": "stats.dummy"}]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - # ignore under 0.9 seconds - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_set_command(self): - """ - Test for set command - - """ - # test set command - self.stats_data['auth.queries.udp'] = 54321 - self.assertEqual(self.stats_data['auth.queries.udp'], 54321) - self.assertEqual(self.stats_data['auth.queries.tcp'], 0) - self.session.group_sendmsg({ "command": [ - "set", { - 'stats_data': {'auth.queries.udp': 54321 } - } ] }, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 2 - self.stats_data['auth.queries.udp'] = 0 - self.assertEqual(self.stats_data['auth.queries.udp'], 0) - self.assertEqual(self.stats_data['auth.queries.tcp'], 0) - self.session.group_sendmsg({ "command": [ "set", {'stats_data': {'auth.queries.udp': 0}} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command 2 - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 3 - self.stats_data['auth.queries.tcp'] = 54322 - self.assertEqual(self.stats_data['auth.queries.udp'], 0) - self.assertEqual(self.stats_data['auth.queries.tcp'], 54322) - self.session.group_sendmsg({ "command": [ - "set", { - 'stats_data': {'auth.queries.tcp': 54322 } - } ] }, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command 3 - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_remove_command(self): - """ - Test for remove command - - """ - self.session.group_sendmsg({"command": - [ "remove", {"stats_item_name": 'bind10.boot_time' }]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - self.assertEqual(self.stats_data.pop('bind10.boot_time'), "1970-01-01T00:00:00Z") - self.assertFalse('bind10.boot_time' in self.stats_data) - - # test show command with arg - self.session.group_sendmsg({"command": - [ "show", {"stats_item_name": 'bind10.boot_time'}]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertFalse('bind10.boot_time' in result_data['result'][1]) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_reset_command(self): - """ - Test for reset command - - """ - self.session.group_sendmsg({"command": [ "reset" ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command - self.session.group_sendmsg({"command": [ "show" ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_status_command(self): - """ - Test for status command - - """ - self.session.group_sendmsg({"command": [ "status" ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(0, "I'm alive."), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - def test_unknown_command(self): - """ - Test for unknown command - - """ - self.session.group_sendmsg({"command": [ "hoge", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(1, "Unknown command: 'hoge'"), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - def test_shutdown_command(self): - """ - Test for shutdown command - - """ - self.session.group_sendmsg({"command": [ "shutdown", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.assertTrue(self.subject.running) - self.subject.check() - self.assertFalse(self.subject.running) - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - - def test_some_commands(self): - """ - Test for some commands in a row - - """ - # test set command - self.stats_data['bind10.boot_time'] = '2010-08-02T14:47:56Z' - self.assertEqual(self.stats_data['bind10.boot_time'], '2010-08-02T14:47:56Z') - self.session.group_sendmsg({ "command": [ - "set", { - 'stats_data': {'bind10.boot_time': '2010-08-02T14:47:56Z' } - }]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ - "show", { 'stats_item_name': 'bind10.boot_time' } - ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'bind10.boot_time': '2010-08-02T14:47:56Z'}), - result_data) - self.assertEqual(result_ok(0, {'bind10.boot_time': self.stats_data['bind10.boot_time']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 2nd - self.stats_data['auth.queries.udp'] = 98765 - self.assertEqual(self.stats_data['auth.queries.udp'], 98765) - self.session.group_sendmsg({ "command": [ - "set", { 'stats_data': { - 'auth.queries.udp': - self.stats_data['auth.queries.udp'] - } } - ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({"command": [ - "show", {'stats_item_name': 'auth.queries.udp'} - ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'auth.queries.udp': 98765}), - result_data) - self.assertEqual(result_ok(0, {'auth.queries.udp': self.stats_data['auth.queries.udp']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 3 - self.stats_data['auth.queries.tcp'] = 4321 - self.session.group_sendmsg({"command": [ - "set", - {'stats_data': {'auth.queries.tcp': 4321 }} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check value - self.session.group_sendmsg({"command": [ "show", {'stats_item_name': 'auth.queries.tcp'} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'auth.queries.tcp': 4321}), - result_data) - self.assertEqual(result_ok(0, {'auth.queries.tcp': self.stats_data['auth.queries.tcp']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - self.session.group_sendmsg({"command": [ "show", {'stats_item_name': 'auth.queries.udp'} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'auth.queries.udp': 98765}), - result_data) - self.assertEqual(result_ok(0, {'auth.queries.udp': self.stats_data['auth.queries.udp']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 4 - self.stats_data['auth.queries.tcp'] = 67890 - self.session.group_sendmsg({"command": [ - "set", {'stats_data': {'auth.queries.tcp': 67890 }} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command for all values - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_some_commands2(self): - """ - Test for some commands in a row using list-type value - - """ - self.stats_data['listtype'] = [1, 2, 3] - self.assertEqual(self.stats_data['listtype'], [1, 2, 3]) - self.session.group_sendmsg({ "command": [ - "set", {'stats_data': {'listtype': [1, 2, 3] }} - ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ - "show", { 'stats_item_name': 'listtype'} - ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'listtype': [1, 2, 3]}), - result_data) - self.assertEqual(result_ok(0, {'listtype': self.stats_data['listtype']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set list-type value - self.assertEqual(self.stats_data['listtype'], [1, 2, 3]) - self.session.group_sendmsg({"command": [ - "set", {'stats_data': {'listtype': [3, 2, 1, 0] }} - ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ - "show", { 'stats_item_name': 'listtype' } - ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'listtype': [3, 2, 1, 0]}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_some_commands3(self): - """ - Test for some commands in a row using dictionary-type value - - """ - self.stats_data['dicttype'] = {"a": 1, "b": 2, "c": 3} - self.assertEqual(self.stats_data['dicttype'], {"a": 1, "b": 2, "c": 3}) - self.session.group_sendmsg({ "command": [ - "set", { - 'stats_data': {'dicttype': {"a": 1, "b": 2, "c": 3} } - }]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ "show", { 'stats_item_name': 'dicttype' } ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'dicttype': {"a": 1, "b": 2, "c": 3}}), - result_data) - self.assertEqual(result_ok(0, {'dicttype': self.stats_data['dicttype']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set list-type value - self.assertEqual(self.stats_data['dicttype'], {"a": 1, "b": 2, "c": 3}) - self.session.group_sendmsg({"command": [ - "set", {'stats_data': {'dicttype': {"a": 3, "b": 2, "c": 1, "d": 0} }} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ "show", { 'stats_item_name': 'dicttype' }]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'dicttype': {"a": 3, "b": 2, "c": 1, "d": 0} }), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_config_update(self): - """ - Test for config update - - """ - # test show command without arg - self.session.group_sendmsg({"command": [ "config_update", {"x-version":999} ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - - def test_for_boss(self): - last_queue = self.session.old_message_queue.pop() + def test_command_handler(self): + statsserver = ThreadingServerManager(MyStats) + statsserver.run() + time.sleep(TIMEOUT_SEC*4) + self.base.boss.server._started.wait() self.assertEqual( - last_queue.msg, {'command': ['sendstats']}) + send_command( + 'show', 'Stats', + params={ 'owner' : 'Boss', + 'name' : 'boot_time' }), + (0, '2011-06-22T08:14:08Z')) self.assertEqual( - last_queue.env['group'], 'Boss') + send_command( + 'set', 'Stats', + params={ 'owner' : 'Boss', + 'data' : { 'boot_time' : '2012-06-22T18:24:08Z' } }), + (0, None)) + self.assertEqual( + send_command( + 'show', 'Stats', + params={ 'owner' : 'Boss', + 'name' : 'boot_time' }), + (0, '2012-06-22T18:24:08Z')) + self.assertEqual( + send_command('status', 'Stats'), + (0, "Stats is up. (PID " + str(os.getpid()) + ")")) -class TestStats2(unittest.TestCase): - - def setUp(self): - self.session = Session() - self.subject = SessionSubject(session=self.session) - self.listener = CCSessionListener(self.subject) - self.module_name = self.listener.cc_session.get_module_spec().get_module_name() - # check starting - self.assertFalse(self.subject.running) - self.subject.start() - self.assertTrue(self.subject.running) - self.assertEqual(len(self.session.message_queue), 0) - self.assertEqual(self.module_name, 'Stats') - - def tearDown(self): - # check closing - self.subject.stop() - self.assertFalse(self.subject.running) - self.subject.detach(self.listener) - self.listener.stop() - - def test_specfile(self): - """ - Test for specfile + (rcode, value) = send_command('show', 'Stats') + self.assertEqual(rcode, 0) + self.assertEqual(len(value), 3) + self.assertTrue('Boss' in value) + self.assertTrue('Stats' in value) + self.assertTrue('Auth' in value) + self.assertEqual(len(value['Stats']), 5) + self.assertEqual(len(value['Boss']), 1) + self.assertTrue('boot_time' in value['Boss']) + self.assertEqual(value['Boss']['boot_time'], '2012-06-22T18:24:08Z') + self.assertTrue('report_time' in value['Stats']) + self.assertTrue('boot_time' in value['Stats']) + self.assertTrue('last_update_time' in value['Stats']) + self.assertTrue('timestamp' in value['Stats']) + self.assertTrue('lname' in value['Stats']) + (rcode, value) = send_command('showschema', 'Stats') + self.assertEqual(rcode, 0) + self.assertEqual(len(value), 3) + self.assertTrue('Boss' in value) + self.assertTrue('Stats' in value) + self.assertTrue('Auth' in value) + self.assertEqual(len(value['Stats']), 5) + self.assertEqual(len(value['Boss']), 1) + for item in value['Boss']: + self.assertTrue(len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + self.assertTrue('item_format' in item) + for item in value['Stats']: + self.assertTrue(len(item) == 6 or len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + if len(item) == 7: + self.assertTrue('item_format' in item) - """ - if "B10_FROM_SOURCE" in os.environ: - self.assertEqual(stats.SPECFILE_LOCATION, - os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + \ - os.sep + "stats.spec") - self.assertEqual(stats.SCHEMA_SPECFILE_LOCATION, - os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + \ - os.sep + "stats-schema.spec") - imp.reload(stats) - # change path of SPECFILE_LOCATION - stats.SPECFILE_LOCATION = TEST_SPECFILE_LOCATION - stats.SCHEMA_SPECFILE_LOCATION = TEST_SPECFILE_LOCATION - self.assertEqual(stats.SPECFILE_LOCATION, TEST_SPECFILE_LOCATION) - self.subject = stats.SessionSubject(session=self.session) - self.session = self.subject.session - self.listener = stats.CCSessionListener(self.subject) + self.assertEqual( + send_command('__UNKNOWN__', 'Stats'), + (1, "Unknown command: '__UNKNOWN__'")) - self.assertEqual(self.listener.stats_spec, []) - self.assertEqual(self.listener.stats_data, {}) + statsserver.shutdown() - self.assertEqual(self.listener.commands_spec, [ - { - "command_name": "status", - "command_description": "identify whether stats module is alive or not", - "command_args": [] - }, - { - "command_name": "the_dummy", - "command_description": "this is for testing", - "command_args": [] - }]) + def test_update_modules(self): + self.assertEqual(len(self.stats.modules), 0) + self.stats.update_modules() + self.assertTrue('Stats' in self.stats.modules) + self.assertTrue('Boss' in self.stats.modules) + self.assertFalse('Dummy' in self.stats.modules) + my_statistics_data = stats.parse_spec(self.stats.modules['Stats'].get_statistics_spec()) + self.assertTrue('report_time' in my_statistics_data) + self.assertTrue('boot_time' in my_statistics_data) + self.assertTrue('last_update_time' in my_statistics_data) + self.assertTrue('timestamp' in my_statistics_data) + self.assertTrue('lname' in my_statistics_data) + self.assertEqual(my_statistics_data['report_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['last_update_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['timestamp'], 0.0) + self.assertEqual(my_statistics_data['lname'], "") + my_statistics_data = stats.parse_spec(self.stats.modules['Boss'].get_statistics_spec()) + self.assertTrue('boot_time' in my_statistics_data) + self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") - def test_func_initialize_data(self): - """ - Test for initialize_data function + def test_get_statistics_data(self): + my_statistics_data = self.stats.get_statistics_data() + self.assertTrue('Stats' in my_statistics_data) + self.assertTrue('Boss' in my_statistics_data) + my_statistics_data = self.stats.get_statistics_data(owner='Stats') + self.assertTrue('report_time' in my_statistics_data) + self.assertTrue('boot_time' in my_statistics_data) + self.assertTrue('last_update_time' in my_statistics_data) + self.assertTrue('timestamp' in my_statistics_data) + self.assertTrue('lname' in my_statistics_data) + self.assertIsNone(self.stats.get_statistics_data(owner='Foo')) + my_statistics_data = self.stats.get_statistics_data(owner='Stats') + self.assertTrue('boot_time' in my_statistics_data) + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='report_time') + self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='boot_time') + self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='last_update_time') + self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='timestamp') + self.assertEqual(my_statistics_data, 0.0) + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='lname') + self.assertEqual(my_statistics_data, '') + self.assertIsNone(self.stats.get_statistics_data(owner='Stats', name='Bar')) + self.assertIsNone(self.stats.get_statistics_data(owner='Foo', name='Bar')) + self.assertEqual(self.stats.get_statistics_data(name='Bar'), None) + + def test_update_statistics_data(self): + self.stats.update_statistics_data(owner='Stats', lname='foo@bar') + self.assertTrue('Stats' in self.stats.statistics_data) + my_statistics_data = self.stats.statistics_data['Stats'] + self.assertEqual(my_statistics_data['lname'], 'foo@bar') + self.stats.update_statistics_data(owner='Stats', last_update_time='2000-01-01T10:10:10Z') + self.assertTrue('Stats' in self.stats.statistics_data) + my_statistics_data = self.stats.statistics_data['Stats'] + self.assertEqual(my_statistics_data['last_update_time'], '2000-01-01T10:10:10Z') + self.assertEqual(self.stats.update_statistics_data(owner='Stats', lname=0.0), + ['0.0 should be a string']) + self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), + ['unknown module name']) + + def test_command_status(self): + self.assertEqual(self.stats.command_status(), + isc.config.create_answer( + 0, "Stats is up. (PID " + str(os.getpid()) + ")")) - """ - # prepare for sample data set - stats_spec = [ - { - "item_name": "none_sample", - "item_type": "null", - "item_default": "None" - }, - { - "item_name": "boolean_sample", - "item_type": "boolean", - "item_default": True - }, - { - "item_name": "string_sample", - "item_type": "string", - "item_default": "A something" - }, - { - "item_name": "int_sample", - "item_type": "integer", - "item_default": 9999999 - }, - { - "item_name": "real_sample", - "item_type": "real", - "item_default": 0.0009 - }, - { - "item_name": "list_sample", - "item_type": "list", - "item_default": [0, 1, 2, 3, 4], - "list_item_spec": [] - }, - { - "item_name": "map_sample", - "item_type": "map", - "item_default": {'name':'value'}, - "map_item_spec": [] - }, - { - "item_name": "other_sample", - "item_type": "__unknown__", - "item_default": "__unknown__" - } - ] - # data for comparison - stats_data = { - 'none_sample': None, - 'boolean_sample': True, - 'string_sample': 'A something', - 'int_sample': 9999999, - 'real_sample': 0.0009, - 'list_sample': [0, 1, 2, 3, 4], - 'map_sample': {'name':'value'}, - 'other_sample': '__unknown__' - } - self.assertEqual(self.listener.initialize_data(stats_spec), stats_data) + def test_command_shutdown(self): + self.stats.running = True + self.assertEqual(self.stats.command_shutdown(), + isc.config.create_answer(0)) + self.assertFalse(self.stats.running) + + def test_command_show(self): + self.assertEqual(self.stats.command_show(owner='Foo', name=None), + isc.config.create_answer(1, "item name is not specified")) + self.assertEqual(self.stats.command_show(owner='Foo', name='_bar_'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + self.assertEqual(self.stats.command_show(owner='Foo', name='bar'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + orig_get_timestamp = stats.get_timestamp + orig_get_datetime = stats.get_datetime + stats.get_timestamp = lambda : 1308730448.965706 + stats.get_datetime = lambda : '2011-06-22T08:14:08Z' + self.assertEqual(stats.get_timestamp(), 1308730448.965706) + self.assertEqual(stats.get_datetime(), '2011-06-22T08:14:08Z') + self.assertEqual(self.stats.command_show(owner='Stats', name='report_time'), \ + isc.config.create_answer(0, '2011-06-22T08:14:08Z')) + self.assertEqual(self.stats.statistics_data['Stats']['timestamp'], 1308730448.965706) + self.assertEqual(self.stats.statistics_data['Stats']['boot_time'], '1970-01-01T00:00:00Z') + stats.get_timestamp = orig_get_timestamp + stats.get_datetime = orig_get_datetime + self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( + { "module_name": self.stats.module_name, + "statistics": [] } ) + self.assertRaises( + stats.StatsError, self.stats.command_show, owner='Foo', name='bar') + + def test_command_showchema(self): + (rcode, value) = isc.config.ccsession.parse_answer( + self.stats.command_showschema()) + self.assertEqual(rcode, 0) + self.assertEqual(len(value), 3) + self.assertTrue('Stats' in value) + self.assertTrue('Boss' in value) + self.assertTrue('Auth' in value) + self.assertFalse('__Dummy__' in value) + schema = value['Stats'] + self.assertEqual(len(schema), 5) + for item in schema: + self.assertTrue(len(item) == 6 or len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + if len(item) == 7: + self.assertTrue('item_format' in item) - def test_func_main(self): - # explicitly make failed - self.session.close() - stats.main(session=self.session) + schema = value['Boss'] + self.assertEqual(len(schema), 1) + for item in schema: + self.assertTrue(len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + self.assertTrue('item_format' in item) + + schema = value['Auth'] + self.assertEqual(len(schema), 2) + for item in schema: + self.assertTrue(len(item) == 6) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + + (rcode, value) = isc.config.ccsession.parse_answer( + self.stats.command_showschema(owner='Stats')) + self.assertEqual(rcode, 0) + self.assertFalse('Stats' in value) + self.assertFalse('Boss' in value) + self.assertFalse('Auth' in value) + for item in value: + self.assertTrue(len(item) == 6 or len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + if len(item) == 7: + self.assertTrue('item_format' in item) + + (rcode, value) = isc.config.ccsession.parse_answer( + self.stats.command_showschema(owner='Stats', name='report_time')) + self.assertEqual(rcode, 0) + self.assertFalse('Stats' in value) + self.assertFalse('Boss' in value) + self.assertFalse('Auth' in value) + self.assertTrue(len(value) == 7) + self.assertTrue('item_name' in value) + self.assertTrue('item_type' in value) + self.assertTrue('item_optional' in value) + self.assertTrue('item_default' in value) + self.assertTrue('item_title' in value) + self.assertTrue('item_description' in value) + self.assertTrue('item_format' in value) + self.assertEqual(value['item_name'], 'report_time') + self.assertEqual(value['item_format'], 'date-time') + + self.assertEqual(self.stats.command_showschema(owner='Foo'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + self.assertEqual(self.stats.command_showschema(owner='Foo', name='bar'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + self.assertEqual(self.stats.command_showschema(owner='Stats', name='bar'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + self.assertEqual(self.stats.command_showschema(name='bar'), + isc.config.create_answer( + 1, "module name is not specified")) + + def test_command_set(self): + orig_get_datetime = stats.get_datetime + stats.get_datetime = lambda : '2011-06-22T06:12:38Z' + (rcode, value) = isc.config.ccsession.parse_answer( + self.stats.command_set(owner='Boss', + data={ 'boot_time' : '2011-06-22T13:15:04Z' })) + stats.get_datetime = orig_get_datetime + self.assertEqual(rcode, 0) + self.assertTrue(value is None) + self.assertEqual(self.stats.statistics_data['Boss']['boot_time'], + '2011-06-22T13:15:04Z') + self.assertEqual(self.stats.statistics_data['Stats']['last_update_time'], + '2011-06-22T06:12:38Z') + self.assertEqual(self.stats.command_set(owner='Stats', + data={ 'lname' : 'foo@bar' }), + isc.config.create_answer(0, None)) + self.stats.statistics_data['Stats'] = {} + self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( + { "module_name": self.stats.module_name, + "statistics": [] } ) + self.assertEqual(self.stats.command_set(owner='Stats', + data={ 'lname' : '_foo_@_bar_' }), + isc.config.create_answer( + 1, + "specified module name and/or statistics data are incorrect:" + + " No statistics specification")) + self.stats.statistics_data['Stats'] = {} + self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( + { "module_name": self.stats.module_name, + "statistics": [ + { + "item_name": "dummy", + "item_type": "string", + "item_optional": False, + "item_default": "", + "item_title": "Local Name", + "item_description": "brabra" + } ] } ) + self.assertRaises(stats.StatsError, + self.stats.command_set, owner='Stats', data={ 'dummy' : '_xxxx_yyyy_zzz_' }) def test_osenv(self): """ @@ -651,11 +515,8 @@ class TestStats2(unittest.TestCase): os.environ["B10_FROM_SOURCE"] = path imp.reload(stats) -def result_ok(*args): - if args: - return { 'result': list(args) } - else: - return { 'result': [ 0 ] } +def test_main(): + unittest.main() if __name__ == "__main__": - unittest.main() + test_main() diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index bd23182d2c..cfffc15a35 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -42,11 +42,10 @@ def send_shutdown(module_name): return send_command("shutdown", module_name) class ThreadingServerManager: - def __init__(self, server_class, verbose): + def __init__(self, server_class): self.server_class = server_class self.server_class_name = server_class.__name__ - self.verbose = verbose - self.server = self.server_class(self.verbose) + self.server = self.server_class() self.server._thread = threading.Thread( name=self.server_class_name, target=self.server.run) self.server._thread.daemon = True @@ -60,10 +59,9 @@ class ThreadingServerManager: self.server._thread.join(TIMEOUT_SEC) class MockMsgq: - def __init__(self, verbose): - self.verbose = verbose + def __init__(self): self._started = threading.Event() - self.msgq = msgq.MsgQ(None, verbose) + self.msgq = msgq.MsgQ(None) result = self.msgq.setup() if result: sys.exit("Error on Msgq startup: %s" % result) @@ -81,7 +79,7 @@ class MockMsgq: self.msgq.shutdown() class MockCfgmgr: - def __init__(self, verbose): + def __init__(self): self._started = threading.Event() self.cfgmgr = isc.config.cfgmgr.ConfigManager( os.environ['CONFIG_TESTDATA_PATH'], "b10-config.db") @@ -127,8 +125,7 @@ class MockBoss: """ _BASETIME = (2011, 6, 22, 8, 14, 8, 2, 173, 0) - def __init__(self, verbose): - self.verbose = verbose + def __init__(self): self._started = threading.Event() self.running = False self.spec_file = io.StringIO(self.spec_str) @@ -200,8 +197,7 @@ class MockAuth: } } """ - def __init__(self, verbose): - self.verbose = verbose + def __init__(self): self._started = threading.Event() self.running = False self.spec_file = io.StringIO(self.spec_str) @@ -239,9 +235,9 @@ class MockAuth: return isc.config.create_answer(1, "Unknown Command") class MyStats(stats.Stats): - def __init__(self, verbose): + def __init__(self): self._started = threading.Event() - stats.Stats.__init__(self, verbose) + stats.Stats.__init__(self) def run(self): self._started.set() @@ -251,9 +247,9 @@ class MyStats(stats.Stats): send_shutdown("Stats") class MyStatsHttpd(stats_httpd.StatsHttpd): - def __init__(self, verbose): + def __init__(self): self._started = threading.Event() - stats_httpd.StatsHttpd.__init__(self, verbose) + stats_httpd.StatsHttpd.__init__(self) def run(self): self._started.set() @@ -263,23 +259,22 @@ class MyStatsHttpd(stats_httpd.StatsHttpd): send_shutdown("StatsHttpd") class BaseModules: - def __init__(self, verbose): - self.verbose = verbose + def __init__(self): self.class_name = BaseModules.__name__ # Change value of BIND10_MSGQ_SOCKET_FILE in environment variables os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='unix_socket.') # MockMsgq - self.msgq = ThreadingServerManager(MockMsgq, self.verbose) + self.msgq = ThreadingServerManager(MockMsgq) self.msgq.run() # MockCfgmgr - self.cfgmgr = ThreadingServerManager(MockCfgmgr, self.verbose) + self.cfgmgr = ThreadingServerManager(MockCfgmgr) self.cfgmgr.run() # MockBoss - self.boss = ThreadingServerManager(MockBoss, self.verbose) + self.boss = ThreadingServerManager(MockBoss) self.boss.run() # MockAuth - self.auth = ThreadingServerManager(MockAuth, self.verbose) + self.auth = ThreadingServerManager(MockAuth) self.auth.run() def shutdown(self): From 691328d91b4c4d15ace467ca47a3c987a9fb52b9 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 21:09:41 +0900 Subject: [PATCH 481/974] [trac930] add new entry for #928-#930 --- ChangeLog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog b/ChangeLog index 56bf8e97d7..d4cd88de14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +xxx. [func] naokikambe + Add statistics category in each module spec file for management of + statistics data schemas by each module. Add get_statistics_spec into + cfgmgr and related codes. show statistics data and data schema by each + module via both bintcl and HTTP/XML interfaces. Change item name in + each statistics data. (Remove prefix "xxx." indicating the module + name.) Add new mock modules for unittests of stats and stats httpd + modules. + (Trac #928,#929,#930, git nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn) + 278. [doc] jelte Add logging configuration documentation to the guide. (Trac #1011, git TODO) From aa108cc824539a1d32a4aa2f46f9e58171074a9e Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 21:22:34 +0900 Subject: [PATCH 482/974] [trac930] remove unneeded empty TODO comments --- doc/guide/bind10-guide.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 4883bb0a29..297400cca6 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1453,7 +1453,6 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - This stats daemon provides commands to identify if it is running, show specified or all statistics data, show specified or all statistics data schema, and set specified statistics @@ -1461,7 +1460,6 @@ then change those defaults with config set Resolver/forward_addresses[0]/address For example, using bindctl: - > Stats show { From 4de3a5bdf367d87247cb9138f8929ab4798f014e Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 13 Jul 2011 20:25:54 +0900 Subject: [PATCH 483/974] [trac930] - increase seconds in sleep time which is before HTTP client connects to the server - delete 'test_log_message' because of the deletion of original function --- src/bin/stats/tests/b10-stats-httpd_test.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index ae07aa9f27..fcf95ad36f 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -68,7 +68,7 @@ class TestHttpHandler(unittest.TestCase): self.assertTrue(type(self.stats_httpd.httpd) is list) self.assertEqual(len(self.stats_httpd.httpd), 0) statshttpd_server.run() - time.sleep(TIMEOUT_SEC*5) + time.sleep(TIMEOUT_SEC*8) client = http.client.HTTPConnection(address, port) client._http_vsn_str = 'HTTP/1.0\n' client.connect() @@ -249,23 +249,6 @@ class TestHttpHandler(unittest.TestCase): client.close() statshttpd_server.shutdown() - def test_log_message(self): - class MyHttpHandler(stats_httpd.HttpHandler): - def __init__(self): - class _Dummy_class_(): pass - self.address_string = lambda : 'dummyhost' - self.log_date_time_string = lambda : \ - 'DD/MM/YYYY HH:MI:SS' - self.server = _Dummy_class_() - self.server.log_writer = self.log_writer - def log_writer(self, line): - self.logged_line = line - self.handler = MyHttpHandler() - self.handler.log_message("%s %d", 'ABCDEFG', 12345) - self.assertEqual(self.handler.logged_line, - "[b10-stats-httpd] dummyhost - - " - + "[DD/MM/YYYY HH:MI:SS] ABCDEFG 12345\n") - class TestHttpServerError(unittest.TestCase): """Tests for HttpServerError exception""" def test_raises(self): From 28cad73dff9dae43a38ad7dafbee406c690fb77c Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 20 Jul 2011 10:00:29 +0900 Subject: [PATCH 484/974] [trac930] add statistics validation for bob --- src/bin/bind10/bind10_src.py.in | 18 +++++++++++------- src/bin/bind10/tests/bind10_test.py.in | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 5189802c27..f905892221 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -318,14 +318,18 @@ class BoB: answer = isc.config.ccsession.create_answer(0) elif command == "sendstats": # send statistics data to the stats daemon immediately - cmd = isc.config.ccsession.create_command( + statistics_data = { + 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + } + valid = self.ccs.get_module_spec().validate_statistics( + True, statistics_data) + if valid: + cmd = isc.config.ccsession.create_command( 'set', { "owner": "Boss", - "data": { - 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) - }}) - seq = self.cc_session.group_sendmsg(cmd, 'Stats') - self.cc_session.group_recvmsg(True, seq) - answer = isc.config.ccsession.create_answer(0) + "data": statistics_data }) + seq = self.cc_session.group_sendmsg(cmd, 'Stats') + self.cc_session.group_recvmsg(True, seq) + answer = isc.config.ccsession.create_answer(0) elif command == "ping": answer = isc.config.ccsession.create_answer(0, "pong") elif command == "show_processes": diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index dc1d6603c4..af7b6f49ef 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -137,9 +137,27 @@ class TestBoB(unittest.TestCase): def group_sendmsg(self, msg, group): (self.msg, self.group) = (msg, group) def group_recvmsg(self, nonblock, seq): pass + class DummyModuleCCSession(): + module_spec = isc.config.module_spec.ModuleSpec({ + "module_name": "Boss", + "statistics": [ + { + "item_name": "boot_time", + "item_type": "string", + "item_optional": False, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Boot time", + "item_description": "A date time when bind10 process starts initially", + "item_format": "date-time" + } + ] + }) + def get_module_spec(self): + return self.module_spec bob = BoB() bob.verbose = True bob.cc_session = DummySession() + bob.ccs = DummyModuleCCSession() # a bad command self.assertEqual(bob.command_handler(-1, None), isc.config.ccsession.create_answer(1, "bad command")) From e95625332a20fb50afe43da2db0cab507efe8ebe Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:28:40 +0900 Subject: [PATCH 485/974] [trac930] add new messages into the message file of Auth and Boss when validation of statistics data to send to statistics module is failed. --- src/bin/auth/auth_messages.mes | 3 +++ src/bin/bind10/bind10_messages.mes | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes index 9f04b76264..1ffa6871ea 100644 --- a/src/bin/auth/auth_messages.mes +++ b/src/bin/auth/auth_messages.mes @@ -257,4 +257,7 @@ request. The zone manager component has been informed of the request, but has returned an error response (which is included in the message). The NOTIFY request will not be honored. +% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified +An error was encountered when the authoritiative server specified +statistics data which is invalid for the auth specification file. diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 4bac069098..4debcdb3ec 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -198,3 +198,7 @@ the message channel. % BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited An unknown child process has exited. The PID is printed, but no further action will be taken by the boss process. + +% BIND10_INVALID_STATISTICS_DATA invalid specification of statistics data specified +An error was encountered when the boss module specified +statistics data which is invalid for the boss specification file. From df9a8f921f0d20bd70c519218335357297bffa7d Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:32:22 +0900 Subject: [PATCH 486/974] [trac930] add the helper functions which are used around the registration of the function to validate the statistics data. --- src/bin/auth/auth_srv.cc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index 5a3144283a..c9dac88e99 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -125,6 +125,10 @@ public: /// The TSIG keyring const shared_ptr* keyring_; + + /// Bind the ModuleSpec object in config_session_ with + /// isc:config::ModuleSpec::validateStatistics. + void registerStatisticsValidator(); private: std::string db_file_; @@ -139,6 +143,9 @@ private: /// Increment query counter void incCounter(const int protocol); + + // validateStatistics + bool validateStatistics(isc::data::ConstElementPtr data) const; }; AuthSrvImpl::AuthSrvImpl(const bool use_cache, @@ -317,6 +324,7 @@ AuthSrv::setXfrinSession(AbstractSession* xfrin_session) { void AuthSrv::setConfigSession(ModuleCCSession* config_session) { impl_->config_session_ = config_session; + impl_->registerStatisticsValidator(); } void @@ -670,6 +678,22 @@ AuthSrvImpl::incCounter(const int protocol) { } } +void +AuthSrvImpl::registerStatisticsValidator() { + counters_.registerStatisticsValidator( + boost::bind(&AuthSrvImpl::validateStatistics, this, _1)); +} + +bool +AuthSrvImpl::validateStatistics(isc::data::ConstElementPtr data) const { + if (config_session_ == NULL) { + return (false); + } + return ( + config_session_->getModuleSpec().validateStatistics( + data, true)); +} + ConstElementPtr AuthSrvImpl::setDbFile(ConstElementPtr config) { ConstElementPtr answer = isc::config::createAnswer(); From a9a976d2a5871f1501018d697d3afd299ceec5da Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:37:22 +0900 Subject: [PATCH 487/974] [trac930] - Add implementation to validate statistics data -- When validation is success, it sends data to statistics module. But when it fails, it doesn't send and logs the message. - Add the function to register the validation function into the class --- src/bin/auth/statistics.cc | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc index 444fb8b35b..e62719f7e2 100644 --- a/src/bin/auth/statistics.cc +++ b/src/bin/auth/statistics.cc @@ -37,11 +37,14 @@ public: void inc(const AuthCounters::CounterType type); bool submitStatistics() const; void setStatisticsSession(isc::cc::AbstractSession* statistics_session); + void registerStatisticsValidator + (AuthCounters::validator_type validator); // Currently for testing purpose only uint64_t getCounter(const AuthCounters::CounterType type) const; private: std::vector counters_; isc::cc::AbstractSession* statistics_session_; + AuthCounters::validator_type validator_; }; AuthCountersImpl::AuthCountersImpl() : @@ -78,6 +81,14 @@ AuthCountersImpl::submitStatistics() const { << "]}"; isc::data::ConstElementPtr statistics_element = isc::data::Element::fromJSON(statistics_string); + // validate the statistics data before send + if (validator_) { + if (!validator_( + statistics_element->get("command")->get(1)->get("data"))) { + LOG_ERROR(auth_logger, AUTH_INVALID_STATISTICS_DATA); + return (false); + } + } try { // group_{send,recv}msg() can throw an exception when encountering // an error, and group_recvmsg() will throw an exception on timeout. @@ -106,6 +117,13 @@ AuthCountersImpl::setStatisticsSession statistics_session_ = statistics_session; } +void +AuthCountersImpl::registerStatisticsValidator + (AuthCounters::validator_type validator) +{ + validator_ = validator; +} + // Currently for testing purpose only uint64_t AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const { @@ -140,3 +158,10 @@ uint64_t AuthCounters::getCounter(const AuthCounters::CounterType type) const { return (impl_->getCounter(type)); } + +void +AuthCounters::registerStatisticsValidator + (AuthCounters::validator_type validator) const +{ + return (impl_->registerStatisticsValidator(validator)); +} From d0d5a67123b8009e89e84515eee4f93b37ec8497 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:41:34 +0900 Subject: [PATCH 488/974] [trac930] Add prototypes of validator_typea and registerStatisticsValidator - validator_type -- a type of statistics validation function - registerStatisticsValidator -- the function to register the validation function --- src/bin/auth/statistics.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h index 5bf643656d..c930414c65 100644 --- a/src/bin/auth/statistics.h +++ b/src/bin/auth/statistics.h @@ -131,6 +131,26 @@ public: /// \return the value of the counter specified by \a type. /// uint64_t getCounter(const AuthCounters::CounterType type) const; + + /// \brief A type of validation function for the specification in + /// isc::config::ModuleSpec. + /// + /// This type might be useful for not only statistics + /// specificatoin but also for config_data specification and for + /// commnad. + /// + typedef boost::function + validator_type; + + /// \brief Register a function type of the statistics validation + /// function for AuthCounters. + /// + /// This method never throws an exception. + /// + /// \param validator A function type of the validation of + /// statistics specification. + /// + void registerStatisticsValidator(AuthCounters::validator_type validator) const; }; #endif // __STATISTICS_H From ae8748f77a0261623216b1a11f9d979f555fe892 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:43:26 +0900 Subject: [PATCH 489/974] [trac930] Add unittests to test sumitStatistics with the validation of statistics data and add mock ModuleSpec class --- src/bin/auth/tests/statistics_unittest.cc | 66 ++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc index cd2755b110..98e573b495 100644 --- a/src/bin/auth/tests/statistics_unittest.cc +++ b/src/bin/auth/tests/statistics_unittest.cc @@ -16,6 +16,8 @@ #include +#include + #include #include @@ -76,6 +78,13 @@ protected: } MockSession statistics_session_; AuthCounters counters; + // no need to be inherited from the original class here. + class MockModuleSpec { + public: + bool validateStatistics(ConstElementPtr, const bool valid) const + { return (valid); } + }; + MockModuleSpec module_spec_; }; void @@ -181,7 +190,7 @@ TEST_F(AuthCountersTest, submitStatisticsWithException) { statistics_session_.setThrowSessionTimeout(false); } -TEST_F(AuthCountersTest, submitStatistics) { +TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) { // Submit statistics data. // Validate if it submits correct data. @@ -211,4 +220,59 @@ TEST_F(AuthCountersTest, submitStatistics) { EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue()); } +TEST_F(AuthCountersTest, submitStatisticsWithValidator) { + + //a validator for the unittest + AuthCounters::validator_type validator; + ConstElementPtr el; + + // Submit statistics data with correct statistics validator. + validator = boost::bind( + &AuthCountersTest::MockModuleSpec::validateStatistics, + &module_spec_, _1, true); + + EXPECT_TRUE(validator(el)); + + // register validator to AuthCounters + counters.registerStatisticsValidator(validator); + + // Counters should be initialized to 0. + EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY)); + EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY)); + + // UDP query counter is set to 2. + counters.inc(AuthCounters::COUNTER_UDP_QUERY); + counters.inc(AuthCounters::COUNTER_UDP_QUERY); + // TCP query counter is set to 1. + counters.inc(AuthCounters::COUNTER_TCP_QUERY); + + // checks the value returned by submitStatistics + EXPECT_TRUE(counters.submitStatistics()); + + // Destination is "Stats". + EXPECT_EQ("Stats", statistics_session_.msg_destination); + // Command is "set". + EXPECT_EQ("set", statistics_session_.sent_msg->get("command") + ->get(0)->stringValue()); + EXPECT_EQ("Auth", statistics_session_.sent_msg->get("command") + ->get(1)->get("owner")->stringValue()); + ConstElementPtr statistics_data = statistics_session_.sent_msg + ->get("command")->get(1) + ->get("data"); + // UDP query counter is 2 and TCP query counter is 1. + EXPECT_EQ(2, statistics_data->get("queries.udp")->intValue()); + EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue()); + + // Submit statistics data with incorrect statistics validator. + validator = boost::bind( + &AuthCountersTest::MockModuleSpec::validateStatistics, + &module_spec_, _1, false); + + EXPECT_FALSE(validator(el)); + + counters.registerStatisticsValidator(validator); + + // checks the value returned by submitStatistics + EXPECT_FALSE(counters.submitStatistics()); +} } From a142fa6302e1e0ea2ad1c9faf59d6a70a53a6489 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:45:19 +0900 Subject: [PATCH 490/974] [trac930] add the logging when the validation of statistics data fails --- src/bin/bind10/bind10_src.py.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index f905892221..3deba6172b 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -330,6 +330,10 @@ class BoB: seq = self.cc_session.group_sendmsg(cmd, 'Stats') self.cc_session.group_recvmsg(True, seq) answer = isc.config.ccsession.create_answer(0) + else: + logger.fatal(BIND10_INVALID_STATISTICS_DATA); + answer = isc.config.ccsession.create_answer( + 1, "specified statistics data is invalid") elif command == "ping": answer = isc.config.ccsession.create_answer(0, "pong") elif command == "show_processes": From bcf37a11b08922d69d02fa2ea1b280b2fa2c21e0 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:50:41 +0900 Subject: [PATCH 491/974] [trac930] add changes because query counter names described in the specfile are changed. --- tests/system/bindctl/tests.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/system/bindctl/tests.sh b/tests/system/bindctl/tests.sh index 6923c4167c..49ef0f17b0 100755 --- a/tests/system/bindctl/tests.sh +++ b/tests/system/bindctl/tests.sh @@ -24,6 +24,10 @@ SYSTEMTESTTOP=.. status=0 n=0 +# TODO: consider consistency with statistics definition in auth.spec +auth_queries_tcp="\" +auth_queries_udp="\" + echo "I:Checking b10-auth is working by default ($n)" $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1 # perform a simple check on the output (digcomp would be too much for this) @@ -40,8 +44,8 @@ echo 'Stats show --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # the server should have received 1 UDP and 1 TCP queries (TCP query was # sent from the server startup script) -grep "\"auth.queries.tcp\": 1," bindctl.out.$n > /dev/null || status=1 -grep "\"auth.queries.udp\": 1," bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_tcp".*\<1\>" bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_udp".*\<1\>" bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` @@ -73,8 +77,8 @@ echo 'Stats show ' | $RUN_BINDCTL \ --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # The statistics counters should have been reset while stop/start. -grep "\"auth.queries.tcp\": 0," bindctl.out.$n > /dev/null || status=1 -grep "\"auth.queries.udp\": 1," bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_tcp".*\<0\>" bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_udp".*\<1\>" bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` @@ -97,8 +101,8 @@ echo 'Stats show ' | $RUN_BINDCTL \ --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # The statistics counters shouldn't be reset due to hot-swapping datasource. -grep "\"auth.queries.tcp\": 0," bindctl.out.$n > /dev/null || status=1 -grep "\"auth.queries.udp\": 2," bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_tcp".*\<0\>" bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_udp".*\<2\>" bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` From 7275c59de54593d3baca81345226dda2d3a19c30 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 21:40:07 +0900 Subject: [PATCH 492/974] [trac930] fix conflicts with trac1021 --- src/bin/stats/tests/b10-stats-httpd_test.py | 89 ++++++++++++--------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index fcf95ad36f..2cc78ddc32 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -548,10 +548,11 @@ class TestStatsHttpd(unittest.TestCase): def test_xml_handler(self): orig_get_stats_data = stats_httpd.StatsHttpd.get_stats_data - stats_httpd.StatsHttpd.get_stats_data = lambda x: {'foo':'bar'} + stats_httpd.StatsHttpd.get_stats_data = lambda x: \ + { 'Dummy' : { 'foo':'bar' } } xml_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XML_TEMPLATE_LOCATION).substitute( - xml_string='bar', + xml_string='bar', xsd_namespace=stats_httpd.XSD_NAMESPACE, xsd_url_path=stats_httpd.XSD_URL_PATH, xsl_url_path=stats_httpd.XSL_URL_PATH) @@ -559,7 +560,8 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual(type(xml_body1), str) self.assertEqual(type(xml_body2), str) self.assertEqual(xml_body1, xml_body2) - stats_httpd.StatsHttpd.get_stats_data = lambda x: {'bar':'foo'} + stats_httpd.StatsHttpd.get_stats_data = lambda x: \ + { 'Dummy' : {'bar':'foo'} } xml_body2 = stats_httpd.StatsHttpd().xml_handler() self.assertNotEqual(xml_body1, xml_body2) stats_httpd.StatsHttpd.get_stats_data = orig_get_stats_data @@ -567,35 +569,41 @@ class TestStatsHttpd(unittest.TestCase): def test_xsd_handler(self): orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - [{ - "item_name": "foo", - "item_type": "string", - "item_optional": False, - "item_default": "bar", - "item_description": "foo is bar", - "item_title": "Foo" - }] + { "Dummy" : + [{ + "item_name": "foo", + "item_type": "string", + "item_optional": False, + "item_default": "bar", + "item_description": "foo is bar", + "item_title": "Foo" + }] + } xsd_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XSD_TEMPLATE_LOCATION).substitute( - xsd_string='' \ + xsd_string=\ + '' \ + '' \ + 'Foo' \ + 'foo is bar' \ - + '', + + '' \ + + '', xsd_namespace=stats_httpd.XSD_NAMESPACE) xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() self.assertEqual(type(xsd_body1), str) self.assertEqual(type(xsd_body2), str) self.assertEqual(xsd_body1, xsd_body2) stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - [{ - "item_name": "bar", - "item_type": "string", - "item_optional": False, - "item_default": "foo", - "item_description": "bar is foo", - "item_title": "bar" - }] + { "Dummy" : + [{ + "item_name": "bar", + "item_type": "string", + "item_optional": False, + "item_default": "foo", + "item_description": "bar is foo", + "item_title": "bar" + }] + } xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() self.assertNotEqual(xsd_body1, xsd_body2) stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec @@ -603,19 +611,22 @@ class TestStatsHttpd(unittest.TestCase): def test_xsl_handler(self): orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - [{ - "item_name": "foo", - "item_type": "string", - "item_optional": False, - "item_default": "bar", - "item_description": "foo is bar", - "item_title": "Foo" - }] + { "Dummy" : + [{ + "item_name": "foo", + "item_type": "string", + "item_optional": False, + "item_default": "bar", + "item_description": "foo is bar", + "item_title": "Foo" + }] + } xsl_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XSL_TEMPLATE_LOCATION).substitute( xsl_string='

' \ + + '' \ + '' \ - + '' \ + + '' \ + '', xsd_namespace=stats_httpd.XSD_NAMESPACE) xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() @@ -623,14 +634,16 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual(type(xsl_body2), str) self.assertEqual(xsl_body1, xsl_body2) stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - [{ - "item_name": "bar", - "item_type": "string", - "item_optional": False, - "item_default": "foo", - "item_description": "bar is foo", - "item_title": "bar" - }] + { "Dummy" : + [{ + "item_name": "bar", + "item_type": "string", + "item_optional": False, + "item_default": "foo", + "item_description": "bar is foo", + "item_title": "bar" + }] + } xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() self.assertNotEqual(xsl_body1, xsl_body2) stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec From 8a24b9066537caf373d0cfc11dca855eb6c3e4d9 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 10:14:57 +0900 Subject: [PATCH 493/974] [trac930] modify parse_spec function returns empty dict if list-type is not specified in the argument --- src/bin/stats/stats.py.in | 1 + src/bin/stats/tests/b10-stats_test.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 3faa3059a0..1abe4491df 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -72,6 +72,7 @@ def parse_spec(spec): """ parse spec type data """ + if type(spec) is not list: return {} def _parse_spec(spec): item_type = spec['item_type'] if item_type == "integer": diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index b013c7a8bc..b0f87f4377 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -69,7 +69,7 @@ class TestUtilties(unittest.TestCase): 'test_list2' : [0,0,0], 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 }, 'test_none' : None }) - self.assertRaises(TypeError, stats.parse_spec, None) + self.assertEqual(stats.parse_spec(None), {}) self.assertRaises(KeyError, stats.parse_spec, [{'item_name':'Foo'}]) def test_get_timestamp(self): From 2c22d334a05ec1e77299a6c55252f1d1c33082af Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 10:18:07 +0900 Subject: [PATCH 494/974] [trac930] add a test pattern which the set command with a non-existent item name is sent --- src/bin/stats/tests/b10-stats_test.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index b0f87f4377..6ddd39b18a 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -483,6 +483,15 @@ class TestStats(unittest.TestCase): self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( { "module_name": self.stats.module_name, "statistics": [] } ) + self.assertEqual(self.stats.command_set(owner='Stats', + data={ 'lname' : '_foo_@_bar_' }), + isc.config.create_answer( + 1, + "specified module name and/or statistics data are incorrect:" + + " unknown item lname")) + self.stats.statistics_data['Stats'] = {} + self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( + { "module_name": self.stats.module_name } ) self.assertEqual(self.stats.command_set(owner='Stats', data={ 'lname' : '_foo_@_bar_' }), isc.config.create_answer( From e8a22472e58bfc7df4a661d665152fe4d70454a6 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 16:42:54 +0900 Subject: [PATCH 495/974] [trac930] remove unnecessary a white space --- src/bin/stats/tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index cfffc15a35..b3e20b5f76 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -182,7 +182,7 @@ class MockAuth: "item_type": "integer", "item_optional": false, "item_default": 0, - "item_title": "Queries TCP ", + "item_title": "Queries TCP", "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" }, { From aaffb9c83c0fe59d9c7d590c5bea559ed8876269 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 16:49:21 +0900 Subject: [PATCH 496/974] [trac930] - correct error messages in bindctl it prints together with arguments. - modify the command_show function it reports statistics data of the module even if name is not specified. - add/modify unittests depending on the changes of error messages --- src/bin/stats/stats.py.in | 17 ++++---- src/bin/stats/tests/b10-stats_test.py | 56 +++++++++++++++++++++------ 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 1abe4491df..84cdf9ce64 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -259,7 +259,7 @@ class Stats: self.statistics_data[owner].update(data) return except KeyError: - errors.append('unknown module name') + errors.append("unknown module name: " + str(owner)) return errors def command_status(self): @@ -289,8 +289,6 @@ class Stats: else: logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_SHOW_ALL_COMMAND) - if owner and not name: - return isc.config.create_answer(1, "item name is not specified") errors = self.update_statistics_data( self.module_name, timestamp=get_timestamp(), @@ -298,11 +296,12 @@ class Stats: ) if errors: raise StatsError("stats spec file is incorrect") ret = self.get_statistics_data(owner, name) - if ret: + if ret is not None: return isc.config.create_answer(0, ret) else: return isc.config.create_answer( - 1, "specified module name and/or item name are incorrect") + 1, "specified arguments are incorrect: " \ + + "owner: " + str(owner) + ", name: " + str(name)) def command_showschema(self, owner=None, name=None): """ @@ -335,7 +334,8 @@ class Stats: else: return isc.config.create_answer(0, schema) return isc.config.create_answer( - 1, "specified module name and/or item name are incorrect") + 1, "specified arguments are incorrect: " \ + + "owner: " + str(owner) + ", name: " + str(name)) def command_set(self, owner, data): """ @@ -344,9 +344,8 @@ class Stats: errors = self.update_statistics_data(owner, **data) if errors: return isc.config.create_answer( - 1, - "specified module name and/or statistics data are incorrect: " - + ", ".join(errors)) + 1, "errors while setting statistics data: " \ + + ", ".join(errors)) errors = self.update_statistics_data( self.module_name, last_update_time=get_datetime() ) if errors: diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 6ddd39b18a..640b796fad 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -331,7 +331,7 @@ class TestStats(unittest.TestCase): self.assertEqual(self.stats.update_statistics_data(owner='Stats', lname=0.0), ['0.0 should be a string']) self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), - ['unknown module name']) + ['unknown module name: Dummy']) def test_command_status(self): self.assertEqual(self.stats.command_status(), @@ -346,13 +346,20 @@ class TestStats(unittest.TestCase): def test_command_show(self): self.assertEqual(self.stats.command_show(owner='Foo', name=None), - isc.config.create_answer(1, "item name is not specified")) + isc.config.create_answer( + 1, "specified arguments are incorrect: owner: Foo, name: None")) self.assertEqual(self.stats.command_show(owner='Foo', name='_bar_'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Foo, name: _bar_")) self.assertEqual(self.stats.command_show(owner='Foo', name='bar'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Foo, name: bar")) + self.assertEqual(self.stats.command_show(owner='Auth'), + isc.config.create_answer( + 0, {'queries.tcp': 0, 'queries.udp': 0})) + self.assertEqual(self.stats.command_show(owner='Auth', name='queries.udp'), + isc.config.create_answer( + 0, 0)) orig_get_timestamp = stats.get_timestamp orig_get_datetime = stats.get_datetime stats.get_timestamp = lambda : 1308730448.965706 @@ -452,13 +459,42 @@ class TestStats(unittest.TestCase): self.assertEqual(self.stats.command_showschema(owner='Foo'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Foo, name: None")) self.assertEqual(self.stats.command_showschema(owner='Foo', name='bar'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Foo, name: bar")) + self.assertEqual(self.stats.command_showschema(owner='Auth'), + isc.config.create_answer( + 0, [{ + "item_default": 0, + "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially", + "item_name": "queries.tcp", + "item_optional": False, + "item_title": "Queries TCP", + "item_type": "integer" + }, + { + "item_default": 0, + "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially", + "item_name": "queries.udp", + "item_optional": False, + "item_title": "Queries UDP", + "item_type": "integer" + }])) + self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.tcp'), + isc.config.create_answer( + 0, { + "item_default": 0, + "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially", + "item_name": "queries.tcp", + "item_optional": False, + "item_title": "Queries TCP", + "item_type": "integer" + })) + self.assertEqual(self.stats.command_showschema(owner='Stats', name='bar'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Stats, name: bar")) self.assertEqual(self.stats.command_showschema(name='bar'), isc.config.create_answer( 1, "module name is not specified")) @@ -487,8 +523,7 @@ class TestStats(unittest.TestCase): data={ 'lname' : '_foo_@_bar_' }), isc.config.create_answer( 1, - "specified module name and/or statistics data are incorrect:" - + " unknown item lname")) + "errors while setting statistics data: unknown item lname")) self.stats.statistics_data['Stats'] = {} self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( { "module_name": self.stats.module_name } ) @@ -496,8 +531,7 @@ class TestStats(unittest.TestCase): data={ 'lname' : '_foo_@_bar_' }), isc.config.create_answer( 1, - "specified module name and/or statistics data are incorrect:" - + " No statistics specification")) + "errors while setting statistics data: No statistics specification")) self.stats.statistics_data['Stats'] = {} self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( { "module_name": self.stats.module_name, From 4c2732cbf0bb7384ed61ab3604855f143a0c6c5d Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 20:45:18 +0900 Subject: [PATCH 497/974] [trac930] modify the update_modues function There is no part of statistics category in the spec file of a module which has no statistics data. --- src/bin/stats/stats.py.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 84cdf9ce64..0d43570cb8 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -208,8 +208,7 @@ class Stats: (rcode, value) = isc.config.ccsession.parse_answer(answer) if rcode == 0: for mod in value: - spec = { "module_name" : mod, - "statistics" : [] } + spec = { "module_name" : mod } if value[mod] and type(value[mod]) is list: spec["statistics"] = value[mod] modules[mod] = isc.config.module_spec.ModuleSpec(spec) From d86a9dceaddf5a2cee44170e6e677f492df5e0ea Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 28 Jul 2011 22:07:15 +0900 Subject: [PATCH 498/974] [trac930] modify logging add loggings and new messages for logging remove unused messages from the message file add test logging names into unittest scripts --- src/bin/stats/stats.py.in | 22 ++++++++++++--------- src/bin/stats/stats_httpd.py.in | 3 +-- src/bin/stats/stats_messages.mes | 21 ++++++++++---------- src/bin/stats/tests/b10-stats-httpd_test.py | 3 +++ src/bin/stats/tests/b10-stats_test.py | 3 +++ 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 0d43570cb8..ca206bf83a 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -148,9 +148,7 @@ class Stats: Start stats module """ self.running = True - # TODO: should be added into new logging interface - # if self.verbose: - # sys.stdout.write("[b10-stats] starting\n") + logger.info(STATS_STARTING) # request Bob to send statistics data logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) @@ -281,7 +279,7 @@ class Stats: """ handle show command """ - if (owner or name): + if owner or name: logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_SHOW_NAME_COMMAND, str(owner)+", "+str(name)) @@ -306,9 +304,13 @@ class Stats: """ handle show command """ - # TODO: should be added into new logging interface - # if self.verbose: - # sys.stdout.write("[b10-stats] 'showschema' command received\n") + if owner or name: + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND, + str(owner)+", "+str(name)) + else: + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND) self.update_modules() schema = {} schema_byname = {} @@ -364,10 +366,12 @@ if __name__ == "__main__": stats.start() except OptionValueError as ove: logger.fatal(STATS_BAD_OPTION_VALUE, ove) + sys.exit(1) except SessionError as se: logger.fatal(STATS_CC_SESSION_ERROR, se) - # TODO: should be added into new logging interface + sys.exit(1) except StatsError as se: - sys.exit("[b10-stats] %s" % se) + logger.fatal(STATS_START_ERROR, se) + sys.exit(1) except KeyboardInterrupt as kie: logger.info(STATS_STOPPED_BY_KEYBOARD) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index cc9c6045c2..32ec6b78fe 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -301,8 +301,7 @@ class StatsHttpd: # restore old config self.load_config(old_config) self.open_httpd() - return isc.config.ccsession.create_answer( - 1, "[b10-stats-httpd] %s" % err) + return isc.config.ccsession.create_answer(1, str(err)) else: return isc.config.ccsession.create_answer(0) diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes index 9ad07cf493..cfffb3adb8 100644 --- a/src/bin/stats/stats_messages.mes +++ b/src/bin/stats/stats_messages.mes @@ -28,16 +28,6 @@ control bus. A likely problem is that the message bus daemon This debug message is printed when the stats module has received a configuration update from the configuration manager. -% STATS_RECEIVED_REMOVE_COMMAND received command to remove %1 -A remove command for the given name was sent to the stats module, and -the given statistics value will now be removed. It will not appear in -statistics reports until it appears in a statistics update from a -module again. - -% STATS_RECEIVED_RESET_COMMAND received command to reset all statistics -The stats module received a command to clear all collected statistics. -The data is cleared until it receives an update from the modules again. - % STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics The stats module received a command to show all statistics that it has collected. @@ -72,4 +62,15 @@ installation problem, where the specification file stats.spec is from a different version of BIND 10 than the stats module itself. Please check your installation. +% STATS_STARTING starting +The stats module will be now starting. +% STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND received command to show all statistics schema +The stats module received a command to show all statistics schemas of all modules. + +% STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND received command to show statistics schema for %1 +The stats module received a command to show the specified statistics schema of the specified module. + +% STATS_START_ERROR stats module error: %1 +An internal error occurred while starting the stats module. The stats +module will be now shutting down. diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 2cc78ddc32..89dea29501 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -30,6 +30,9 @@ import stats_httpd import stats from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC +# set test name for logger +isc.log.init("b10-stats-httpd_test") + DUMMY_DATA = { 'Boss' : { "boot_time": "2011-03-04T11:59:06Z" diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 640b796fad..4c6bde0f5d 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -24,6 +24,9 @@ import stats import isc.cc.session from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, TIMEOUT_SEC +# set test name for logger +isc.log.init("b10-stats_test") + class TestUtilties(unittest.TestCase): items = [ { 'item_name': 'test_int1', 'item_type': 'integer', 'item_default': 12345 }, From e906efc3747f052128eef50bed0107a0d53546c8 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 29 Jul 2011 22:11:38 +0900 Subject: [PATCH 499/974] [trac930] remove a unnecessary x bit from stats_httpd.py.in --- src/bin/stats/stats_httpd.py.in | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/bin/stats/stats_httpd.py.in diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in old mode 100755 new mode 100644 From db0371fc9e5c7a85ab524ab7bc0b8169b9ba0486 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 1 Aug 2011 18:21:23 +0900 Subject: [PATCH 500/974] [trac930] rename the function name - rename the name of 'parse_spec' to 'get_spec_defaults' in the result of consideration of what it is doing - modify the description of the function as docstring - fix unitttests for the stats module depending on the function name --- src/bin/stats/stats.py.in | 18 ++++++++++-------- src/bin/stats/tests/b10-stats_test.py | 12 ++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index ca206bf83a..aab98614ce 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -68,12 +68,14 @@ def get_datetime(gmt=None): if not gmt: gmt = gmtime() return strftime("%Y-%m-%dT%H:%M:%SZ", gmt) -def parse_spec(spec): +def get_spec_defaults(spec): """ - parse spec type data + extracts the default values of the items from spec specified in + arg, and returns the dict-type variable which is a set of the item + names and the default values """ if type(spec) is not list: return {} - def _parse_spec(spec): + def _get_spec_defaults(spec): item_type = spec['item_type'] if item_type == "integer": return int(spec.get('item_default', 0)) @@ -86,14 +88,14 @@ def parse_spec(spec): elif item_type == "list": return spec.get( "item_default", - [ _parse_spec(s) for s in spec["list_item_spec"] ]) + [ _get_spec_defaults(s) for s in spec["list_item_spec"] ]) elif item_type == "map": return spec.get( "item_default", - dict([ (s["item_name"], _parse_spec(s)) for s in spec["map_item_spec"] ]) ) + dict([ (s["item_name"], _get_spec_defaults(s)) for s in spec["map_item_spec"] ]) ) else: return spec.get("item_default", None) - return dict([ (s['item_name'], _parse_spec(s)) for s in spec ]) + return dict([ (s['item_name'], _get_spec_defaults(s)) for s in spec ]) class Callback(): """ @@ -137,7 +139,7 @@ class Stats: name = "command_" + cmd["command_name"] try: callback = getattr(self, name) - kwargs = parse_spec(cmd["command_args"]) + kwargs = get_spec_defaults(cmd["command_args"]) self.callbacks[name] = Callback(command=callback, kwargs=kwargs) except AttributeError: raise StatsError(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) @@ -240,7 +242,7 @@ class Stats: self.update_modules() statistics_data = {} for (name, module) in self.modules.items(): - value = parse_spec(module.get_statistics_spec()) + value = get_spec_defaults(module.get_statistics_spec()) if module.validate_statistics(True, value): statistics_data[name] = value for (name, value) in self.statistics_data.items(): diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 4c6bde0f5d..011c95d9cb 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -56,9 +56,9 @@ class TestUtilties(unittest.TestCase): { 'item_name': 'test_none', 'item_type': 'none' } ] - def test_parse_spec(self): + def test_get_spec_defaults(self): self.assertEqual( - stats.parse_spec(self.items), { + stats.get_spec_defaults(self.items), { 'test_int1' : 12345 , 'test_real1' : 12345.6789 , 'test_bool1' : True , @@ -72,8 +72,8 @@ class TestUtilties(unittest.TestCase): 'test_list2' : [0,0,0], 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 }, 'test_none' : None }) - self.assertEqual(stats.parse_spec(None), {}) - self.assertRaises(KeyError, stats.parse_spec, [{'item_name':'Foo'}]) + self.assertEqual(stats.get_spec_defaults(None), {}) + self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}]) def test_get_timestamp(self): self.assertEqual(stats.get_timestamp(), 1308730448.965706) @@ -280,7 +280,7 @@ class TestStats(unittest.TestCase): self.assertTrue('Stats' in self.stats.modules) self.assertTrue('Boss' in self.stats.modules) self.assertFalse('Dummy' in self.stats.modules) - my_statistics_data = stats.parse_spec(self.stats.modules['Stats'].get_statistics_spec()) + my_statistics_data = stats.get_spec_defaults(self.stats.modules['Stats'].get_statistics_spec()) self.assertTrue('report_time' in my_statistics_data) self.assertTrue('boot_time' in my_statistics_data) self.assertTrue('last_update_time' in my_statistics_data) @@ -291,7 +291,7 @@ class TestStats(unittest.TestCase): self.assertEqual(my_statistics_data['last_update_time'], "1970-01-01T00:00:00Z") self.assertEqual(my_statistics_data['timestamp'], 0.0) self.assertEqual(my_statistics_data['lname'], "") - my_statistics_data = stats.parse_spec(self.stats.modules['Boss'].get_statistics_spec()) + my_statistics_data = stats.get_spec_defaults(self.stats.modules['Boss'].get_statistics_spec()) self.assertTrue('boot_time' in my_statistics_data) self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") From c6948a6df9aeedd3753bc4c5e3a553088cd98f63 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 1 Aug 2011 18:38:35 +0900 Subject: [PATCH 501/974] [trac930] raise StatsError including errors in the stats spec file --- src/bin/stats/stats.py.in | 10 +++++++--- src/bin/stats/tests/b10-stats_test.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index aab98614ce..bea70168e2 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -165,7 +165,8 @@ class Stats: boot_time=get_datetime(_BASETIME) ) if errors: - raise StatsError("stats spec file is incorrect") + raise StatsError("stats spec file is incorrect: " + + ", ".join(errors)) while self.running: self.mccs.check_command(False) @@ -293,7 +294,9 @@ class Stats: timestamp=get_timestamp(), report_time=get_datetime() ) - if errors: raise StatsError("stats spec file is incorrect") + if errors: + raise StatsError("stats spec file is incorrect: " + + ", ".join(errors)) ret = self.get_statistics_data(owner, name) if ret is not None: return isc.config.create_answer(0, ret) @@ -352,7 +355,8 @@ class Stats: errors = self.update_statistics_data( self.module_name, last_update_time=get_datetime() ) if errors: - raise StatsError("stats spec file is incorrect") + raise StatsError("stats spec file is incorrect: " + + ", ".join(errors)) return isc.config.create_answer(0) if __name__ == "__main__": diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 011c95d9cb..7eb25559d8 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -191,7 +191,7 @@ class TestStats(unittest.TestCase): def test_start_with_err(self): statsd = stats.Stats() - statsd.update_statistics_data = lambda x,**y: [1] + statsd.update_statistics_data = lambda x,**y: ['an error'] self.assertRaises(stats.StatsError, statsd.start) def test_config_handler(self): From 1d1a87939a010bd16ed23cd817261e9a655bf98f Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 2 Aug 2011 19:57:58 +0900 Subject: [PATCH 502/974] [trac930] remove tailing whitespaces. --- src/bin/stats/b10-stats-httpd.xml | 2 +- src/bin/stats/stats_httpd.py.in | 2 +- src/bin/stats/tests/Makefile.am | 2 +- src/bin/stats/tests/b10-stats-httpd_test.py | 10 +++++----- src/bin/stats/tests/b10-stats_test.py | 16 ++++++++-------- src/bin/stats/tests/test_utils.py | 8 ++++---- src/bin/tests/Makefile.am | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml index 3636d9d543..c8df9b8a6e 100644 --- a/src/bin/stats/b10-stats-httpd.xml +++ b/src/bin/stats/b10-stats-httpd.xml @@ -132,7 +132,7 @@ CONFIGURATION AND COMMANDS - The configurable setting in + The configurable setting in stats-httpd.spec is: diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index 32ec6b78fe..f8a09e5610 100644 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -415,7 +415,7 @@ class StatsHttpd: annotation.append(documentation) element.append(annotation) alltag.append(element) - + complextype = xml.etree.ElementTree.Element("complexType") complextype.append(alltag) mod_element = xml.etree.ElementTree.Element("element", { "name" : mod }) diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index 19f6117334..368e90c700 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -13,7 +13,7 @@ endif # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 89dea29501..5d22f72441 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -30,7 +30,7 @@ import stats_httpd import stats from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC -# set test name for logger +# set test name for logger isc.log.init("b10-stats-httpd_test") DUMMY_DATA = { @@ -364,7 +364,7 @@ class TestStatsHttpd(unittest.TestCase): for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.close_httpd() - + # dual stack (address is ipv4) if self.ipv6_enabled: self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] @@ -380,20 +380,20 @@ class TestStatsHttpd(unittest.TestCase): for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.close_httpd() - + # only-ipv4 single stack (force set ipv6 ) if not self.ipv6_enabled: self.stats_httpd.http_addrs = [ ('::1', 8000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - + # hostname self.stats_httpd.http_addrs = [ ('localhost', 8000) ] self.stats_httpd.open_httpd() for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.close_httpd() - + self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) self.assertEqual(type(self.stats_httpd.httpd), list) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 7eb25559d8..cd53a57821 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -24,7 +24,7 @@ import stats import isc.cc.session from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, TIMEOUT_SEC -# set test name for logger +# set test name for logger isc.log.init("b10-stats_test") class TestUtilties(unittest.TestCase): @@ -205,19 +205,19 @@ class TestStats(unittest.TestCase): self.base.boss.server._started.wait() self.assertEqual( send_command( - 'show', 'Stats', + 'show', 'Stats', params={ 'owner' : 'Boss', 'name' : 'boot_time' }), (0, '2011-06-22T08:14:08Z')) self.assertEqual( send_command( - 'set', 'Stats', + 'set', 'Stats', params={ 'owner' : 'Boss', 'data' : { 'boot_time' : '2012-06-22T18:24:08Z' } }), (0, None)) self.assertEqual( send_command( - 'show', 'Stats', + 'show', 'Stats', params={ 'owner' : 'Boss', 'name' : 'boot_time' }), (0, '2012-06-22T18:24:08Z')) @@ -267,7 +267,7 @@ class TestStats(unittest.TestCase): self.assertTrue('item_description' in item) if len(item) == 7: self.assertTrue('item_format' in item) - + self.assertEqual( send_command('__UNKNOWN__', 'Stats'), (1, "Unknown command: '__UNKNOWN__'")) @@ -340,13 +340,13 @@ class TestStats(unittest.TestCase): self.assertEqual(self.stats.command_status(), isc.config.create_answer( 0, "Stats is up. (PID " + str(os.getpid()) + ")")) - + def test_command_shutdown(self): self.stats.running = True self.assertEqual(self.stats.command_shutdown(), isc.config.create_answer(0)) self.assertFalse(self.stats.running) - + def test_command_show(self): self.assertEqual(self.stats.command_show(owner='Foo', name=None), isc.config.create_answer( @@ -380,7 +380,7 @@ class TestStats(unittest.TestCase): "statistics": [] } ) self.assertRaises( stats.StatsError, self.stats.command_show, owner='Foo', name='bar') - + def test_command_showchema(self): (rcode, value) = isc.config.ccsession.parse_answer( self.stats.command_showschema()) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index b3e20b5f76..a793dc69a6 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -10,7 +10,7 @@ import threading import tempfile import msgq -import isc.config.cfgmgr +import isc.config.cfgmgr import stats import stats_httpd @@ -49,7 +49,7 @@ class ThreadingServerManager: self.server._thread = threading.Thread( name=self.server_class_name, target=self.server.run) self.server._thread.daemon = True - + def run(self): self.server._thread.start() self.server._started.wait() @@ -94,7 +94,7 @@ class MockCfgmgr: def shutdown(self): self.cfgmgr.running = False - + class MockBoss: spec_str = """\ { @@ -157,7 +157,7 @@ class MockBoss: params = { "owner": "Boss", "data": { 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', self._BASETIME) - } + } } return send_command("set", "Stats", params=params, session=self.cc_session) return isc.config.create_answer(1, "Unknown Command") diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am index 56ff68b0c7..0dc5021302 100644 --- a/src/bin/tests/Makefile.am +++ b/src/bin/tests/Makefile.am @@ -14,7 +14,7 @@ endif # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif From e18a678b62d03729f065c40650d7183e2f260b22 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 2 Aug 2011 20:17:28 +0900 Subject: [PATCH 503/974] [trac930] modify b10-stats_test.py - set the constant variables in the setUp method in the TestUtilties class, and compare values returned from the functions with these constants in testing methods. [trac930] remove the tearDown method which has no test case in the TestCallback class --- src/bin/stats/tests/b10-stats_test.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index cd53a57821..0c2fa3c2f3 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -56,6 +56,13 @@ class TestUtilties(unittest.TestCase): { 'item_name': 'test_none', 'item_type': 'none' } ] + def setUp(self): + self.const_timestamp = 1308730448.965706 + self.const_timetuple = (2011, 6, 22, 8, 14, 8, 2, 173, 0) + self.const_datetime = '2011-06-22T08:14:08Z' + stats.time = lambda : self.const_timestamp + stats.gmtime = lambda : self.const_timetuple + def test_get_spec_defaults(self): self.assertEqual( stats.get_spec_defaults(self.items), { @@ -76,14 +83,12 @@ class TestUtilties(unittest.TestCase): self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}]) def test_get_timestamp(self): - self.assertEqual(stats.get_timestamp(), 1308730448.965706) + self.assertEqual(stats.get_timestamp(), self.const_timestamp) def test_get_datetime(self): - stats.time = lambda : 1308730448.965706 - stats.gmtime = lambda : (2011, 6, 22, 8, 14, 8, 2, 173, 0) - self.assertEqual(stats.get_datetime(), '2011-06-22T08:14:08Z') + self.assertEqual(stats.get_datetime(), self.const_datetime) self.assertNotEqual(stats.get_datetime( - (2011, 6, 22, 8, 23, 40, 2, 173, 0)), '2011-06-22T08:14:08Z') + (2011, 6, 22, 8, 23, 40, 2, 173, 0)), self.const_datetime) class TestCallback(unittest.TestCase): def setUp(self): @@ -108,9 +113,6 @@ class TestCallback(unittest.TestCase): args=self.dummy_args ) - def tearDown(self): - pass - def test_init(self): self.assertEqual((self.cback1.command, self.cback1.args, self.cback1.kwargs), (self.dummy_func, self.dummy_args, self.dummy_kwargs)) From 7a31e95e63013a298b449573cc5336bcd64a0419 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 2 Aug 2011 21:44:07 +0900 Subject: [PATCH 504/974] [trac930] modify stats.py - add more documentations into update_modules, get_statistics_data and update_statistics_data methods - modify two methods: "update_modules" and "get_statistics_data" methods raise StatsError instead of just returning None, when communication between stats module and config manager is failed or when it can't find specified statistics data. - also modify the unittest depending on the changes of these behaviors. --- src/bin/stats/stats.py.in | 29 ++++++++++++++++++++------- src/bin/stats/tests/b10-stats_test.py | 15 ++++++++++---- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index bea70168e2..9f24c67a9f 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -197,7 +197,10 @@ class Stats: def update_modules(self): """ - update information of each module + updates information of each module. This method gets each + module's information from the config manager and sets it into + self.modules. If its getting from the config manager fails, it + raises StatsError. """ modules = {} seq = self.cc_session.group_sendmsg( @@ -213,12 +216,16 @@ class Stats: if value[mod] and type(value[mod]) is list: spec["statistics"] = value[mod] modules[mod] = isc.config.module_spec.ModuleSpec(spec) + else: + raise StatsError("Updating module spec fails: " + str(value)) modules[self.module_name] = self.mccs.get_module_spec() self.modules = modules def get_statistics_data(self, owner=None, name=None): """ - return statistics data which stats module has of each module + returns statistics data which stats module has of each + module. If it can't find specified statistics data, it raises + StatsError. """ self.update_statistics_data() if owner and name: @@ -235,10 +242,18 @@ class Stats: pass else: return self.statistics_data + raise StatsError("No statistics data found: " + + "owner: " + str(owner) + ", " + + "name: " + str(name)) def update_statistics_data(self, owner=None, **data): """ - change statistics date of specified module into specified data + change statistics date of specified module into specified + data. It updates information of each module first, and it + updates statistics data. If specified data is invalid for + statistics spec of specified owner, it returns a list of error + messeges. If there is no error or if neither owner nor data is + specified in args, it returns None. """ self.update_modules() statistics_data = {} @@ -297,10 +312,10 @@ class Stats: if errors: raise StatsError("stats spec file is incorrect: " + ", ".join(errors)) - ret = self.get_statistics_data(owner, name) - if ret is not None: - return isc.config.create_answer(0, ret) - else: + try: + return isc.config.create_answer( + 0, self.get_statistics_data(owner, name)) + except StatsError: return isc.config.create_answer( 1, "specified arguments are incorrect: " \ + "owner: " + str(owner) + ", name: " + str(name)) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 0c2fa3c2f3..acf2269c2b 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -296,6 +296,10 @@ class TestStats(unittest.TestCase): my_statistics_data = stats.get_spec_defaults(self.stats.modules['Boss'].get_statistics_spec()) self.assertTrue('boot_time' in my_statistics_data) self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") + orig_parse_answer = stats.isc.config.ccsession.parse_answer + stats.isc.config.ccsession.parse_answer = lambda x: (99, 'error') + self.assertRaises(stats.StatsError, self.stats.update_modules) + stats.isc.config.ccsession.parse_answer = orig_parse_answer def test_get_statistics_data(self): my_statistics_data = self.stats.get_statistics_data() @@ -307,7 +311,7 @@ class TestStats(unittest.TestCase): self.assertTrue('last_update_time' in my_statistics_data) self.assertTrue('timestamp' in my_statistics_data) self.assertTrue('lname' in my_statistics_data) - self.assertIsNone(self.stats.get_statistics_data(owner='Foo')) + self.assertRaises(stats.StatsError, self.stats.get_statistics_data, owner='Foo') my_statistics_data = self.stats.get_statistics_data(owner='Stats') self.assertTrue('boot_time' in my_statistics_data) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='report_time') @@ -320,9 +324,12 @@ class TestStats(unittest.TestCase): self.assertEqual(my_statistics_data, 0.0) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='lname') self.assertEqual(my_statistics_data, '') - self.assertIsNone(self.stats.get_statistics_data(owner='Stats', name='Bar')) - self.assertIsNone(self.stats.get_statistics_data(owner='Foo', name='Bar')) - self.assertEqual(self.stats.get_statistics_data(name='Bar'), None) + self.assertRaises(stats.StatsError, self.stats.get_statistics_data, + owner='Stats', name='Bar') + self.assertRaises(stats.StatsError, self.stats.get_statistics_data, + owner='Foo', name='Bar') + self.assertRaises(stats.StatsError, self.stats.get_statistics_data, + name='Bar') def test_update_statistics_data(self): self.stats.update_statistics_data(owner='Stats', lname='foo@bar') From b8cecbbd905c10d28bcb905def7160d9e406dac4 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 2 Aug 2011 22:00:11 +0900 Subject: [PATCH 505/974] [trac930] add comments about abstracts of the test scripts in their headers --- src/bin/stats/tests/b10-stats-httpd_test.py | 7 +++++++ src/bin/stats/tests/b10-stats_test.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 5d22f72441..e1c05dae42 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -13,6 +13,13 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +This unittests run Msgq, Cfgmgr, Auth, Boss and Stats as mock in +background. Because the stats httpd communicates various other modules +in runtime. However this aim is not to actually simulate a whole +system running. +""" + import unittest import os import imp diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index acf2269c2b..f8830eb5ea 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -13,6 +13,13 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +This unittests run Msgq, Cfgmgr, Auth and Boss as mock in +background. Because the stats module communicates various other +modules in runtime. However this aim is not to actually simulate a +whole system running. +""" + import unittest import os import threading From 0314c7bb66b85775dea73c95463eed88e9e286c3 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 3 Aug 2011 11:41:05 +0900 Subject: [PATCH 506/974] [trac930] refactor unittests - remove time.sleep from various unittests and add in the "run" method in ThreadingServerManager - adjust the sleep time (TIMEOUT_SEC) - join some small unittests (test_start_with_err, test_command_status, test_command_shutdown) --- src/bin/stats/tests/b10-stats-httpd_test.py | 7 ------- src/bin/stats/tests/b10-stats_test.py | 23 +++++++++++---------- src/bin/stats/tests/test_utils.py | 6 ++++-- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index e1c05dae42..870e6b9038 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -78,7 +78,6 @@ class TestHttpHandler(unittest.TestCase): self.assertTrue(type(self.stats_httpd.httpd) is list) self.assertEqual(len(self.stats_httpd.httpd), 0) statshttpd_server.run() - time.sleep(TIMEOUT_SEC*8) client = http.client.HTTPConnection(address, port) client._http_vsn_str = 'HTTP/1.0\n' client.connect() @@ -175,7 +174,6 @@ class TestHttpHandler(unittest.TestCase): statshttpd_server.run() self.assertTrue(self.stats_server.server.running) self.stats_server.shutdown() - time.sleep(TIMEOUT_SEC*2) self.assertFalse(self.stats_server.server.running) statshttpd.cc_session.set_timeout(milliseconds=TIMEOUT_SEC/1000) client = http.client.HTTPConnection(address, port) @@ -213,7 +211,6 @@ class TestHttpHandler(unittest.TestCase): lambda cmd, args: \ isc.config.ccsession.create_answer(1, "I have an error.") ) - time.sleep(TIMEOUT_SEC*5) client = http.client.HTTPConnection(address, port) client.connect() @@ -244,7 +241,6 @@ class TestHttpHandler(unittest.TestCase): self.stats_httpd = statshttpd_server.server self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) statshttpd_server.run() - time.sleep(TIMEOUT_SEC*5) client = http.client.HTTPConnection(address, port) client.connect() client.putrequest('HEAD', stats_httpd.XML_URL_PATH) @@ -423,7 +419,6 @@ class TestStatsHttpd(unittest.TestCase): self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) self.statshttpd_server.server.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) self.statshttpd_server.run() - time.sleep(TIMEOUT_SEC) self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) self.statshttpd_server.shutdown() @@ -434,7 +429,6 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd = self.statshttpd_server.server self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65455 }]}) self.statshttpd_server.run() - time.sleep(TIMEOUT_SEC*2) self.assertTrue(self.stats_httpd.running) self.statshttpd_server.shutdown() self.assertFalse(self.stats_httpd.running) @@ -461,7 +455,6 @@ class TestStatsHttpd(unittest.TestCase): statshttpd = statshttpd_server.server statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) statshttpd_server.run() - time.sleep(TIMEOUT_SEC*2) statshttpd_server.shutdown() def test_open_template(self): diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index f8830eb5ea..b2c1b7fdea 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -189,28 +189,28 @@ class TestStats(unittest.TestCase): stats.SPECFILE_LOCATION = orig_spec_location def test_start(self): + # start without err statsserver = ThreadingServerManager(MyStats) - stats = statsserver.server - self.assertFalse(stats.running) + statsd = statsserver.server + self.assertFalse(statsd.running) statsserver.run() - time.sleep(TIMEOUT_SEC) - self.assertTrue(stats.running) + self.assertTrue(statsd.running) statsserver.shutdown() - self.assertFalse(stats.running) + self.assertFalse(statsd.running) - def test_start_with_err(self): + # start with err statsd = stats.Stats() statsd.update_statistics_data = lambda x,**y: ['an error'] self.assertRaises(stats.StatsError, statsd.start) - def test_config_handler(self): + def test_handlers(self): + # config_handler self.assertEqual(self.stats.config_handler({'foo':'bar'}), isc.config.create_answer(0)) - def test_command_handler(self): + # command_handler statsserver = ThreadingServerManager(MyStats) statsserver.run() - time.sleep(TIMEOUT_SEC*4) self.base.boss.server._started.wait() self.assertEqual( send_command( @@ -352,12 +352,13 @@ class TestStats(unittest.TestCase): self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), ['unknown module name: Dummy']) - def test_command_status(self): + def test_commands(self): + # status self.assertEqual(self.stats.command_status(), isc.config.create_answer( 0, "Stats is up. (PID " + str(os.getpid()) + ")")) - def test_command_shutdown(self): + # shutdown self.stats.running = True self.assertEqual(self.stats.command_shutdown(), isc.config.create_answer(0)) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index a793dc69a6..f9ab969e13 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -15,9 +15,9 @@ import stats import stats_httpd # TODO: consider appropriate timeout seconds -TIMEOUT_SEC = 0.01 +TIMEOUT_SEC = 0.05 -def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC*2): +def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC): if not session: cc_session = isc.cc.Session() else: @@ -53,6 +53,8 @@ class ThreadingServerManager: def run(self): self.server._thread.start() self.server._started.wait() + # waiting for the server's being ready for listening + time.sleep(TIMEOUT_SEC) def shutdown(self): self.server.shutdown() From da5d5926cb26ca8dbdae119c03687cd3415f6638 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 5 Aug 2011 14:48:27 +0900 Subject: [PATCH 507/974] [trac930] - change address for test to 127.0.0.1 due to platform 127.0.0.2 can't be assigned - remove unnecessary thread.Event.wait() - add thread.Event.clear() after thread.Event.wait() --- src/bin/stats/tests/b10-stats-httpd_test.py | 4 ++-- src/bin/stats/tests/b10-stats_test.py | 1 - src/bin/stats/tests/test_utils.py | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 870e6b9038..3677c5f1f3 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -512,13 +512,13 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="127.0.0.2",port=8000)])), + dict(listen_on=[dict(address="127.0.0.1",port=8000)])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "127.0.0.2") + self.assertTrue(addr["address"] == "127.0.0.1") self.assertTrue(addr["port"] == 8000) if self.ipv6_enabled: diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index b2c1b7fdea..2757c61ece 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -211,7 +211,6 @@ class TestStats(unittest.TestCase): # command_handler statsserver = ThreadingServerManager(MyStats) statsserver.run() - self.base.boss.server._started.wait() self.assertEqual( send_command( 'show', 'Stats', diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index f9ab969e13..e79db48951 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -53,6 +53,7 @@ class ThreadingServerManager: def run(self): self.server._thread.start() self.server._started.wait() + self.server._started.clear() # waiting for the server's being ready for listening time.sleep(TIMEOUT_SEC) From fcc707041d663b98c1992cdd1402cc183155d3c0 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 5 Aug 2011 16:24:03 +0900 Subject: [PATCH 508/974] [trac930] - revise header comments in each test script - replace some hard-coded time strings with the constants defined in the setUp function - merged several checks about B10_FROM_SOURCE into the TestOSEnv class --- src/bin/stats/tests/b10-stats-httpd_test.py | 9 ++- src/bin/stats/tests/b10-stats_test.py | 90 ++++++++++++--------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 3677c5f1f3..8c84277930 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -14,10 +14,11 @@ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ -This unittests run Msgq, Cfgmgr, Auth, Boss and Stats as mock in -background. Because the stats httpd communicates various other modules -in runtime. However this aim is not to actually simulate a whole -system running. +In each of these tests we start several virtual components. They are +not the real components, no external processes are started. They are +just simple mock objects running each in its own thread and pretending +to be bind10 modules. This helps testing the stats http server in a +close to real environment. """ import unittest diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 2757c61ece..7cf4f7ede0 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -14,10 +14,11 @@ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ -This unittests run Msgq, Cfgmgr, Auth and Boss as mock in -background. Because the stats module communicates various other -modules in runtime. However this aim is not to actually simulate a -whole system running. +In each of these tests we start several virtual components. They are +not the real components, no external processes are started. They are +just simple mock objects running each in its own thread and pretending +to be bind10 modules. This helps testing the stats module in a close +to real environment. """ import unittest @@ -146,11 +147,9 @@ class TestStats(unittest.TestCase): def setUp(self): self.base = BaseModules() self.stats = stats.Stats() - self.assertTrue("B10_FROM_SOURCE" in os.environ) - self.assertEqual(stats.SPECFILE_LOCATION, \ - os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + \ - os.sep + "stats.spec") + self.const_timestamp = 1308730448.965706 + self.const_datetime = '2011-06-22T08:14:08Z' + self.const_default_datetime = '1970-01-01T00:00:00Z' def tearDown(self): self.base.shutdown() @@ -216,19 +215,19 @@ class TestStats(unittest.TestCase): 'show', 'Stats', params={ 'owner' : 'Boss', 'name' : 'boot_time' }), - (0, '2011-06-22T08:14:08Z')) + (0, self.const_datetime)) self.assertEqual( send_command( 'set', 'Stats', params={ 'owner' : 'Boss', - 'data' : { 'boot_time' : '2012-06-22T18:24:08Z' } }), + 'data' : { 'boot_time' : self.const_datetime } }), (0, None)) self.assertEqual( send_command( 'show', 'Stats', params={ 'owner' : 'Boss', 'name' : 'boot_time' }), - (0, '2012-06-22T18:24:08Z')) + (0, self.const_datetime)) self.assertEqual( send_command('status', 'Stats'), (0, "Stats is up. (PID " + str(os.getpid()) + ")")) @@ -242,7 +241,7 @@ class TestStats(unittest.TestCase): self.assertEqual(len(value['Stats']), 5) self.assertEqual(len(value['Boss']), 1) self.assertTrue('boot_time' in value['Boss']) - self.assertEqual(value['Boss']['boot_time'], '2012-06-22T18:24:08Z') + self.assertEqual(value['Boss']['boot_time'], self.const_datetime) self.assertTrue('report_time' in value['Stats']) self.assertTrue('boot_time' in value['Stats']) self.assertTrue('last_update_time' in value['Stats']) @@ -294,14 +293,14 @@ class TestStats(unittest.TestCase): self.assertTrue('last_update_time' in my_statistics_data) self.assertTrue('timestamp' in my_statistics_data) self.assertTrue('lname' in my_statistics_data) - self.assertEqual(my_statistics_data['report_time'], "1970-01-01T00:00:00Z") - self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") - self.assertEqual(my_statistics_data['last_update_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['report_time'], self.const_default_datetime) + self.assertEqual(my_statistics_data['boot_time'], self.const_default_datetime) + self.assertEqual(my_statistics_data['last_update_time'], self.const_default_datetime) self.assertEqual(my_statistics_data['timestamp'], 0.0) self.assertEqual(my_statistics_data['lname'], "") my_statistics_data = stats.get_spec_defaults(self.stats.modules['Boss'].get_statistics_spec()) self.assertTrue('boot_time' in my_statistics_data) - self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['boot_time'], self.const_default_datetime) orig_parse_answer = stats.isc.config.ccsession.parse_answer stats.isc.config.ccsession.parse_answer = lambda x: (99, 'error') self.assertRaises(stats.StatsError, self.stats.update_modules) @@ -321,11 +320,11 @@ class TestStats(unittest.TestCase): my_statistics_data = self.stats.get_statistics_data(owner='Stats') self.assertTrue('boot_time' in my_statistics_data) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='report_time') - self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data, self.const_default_datetime) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='boot_time') - self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data, self.const_default_datetime) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='last_update_time') - self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data, self.const_default_datetime) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='timestamp') self.assertEqual(my_statistics_data, 0.0) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='lname') @@ -342,10 +341,10 @@ class TestStats(unittest.TestCase): self.assertTrue('Stats' in self.stats.statistics_data) my_statistics_data = self.stats.statistics_data['Stats'] self.assertEqual(my_statistics_data['lname'], 'foo@bar') - self.stats.update_statistics_data(owner='Stats', last_update_time='2000-01-01T10:10:10Z') + self.stats.update_statistics_data(owner='Stats', last_update_time=self.const_datetime) self.assertTrue('Stats' in self.stats.statistics_data) my_statistics_data = self.stats.statistics_data['Stats'] - self.assertEqual(my_statistics_data['last_update_time'], '2000-01-01T10:10:10Z') + self.assertEqual(my_statistics_data['last_update_time'], self.const_datetime) self.assertEqual(self.stats.update_statistics_data(owner='Stats', lname=0.0), ['0.0 should be a string']) self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), @@ -381,14 +380,14 @@ class TestStats(unittest.TestCase): 0, 0)) orig_get_timestamp = stats.get_timestamp orig_get_datetime = stats.get_datetime - stats.get_timestamp = lambda : 1308730448.965706 - stats.get_datetime = lambda : '2011-06-22T08:14:08Z' - self.assertEqual(stats.get_timestamp(), 1308730448.965706) - self.assertEqual(stats.get_datetime(), '2011-06-22T08:14:08Z') + stats.get_timestamp = lambda : self.const_timestamp + stats.get_datetime = lambda : self.const_datetime + self.assertEqual(stats.get_timestamp(), self.const_timestamp) + self.assertEqual(stats.get_datetime(), self.const_datetime) self.assertEqual(self.stats.command_show(owner='Stats', name='report_time'), \ - isc.config.create_answer(0, '2011-06-22T08:14:08Z')) - self.assertEqual(self.stats.statistics_data['Stats']['timestamp'], 1308730448.965706) - self.assertEqual(self.stats.statistics_data['Stats']['boot_time'], '1970-01-01T00:00:00Z') + isc.config.create_answer(0, self.const_datetime)) + self.assertEqual(self.stats.statistics_data['Stats']['timestamp'], self.const_timestamp) + self.assertEqual(self.stats.statistics_data['Stats']['boot_time'], self.const_default_datetime) stats.get_timestamp = orig_get_timestamp stats.get_datetime = orig_get_datetime self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( @@ -520,17 +519,17 @@ class TestStats(unittest.TestCase): def test_command_set(self): orig_get_datetime = stats.get_datetime - stats.get_datetime = lambda : '2011-06-22T06:12:38Z' + stats.get_datetime = lambda : self.const_datetime (rcode, value) = isc.config.ccsession.parse_answer( self.stats.command_set(owner='Boss', - data={ 'boot_time' : '2011-06-22T13:15:04Z' })) + data={ 'boot_time' : self.const_datetime })) stats.get_datetime = orig_get_datetime self.assertEqual(rcode, 0) self.assertTrue(value is None) self.assertEqual(self.stats.statistics_data['Boss']['boot_time'], - '2011-06-22T13:15:04Z') + self.const_datetime) self.assertEqual(self.stats.statistics_data['Stats']['last_update_time'], - '2011-06-22T06:12:38Z') + self.const_datetime) self.assertEqual(self.stats.command_set(owner='Stats', data={ 'lname' : 'foo@bar' }), isc.config.create_answer(0, None)) @@ -566,16 +565,27 @@ class TestStats(unittest.TestCase): self.assertRaises(stats.StatsError, self.stats.command_set, owner='Stats', data={ 'dummy' : '_xxxx_yyyy_zzz_' }) +class TestOSEnv(unittest.TestCase): def test_osenv(self): """ - test for not having environ "B10_FROM_SOURCE" + test for the environ variable "B10_FROM_SOURCE" + "B10_FROM_SOURCE" is set in Makefile """ - if "B10_FROM_SOURCE" in os.environ: - path = os.environ["B10_FROM_SOURCE"] - os.environ.pop("B10_FROM_SOURCE") - imp.reload(stats) - os.environ["B10_FROM_SOURCE"] = path - imp.reload(stats) + # test case having B10_FROM_SOURCE + self.assertTrue("B10_FROM_SOURCE" in os.environ) + self.assertEqual(stats.SPECFILE_LOCATION, \ + os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + \ + os.sep + "stats.spec") + # test case not having B10_FROM_SOURCE + path = os.environ["B10_FROM_SOURCE"] + os.environ.pop("B10_FROM_SOURCE") + self.assertFalse("B10_FROM_SOURCE" in os.environ) + # import stats again + imp.reload(stats) + # revert the changes + os.environ["B10_FROM_SOURCE"] = path + imp.reload(stats) def test_main(): unittest.main() From f20be125d667bceea0d940fc5fabf87b2eef86cd Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 9 Aug 2011 15:57:22 +0900 Subject: [PATCH 509/974] [trac930] revise the entry of ChangeLog for trac928, trac929 and trac930 --- ChangeLog | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index d4cd88de14..3e1efbef88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,8 @@ -xxx. [func] naokikambe - Add statistics category in each module spec file for management of - statistics data schemas by each module. Add get_statistics_spec into - cfgmgr and related codes. show statistics data and data schema by each - module via both bintcl and HTTP/XML interfaces. Change item name in - each statistics data. (Remove prefix "xxx." indicating the module - name.) Add new mock modules for unittests of stats and stats httpd - modules. +xxx. [func] naokikambe + Statistics items are specified by each module's spec file. + Stats module can read these through the config manager. Stats + module and stats httpd report statistics data and statistics + schema by each module via both bindctl and HTTP/XML. (Trac #928,#929,#930, git nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn) 278. [doc] jelte From 004afad6ea3fba7c8dd7730428b50fd770daec66 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 15 Aug 2011 14:59:08 +0900 Subject: [PATCH 510/974] [master] update the ChangeLog entry for trac928, trac929 and trac930 --- ChangeLog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3e1efbef88..94b9a22f48 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,9 @@ -xxx. [func] naokikambe +279. [func] naokikambe Statistics items are specified by each module's spec file. Stats module can read these through the config manager. Stats module and stats httpd report statistics data and statistics schema by each module via both bindctl and HTTP/XML. - (Trac #928,#929,#930, git nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn) + (Trac #928,#929,#930, git f20be125d667bceea0d940fc5fabf87b2eef86cd) 278. [doc] jelte Add logging configuration documentation to the guide. From d7834356a301b162fb9757427359d0dbac95cecf Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Mon, 15 Aug 2011 14:43:22 +0800 Subject: [PATCH 511/974] [trac1113] update minfo unittest, avoid using hardcoded test data --- src/lib/dns/rdata/generic/minfo_14.cc | 11 ++- src/lib/dns/rdata/generic/minfo_14.h | 5 + src/lib/dns/tests/rdata_minfo_unittest.cc | 98 ++++++++++++++----- src/lib/dns/tests/testdata/Makefile.am | 14 ++- .../dns/tests/testdata/rdata_minfo_fromWire | 47 --------- .../tests/testdata/rdata_minfo_fromWire1.spec | 3 + .../tests/testdata/rdata_minfo_fromWire2.spec | 7 ++ .../tests/testdata/rdata_minfo_fromWire3.spec | 6 ++ .../tests/testdata/rdata_minfo_fromWire4.spec | 6 ++ .../tests/testdata/rdata_minfo_fromWire5.spec | 5 + .../tests/testdata/rdata_minfo_fromWire6.spec | 5 + src/lib/dns/tests/testdata/rdata_minfo_toWire | 15 --- .../tests/testdata/rdata_minfo_toWire1.spec | 5 + .../tests/testdata/rdata_minfo_toWire2.spec | 6 ++ .../rdata_minfo_toWireUncompressed.wire | 8 -- ...c => rdata_minfo_toWireUncompressed1.spec} | 1 + .../rdata_minfo_toWireUncompressed2.spec | 8 ++ 17 files changed, 150 insertions(+), 100 deletions(-) delete mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec delete mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec delete mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.wire rename src/lib/dns/tests/testdata/{rdata_minfo_toWireUncompressed.spec => rdata_minfo_toWireUncompressed1.spec} (89%) create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec diff --git a/src/lib/dns/rdata/generic/minfo_14.cc b/src/lib/dns/rdata/generic/minfo_14.cc index 072c767f48..734fbc3102 100644 --- a/src/lib/dns/rdata/generic/minfo_14.cc +++ b/src/lib/dns/rdata/generic/minfo_14.cc @@ -79,8 +79,7 @@ MINFO::MINFO(const std::string& minfo_str) : /// names in the wire is invalid. MINFO::MINFO(InputBuffer& buffer, size_t) : rmailbox_(buffer), emailbox_(buffer) -{ -} +{} /// \brief Copy constructor. /// @@ -114,6 +113,14 @@ MINFO::toWire(OutputBuffer& buffer) const { emailbox_.toWire(buffer); } +MINFO& +MINFO::operator=(const MINFO& source) { + rmailbox_ = source.rmailbox_; + emailbox_ = source.emailbox_; + + return (*this); +} + /// \brief Render the \c MINFO in the wire format with taking into account /// compression. /// diff --git a/src/lib/dns/rdata/generic/minfo_14.h b/src/lib/dns/rdata/generic/minfo_14.h index f93f619ff0..f7afcc5f13 100644 --- a/src/lib/dns/rdata/generic/minfo_14.h +++ b/src/lib/dns/rdata/generic/minfo_14.h @@ -37,6 +37,11 @@ public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS + /// \brief Define the assignment operator. + /// + /// This method never throws an exception. + MINFO& operator=(const MINFO& source); + /// \brief Return the value of the rmailbox field. /// /// \exception std::bad_alloc If resource allocation for the returned diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc index b7009689a2..0956161243 100644 --- a/src/lib/dns/tests/rdata_minfo_unittest.cc +++ b/src/lib/dns/tests/rdata_minfo_unittest.cc @@ -31,21 +31,30 @@ using namespace isc::dns; using namespace isc::util; using namespace isc::dns::rdata; -namespace { -class Rdata_MINFO_Test : public RdataTest { - // there's nothing to specialize -}; - // minfo text const char* const minfo_txt = "rmailbox.example.com. emailbox.example.com."; +const char* const minfo_txt2 = "root.example.com. emailbox.example.com."; const char* const too_long_label = "01234567890123456789012345678901234567" "89012345678901234567890123"; -const generic::MINFO rdata_minfo((string(minfo_txt))); +namespace { +class Rdata_MINFO_Test : public RdataTest { + // there's nothing to specialize +public: + Rdata_MINFO_Test(): + rdata_minfo(string(minfo_txt)), rdata_minfo2(string(minfo_txt2)) {} + + const generic::MINFO rdata_minfo; + const generic::MINFO rdata_minfo2; +}; + TEST_F(Rdata_MINFO_Test, createFromText) { EXPECT_EQ(Name("rmailbox.example.com."), rdata_minfo.getRmailbox()); EXPECT_EQ(Name("emailbox.example.com."), rdata_minfo.getEmailbox()); + + EXPECT_EQ(Name("root.example.com."), rdata_minfo2.getRmailbox()); + EXPECT_EQ(Name("emailbox.example.com."), rdata_minfo2.getEmailbox()); } TEST_F(Rdata_MINFO_Test, badText) { @@ -57,60 +66,97 @@ TEST_F(Rdata_MINFO_Test, badText) { "example.com."), InvalidRdataText); // bad rmailbox name - EXPECT_THROW(generic::MINFO("root.example.com. emailbx.example.com." + + EXPECT_THROW(generic::MINFO("root.example.com. emailbox.example.com." + string(too_long_label)), TooLongLabel); // bad emailbox name EXPECT_THROW(generic::MINFO("root.example.com." + - string(too_long_label) + " emailbx.example.com."), + string(too_long_label) + " emailbox.example.com."), TooLongLabel); } TEST_F(Rdata_MINFO_Test, createFromWire) { - // compressed emailbx name + // uncompressed names EXPECT_EQ(0, rdata_minfo.compare( *rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), - "rdata_minfo_fromWire"))); - // compressed rmailbx and emailbx name + "rdata_minfo_fromWire1.wire"))); + // compressed names EXPECT_EQ(0, rdata_minfo.compare( - *rdataFactoryFromFile(RRType("MINFO"), RRClass::IN(), - "rdata_minfo_fromWire", 35))); + *rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire2.wire", 15))); // RDLENGTH is too short EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), - "rdata_minfo_fromWire", 41), + "rdata_minfo_fromWire3.wire"), InvalidRdataLength); // RDLENGTH is too long EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), - "rdata_minfo_fromWire", 47), + "rdata_minfo_fromWire4.wire"), InvalidRdataLength); - // incomplete name. the error should be detected in the name constructor - EXPECT_THROW(rdataFactoryFromFile(RRType("MINFO"), RRClass::IN(), - "rdata_minfo_fromWire", 53), + // bogus rmailbox name, the error should be detected in the name + // constructor + EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire5.wire"), + DNSMessageFORMERR); + // bogus emailbox name, the error should be detected in the name + // constructor + EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire6.wire"), DNSMessageFORMERR); } +TEST_F(Rdata_MINFO_Test, assignment) { + generic::MINFO copy((string(minfo_txt2))); + copy = rdata_minfo; + EXPECT_EQ(0, copy.compare(rdata_minfo)); + + // Check if the copied data is valid even after the original is deleted + generic::MINFO* copy2 = new generic::MINFO(rdata_minfo); + generic::MINFO copy3((string(minfo_txt2))); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_minfo)); + + // Self assignment + copy = copy; + EXPECT_EQ(0, copy.compare(rdata_minfo)); +} + TEST_F(Rdata_MINFO_Test, toWireBuffer) { - obuffer.skip(2); rdata_minfo.toWire(obuffer); vector data; - UnitTestUtil::readWireData("rdata_minfo_toWireUncompressed.wire", data); + UnitTestUtil::readWireData("rdata_minfo_toWireUncompressed1.wire", data); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, - static_cast(obuffer.getData()) + 2, - obuffer.getLength() - 2, &data[2], data.size() - 2); + static_cast(obuffer.getData()), + obuffer.getLength(), &data[0], data.size()); + + obuffer.clear(); + rdata_minfo2.toWire(obuffer); + vector data2; + UnitTestUtil::readWireData("rdata_minfo_toWireUncompressed2.wire", data2); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + static_cast(obuffer.getData()), + obuffer.getLength(), &data2[0], data2.size()); } TEST_F(Rdata_MINFO_Test, toWireRenderer) { - obuffer.skip(2); rdata_minfo.toWire(renderer); vector data; - UnitTestUtil::readWireData("rdata_minfo_toWire", data); + UnitTestUtil::readWireData("rdata_minfo_toWire1.wire", data); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, - static_cast(obuffer.getData()) + 2, - obuffer.getLength() - 2, &data[2], data.size() - 2); + static_cast(obuffer.getData()), + obuffer.getLength(), &data[0], data.size()); + renderer.clear(); + rdata_minfo2.toWire(renderer); + vector data2; + UnitTestUtil::readWireData("rdata_minfo_toWire2.wire", data2); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + static_cast(obuffer.getData()), + obuffer.getLength(), &data2[0], data2.size()); } TEST_F(Rdata_MINFO_Test, toText) { EXPECT_EQ(minfo_txt, rdata_minfo.toText()); + EXPECT_EQ(minfo_txt2, rdata_minfo2.toText()); } TEST_F(Rdata_MINFO_Test, compare) { diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 7e90b2cf9f..3dac8f2b4c 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -26,6 +26,12 @@ BUILT_SOURCES += rdata_nsec3_fromWire10.wire rdata_nsec3_fromWire11.wire BUILT_SOURCES += rdata_nsec3_fromWire12.wire rdata_nsec3_fromWire13.wire BUILT_SOURCES += rdata_nsec3_fromWire14.wire rdata_nsec3_fromWire15.wire BUILT_SOURCES += rdata_rrsig_fromWire2.wire +BUILT_SOURCES += rdata_minfo_fromWire1.wire rdata_minfo_fromWire2.wire +BUILT_SOURCES += rdata_minfo_fromWire3.wire rdata_minfo_fromWire4.wire +BUILT_SOURCES += rdata_minfo_fromWire5.wire rdata_minfo_fromWire6.wire +BUILT_SOURCES += rdata_minfo_toWire1.wire rdata_minfo_toWire2.wire +BUILT_SOURCES += rdata_minfo_toWireUncompressed1.wire +BUILT_SOURCES += rdata_minfo_toWireUncompressed2.wire BUILT_SOURCES += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire BUILT_SOURCES += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire BUILT_SOURCES += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire @@ -101,8 +107,12 @@ EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec EXTRA_DIST += rdata_srv_fromWire -EXTRA_DIST += rdata_minfo_fromWire rdata_minfo_toWireUncompressed.spec -EXTRA_DIST += rdata_minfo_toWireUncompressed.wire rdata_minfo_toWire +EXTRA_DIST += rdata_minfo_fromWire1.spec rdata_minfo_fromWire2.spec +EXTRA_DIST += rdata_minfo_fromWire3.spec rdata_minfo_fromWire4.spec +EXTRA_DIST += rdata_minfo_fromWire5.spec rdata_minfo_fromWire6.spec +EXTRA_DIST += rdata_minfo_toWire1.spec rdata_minfo_toWire2.spec +EXTRA_DIST += rdata_minfo_toWireUncompressed1.spec +EXTRA_DIST += rdata_minfo_toWireUncompressed2.spec EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire deleted file mode 100644 index d620931c2e..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire +++ /dev/null @@ -1,47 +0,0 @@ -# -# various kinds of MINFO RDATA stored in an input buffer -# -# Valid compressed RDATA for "(rmailbox.example.com. emailbox.example.com.)" -# RDLENGHT=32 bytes -# 0 1 - 00 21 -# RMAILBOX: non compressed -# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3(bytes) -#(8) r m a i l b o x (7) e x a m p l e (3) c o m . - 08 72 6d 61 69 6c 62 6f 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# EMAILBOX: compressed -# 4 5 6 7 8 9 30 1 2 3 4 -#(8) e m a i l b o x ptr=11 - 08 65 6d 61 69 6c 62 6f 78 c0 0b -# -# Both RMAILBOX and EMAILBOX compressed -# RDLENGHT=04 bytes -# 5 6 - 00 04 -# 7 8 9 40(bytes) -#ptr=02 ptr=24 - c0 02 c0 18 -# -# rdlength too short -# RDLENGHT=03 bytes -# 1 2 - 00 03 -# 3 4 5 6(bytes) -#ptr=02 ptr=24 - c0 02 c0 18 -# -# rdlength too long -# RDLENGHT=25 bytes -# 7 8 - 00 19 -# 9 50 1 2(bytes) -#ptr=02 ptr=24 - c0 02 c0 18 -# -# incomplete RMAILBOX NAME -# RDLENGHT=19 bytes -# 3 4 - 00 13 -# 5 6 7 8 9 60 1 2 3 4 5 6 7 8 9 60 1 2 3 (bytes) -#(8) r m a i l b o x (7) e x a m p l e (3) c - 08 72 6d 61 69 6c 62 6f 78 07 65 78 61 6d 70 6c 65 03 63 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec new file mode 100644 index 0000000000..2c43db0727 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec @@ -0,0 +1,3 @@ +[custom] +sections: minfo +[minfo] diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec new file mode 100644 index 0000000000..d781cac71d --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec @@ -0,0 +1,7 @@ +[custom] +sections: name:minfo +[name] +name: a.example.com. +[minfo] +rmailbox: rmailbox.ptr=02 +emailbox: emailbox.ptr=02 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec new file mode 100644 index 0000000000..a1d4b769d9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec @@ -0,0 +1,6 @@ +[custom] +sections: minfo +# rdlength too short +[minfo] +emailbox: emailbox.ptr=11 +rdlen: 3 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec new file mode 100644 index 0000000000..269a6ce7e2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec @@ -0,0 +1,6 @@ +[custom] +sections: minfo +# rdlength too long +[minfo] +emailbox: emailbox.ptr=11 +rdlen: 80 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec new file mode 100644 index 0000000000..3a888e3c20 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec @@ -0,0 +1,5 @@ +[custom] +sections: minfo +# bogus rmailbox name +[minfo] +rmailbox: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec new file mode 100644 index 0000000000..c75ed8e214 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec @@ -0,0 +1,5 @@ +[custom] +sections: minfo +# bogus emailbox name +[minfo] +emailbox: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire b/src/lib/dns/tests/testdata/rdata_minfo_toWire deleted file mode 100644 index c0d09d3968..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWire +++ /dev/null @@ -1,15 +0,0 @@ -# -# various kinds of MINFO RDATA stored in an input buffer -# -# Valid compressed RDATA for "(rmailbox.example.com. emailbox.example.com.)" -# RDLENGHT=32 bytes -# 0 1 - 00 21 -# RMAILBOX: non compressed -# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2 3(bytes) -#(8) r m a i l b o x (7) e x a m p l e (3) c o m . - 08 72 6d 61 69 6c 62 6f 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# EMAILBOX: compressed -# 4 5 6 7 8 9 30 1 2 3 4 -#(8) e m a i l b o x ptr=11 - 08 65 6d 61 69 6c 62 6f 78 c0 0b diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec new file mode 100644 index 0000000000..7b340a3904 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec @@ -0,0 +1,5 @@ +[custom] +sections: minfo +[minfo] +emailbox: emailbox.ptr=09 +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec new file mode 100644 index 0000000000..132f11839f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec @@ -0,0 +1,6 @@ +[custom] +sections: minfo +[minfo] +rmailbox: root.example.com. +emailbox: emailbox.ptr=05 +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.wire deleted file mode 100644 index bd51833142..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_toWireUncompressed.spec -### - -# MINFO RDATA, RDLEN=44 -002c -# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.example.com -08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec similarity index 89% rename from src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.spec rename to src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec index 209b30102a..d99a3813ca 100644 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed.spec +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec @@ -4,3 +4,4 @@ [custom] sections: minfo [minfo] +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec new file mode 100644 index 0000000000..1182c2b118 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec @@ -0,0 +1,8 @@ +# +# A simplest form of MINFO: all default parameters +# +[custom] +sections: minfo +[minfo] +rmailbox: root.example.com. +rdlen: -1 From 253d1fc351fffc8a0b1d325044854a2defdd7223 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Mon, 15 Aug 2011 17:27:16 +0800 Subject: [PATCH 512/974] [trac1130] Move some internal used functions to anonymouse namespace Test whether it reaches the end of input before skip left spaces --- src/lib/dns/rdata/generic/naptr_35.cc | 193 ++++++++++++---------- src/lib/dns/rdata/generic/naptr_35.h | 22 --- src/lib/dns/tests/rdata_naptr_unittest.cc | 2 + 3 files changed, 105 insertions(+), 112 deletions(-) diff --git a/src/lib/dns/rdata/generic/naptr_35.cc b/src/lib/dns/rdata/generic/naptr_35.cc index 86bab7ca1a..ede110ba53 100644 --- a/src/lib/dns/rdata/generic/naptr_35.cc +++ b/src/lib/dns/rdata/generic/naptr_35.cc @@ -32,6 +32,105 @@ using namespace isc::util; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE +namespace { +/// Skip the left whitespaces of the input string +/// +/// \param input_str The input string +/// \param input_iterator From which the skipping started +void +skipLeftSpaces(const std::string& input_str, + std::string::const_iterator& input_iterator) +{ + if (input_iterator >= input_str.end()) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format, field is missing."); + } + + if (!isspace(*input_iterator)) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format, fields are not separated by space."); + } + // Skip white spaces + while (input_iterator < input_str.end() && isspace(*input_iterator)) { + ++input_iterator; + } +} + +/// Get a from a string +/// +/// \param input_str The input string +/// \param input_iterator The iterator from which to start extracting, +/// the iterator will be updated to new position after the function +/// is returned +/// \return A std::string that contains the extracted +std::string +getNextCharacterString(const std::string& input_str, + std::string::const_iterator& input_iterator) +{ + string result; + + // If the input string only contains white-spaces, it is an invalid + // + if (input_iterator >= input_str.end()) { + isc_throw(InvalidRdataText, "Invalid NAPTR text format, \ + field is missing."); + } + + // Whether the is seperated with doulble quotes symbol(") + bool quotes_seperated = (*input_iterator == '"'); + + if (quotes_seperated) { + ++input_iterator; + } + + while(input_iterator < input_str.end()){ + if (quotes_seperated) { + // If the is seperated with quotes symbol and + // another quotes symbol is encountered, it is the end of the + // + if (*input_iterator == '"') { + ++input_iterator; + break; + } + } else if (*input_iterator == ' ') { + // If the is not seperated with quotes symbol, + // it is seperated with char + break; + } + + result.push_back(*input_iterator); + + ++input_iterator; + } + + if (result.size() > MAX_CHARSTRING_LEN) { + isc_throw(CharStringTooLong, "NAPTR is too long"); + } + + return (result); +} + +/// Get a from a input buffer +/// +/// \param buffer The input buffer +/// \param len The input buffer total length +/// \return A std::string that contains the extracted +std::string +getNextCharacterString(InputBuffer& buffer, size_t len) { + uint8_t str_len = buffer.readUint8(); + + size_t pos = buffer.getPosition(); + if (len - pos < str_len) { + isc_throw(InvalidRdataLength, "Invalid NAPTR string length"); + } + + uint8_t buf[MAX_CHARSTRING_LEN]; + buffer.readData(buf, str_len); + return (string(buf, buf + str_len)); +} + +} // Anonymouse namespace + NAPTR::NAPTR(InputBuffer& buffer, size_t len): replacement_(".") { @@ -62,31 +161,19 @@ NAPTR::NAPTR(const std::string& naptr_str): string::const_iterator input_iterator = naptr_str.begin() + iss.tellg(); - if (skipLeftSpaces(naptr_str, input_iterator) == 0) { - isc_throw(InvalidRdataText, - "Invalid NAPTR text format, fields are not separated by space"); - } + skipLeftSpaces(naptr_str, input_iterator); flags_ = getNextCharacterString(naptr_str, input_iterator); - if (skipLeftSpaces(naptr_str, input_iterator) == 0) { - isc_throw(InvalidRdataText, - "Invalid NAPTR text format, fields are not separated by space"); - } + skipLeftSpaces(naptr_str, input_iterator); services_ = getNextCharacterString(naptr_str, input_iterator); - if (skipLeftSpaces(naptr_str, input_iterator) == 0) { - isc_throw(InvalidRdataText, - "Invalid NAPTR text format, fields are not separated by space"); - } + skipLeftSpaces(naptr_str, input_iterator); regexp_ = getNextCharacterString(naptr_str, input_iterator); - if (skipLeftSpaces(naptr_str, input_iterator) == 0) { - isc_throw(InvalidRdataText, - "Invalid NAPTR text format, fields are not separated by space"); - } + skipLeftSpaces(naptr_str, input_iterator); if (input_iterator < naptr_str.end()) { string replacementStr(input_iterator, naptr_str.end()); @@ -223,79 +310,5 @@ NAPTR::getReplacement() const { return (replacement_); } -std::string -NAPTR::getNextCharacterString(const std::string& input_str, - std::string::const_iterator& input_iterator) -{ - string result; - - // If the input string only contains white-spaces, it is an invalid - // - if (input_iterator >= input_str.end()) { - isc_throw(InvalidRdataText, "Invalid NAPTR text format, \ - field is missing."); - } - - // Whether the is seperated with doulble quotes symbol(") - bool quotes_seperated = (*input_iterator == '"'); - - if (quotes_seperated) { - ++input_iterator; - } - - while(input_iterator < input_str.end()){ - if (quotes_seperated) { - // If the is seperated with quotes symbol and - // another quotes symbol is encountered, it is the end of the - // - if (*input_iterator == '"') { - ++input_iterator; - break; - } - } else if (*input_iterator == ' ') { - // If the is not seperated with quotes symbol, - // it is seperated with char - break; - } - - result.push_back(*input_iterator); - - ++input_iterator; - } - - if (result.size() > MAX_CHARSTRING_LEN) { - isc_throw(CharStringTooLong, "NAPTR is too long"); - } - - return (result); -} - -std::string -NAPTR::getNextCharacterString(InputBuffer& buffer, size_t len) { - uint8_t str_len = buffer.readUint8(); - - size_t pos = buffer.getPosition(); - if (len - pos < str_len) { - isc_throw(InvalidRdataLength, "Invalid NAPTR string length"); - } - - uint8_t buf[MAX_CHARSTRING_LEN]; - buffer.readData(buf, str_len); - return (string(buf, buf + str_len)); -} - -int -NAPTR::skipLeftSpaces(const std::string& input_str, - std::string::const_iterator& input_iterator) -{ - int space_count = 0; - // Skip white spaces - while (input_iterator < input_str.end() && isspace(*input_iterator)) { - ++input_iterator; - ++space_count; - } - return space_count; -} - // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/naptr_35.h b/src/lib/dns/rdata/generic/naptr_35.h index d6af35d53c..b3015ae2be 100644 --- a/src/lib/dns/rdata/generic/naptr_35.h +++ b/src/lib/dns/rdata/generic/naptr_35.h @@ -46,28 +46,6 @@ public: const std::string& getRegexp() const; const Name& getReplacement() const; private: - /// Get a from a string - /// - /// \param input_str The input string - /// \param input_iterator The iterator from which to start extracting, the iterator will be updated - /// to new position after the function is returned - /// \return A std::string that contains the extracted - std::string getNextCharacterString(const std::string& input_str, std::string::const_iterator& input_iterator); - - /// Get a from a input buffer - /// - /// \param buffer The input buffer - /// \param len The input buffer total length - /// \return A std::string that contains the extracted - std::string getNextCharacterString(util::InputBuffer& buffer, size_t len); - - /// Skip the left whitespaces of the input string - /// - /// \param input_str The input string - /// \param input_iterator From which the skipping started - /// \return How many spaces are skipped - int skipLeftSpaces(const std::string& input_str, std::string::const_iterator& input_iterator); - uint16_t order_; uint16_t preference_; std::string flags_; diff --git a/src/lib/dns/tests/rdata_naptr_unittest.cc b/src/lib/dns/tests/rdata_naptr_unittest.cc index 49a3f8d996..f905943ec5 100644 --- a/src/lib/dns/tests/rdata_naptr_unittest.cc +++ b/src/lib/dns/tests/rdata_naptr_unittest.cc @@ -104,6 +104,8 @@ TEST_F(Rdata_NAPTR_Test, badText) { InvalidRdataText); EXPECT_THROW(const NAPTR naptr("100 10 \"S\"\"SIP\" \"\" _sip._udp.example.com."), InvalidRdataText); + // Field cannot be missing + EXPECT_THROW(const NAPTR naptr("100 10 \"S\""), InvalidRdataText); // The cannot exceed 255 characters string naptr_str; From acb5dff4449286422f23a7d5867b3bd792c888e5 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Mon, 15 Aug 2011 07:19:24 -0400 Subject: [PATCH 513/974] [1140] TXT&SPF re-done to isolate the implementation details in txt_like.h from rdataclass.h using private pointers --- src/lib/dns/Makefile.am | 2 + src/lib/dns/rdata/generic/detail/txt_like.h | 28 +++---- src/lib/dns/rdata/generic/spf_99.cc | 87 +++++++++++++++++++++ src/lib/dns/rdata/generic/spf_99.h | 24 ++++-- src/lib/dns/rdata/generic/txt_16.cc | 87 +++++++++++++++++++++ src/lib/dns/rdata/generic/txt_16.h | 24 ++++-- 6 files changed, 220 insertions(+), 32 deletions(-) create mode 100644 src/lib/dns/rdata/generic/spf_99.cc create mode 100644 src/lib/dns/rdata/generic/txt_16.cc diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 56444bda4b..10a731fcef 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -50,7 +50,9 @@ EXTRA_DIST += rdata/generic/rrsig_46.cc EXTRA_DIST += rdata/generic/rrsig_46.h EXTRA_DIST += rdata/generic/soa_6.cc EXTRA_DIST += rdata/generic/soa_6.h +EXTRA_DIST += rdata/generic/spf_99.cc EXTRA_DIST += rdata/generic/spf_99.h +EXTRA_DIST += rdata/generic/txt_16.cc EXTRA_DIST += rdata/generic/txt_16.h EXTRA_DIST += rdata/hs_4/a_1.cc EXTRA_DIST += rdata/hs_4/a_1.h diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h index 0f18c8add4..f740cbc7fa 100644 --- a/src/lib/dns/rdata/generic/detail/txt_like.h +++ b/src/lib/dns/rdata/generic/detail/txt_like.h @@ -23,24 +23,22 @@ using namespace std; using namespace isc::util; -templateclass TXT_LIKE : public Rdata { +templateclass TXTLikeImpl { public: - TXT_LIKE(InputBuffer& buffer, size_t rdata_len) { + TXTLikeImpl(InputBuffer& buffer, size_t rdata_len) { if (rdata_len > MAX_RDLENGTH) { isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); } if (rdata_len == 0) { // note that this couldn't happen in the loop. - isc_throw(DNSMessageFORMERR, "Error in parsing " + - RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + isc_throw(DNSMessageFORMERR, "Error in parsing " << RRType(typeCode) << " RDATA: 0-length character string"); } do { const uint8_t len = buffer.readUint8(); if (rdata_len < len + 1) { - isc_throw(DNSMessageFORMERR, "Error in parsing " + - RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + isc_throw(DNSMessageFORMERR, "Error in parsing " << RRType(typeCode) << " RDATA: character string length is too large: " << static_cast(len)); } vector data(len + 1); @@ -52,7 +50,7 @@ public: } while (rdata_len > 0); } - explicit TXT_LIKE(const std::string& txtstr) { + explicit TXTLikeImpl(const std::string& txtstr) { // TBD: this is a simple, incomplete implementation that only supports // a single character-string. @@ -65,13 +63,13 @@ public: } if (length > MAX_CHARSTRING_LEN) { - isc_throw(CharStringTooLong, RRParamRegistry::getRegistry().codeToTypeText(typeCode) - + " RDATA construction from text: string length is too long: " << length); + isc_throw(CharStringTooLong, RRType(typeCode) << + " RDATA construction from text: string length is too long: " << length); } // TBD: right now, we don't support escaped characters if (txtstr.find('\\') != string::npos) { - isc_throw(InvalidRdataText, RRParamRegistry::getRegistry().codeToTypeText(typeCode) + + isc_throw(InvalidRdataText, RRType(typeCode) << " RDATA from text: escaped character is currently not supported: " << txtstr); } @@ -83,8 +81,8 @@ public: string_list_.push_back(data); } - TXT_LIKE(const TXT_LIKE& other) : - Rdata(), string_list_(other.string_list_) + TXTLikeImpl(const TXTLikeImpl& other) : + string_list_(other.string_list_) {} void @@ -129,16 +127,14 @@ public: } int - compare(const Rdata& other) const { - const TXT_LIKE& other_txt = dynamic_cast(other); - + compare(const TXTLikeImpl& other) const { // This implementation is not efficient. Revisit this (TBD). OutputBuffer this_buffer(0); toWire(this_buffer); size_t this_len = this_buffer.getLength(); OutputBuffer other_buffer(0); - other_txt.toWire(other_buffer); + other.toWire(other_buffer); const size_t other_len = other_buffer.getLength(); const size_t cmplen = min(this_len, other_len); diff --git a/src/lib/dns/rdata/generic/spf_99.cc b/src/lib/dns/rdata/generic/spf_99.cc new file mode 100644 index 0000000000..492de98551 --- /dev/null +++ b/src/lib/dns/rdata/generic/spf_99.cc @@ -0,0 +1,87 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +#include + +SPF& +SPF::operator=(const SPF& source) { + if (impl_ == source.impl_) { + return (*this); + } + + SPFImpl* newimpl = new SPFImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SPF::~SPF() { + delete impl_; +} + +SPF::SPF(InputBuffer& buffer, size_t rdata_len) : + impl_(new SPFImpl(buffer, rdata_len)) +{} + +SPF::SPF(const std::string& txtstr) : + impl_(new SPFImpl(txtstr)) +{} + +SPF::SPF(const SPF& other) : + Rdata(), impl_(new SPFImpl(*other.impl_)) +{} + +void +SPF::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +SPF::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +string +SPF::toText() const { + return (impl_->toText()); +} + +int +SPF::compare(const Rdata& other) const { + const SPF& other_txt = dynamic_cast(other); + + return (impl_->compare(*other_txt.impl_)); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/spf_99.h b/src/lib/dns/rdata/generic/spf_99.h index dfccec2b2c..c42bfac0c4 100644 --- a/src/lib/dns/rdata/generic/spf_99.h +++ b/src/lib/dns/rdata/generic/spf_99.h @@ -14,16 +14,13 @@ // BEGIN_HEADER_GUARD +#include + #include +#include -#include #include -#include -#include -#include -#include - // BEGIN_ISC_NAMESPACE // BEGIN_COMMON_DECLARATIONS @@ -31,9 +28,20 @@ // BEGIN_RDATA_NAMESPACE -#include +template class TXTLikeImpl; -typedef TXT_LIKE<99> SPF; +class SPF : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + SPF& operator=(const SPF& source); + ~SPF(); + +private: + typedef TXTLikeImpl SPFImpl; + SPFImpl* impl_; +}; // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc new file mode 100644 index 0000000000..418bc05fbc --- /dev/null +++ b/src/lib/dns/rdata/generic/txt_16.cc @@ -0,0 +1,87 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +#include + +TXT& +TXT::operator=(const TXT& source) { + if (impl_ == source.impl_) { + return (*this); + } + + TXTImpl* newimpl = new TXTImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TXT::~TXT() { + delete impl_; +} + +TXT::TXT(InputBuffer& buffer, size_t rdata_len) : + impl_(new TXTImpl(buffer, rdata_len)) +{} + +TXT::TXT(const std::string& txtstr) : + impl_(new TXTImpl(txtstr)) +{} + +TXT::TXT(const TXT& other) : + Rdata(), impl_(new TXTImpl(*other.impl_)) +{} + +void +TXT::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +TXT::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +string +TXT::toText() const { + return (impl_->toText()); +} + +int +TXT::compare(const Rdata& other) const { + const TXT& other_txt = dynamic_cast(other); + + return (impl_->compare(*other_txt.impl_)); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/txt_16.h b/src/lib/dns/rdata/generic/txt_16.h index 7075837e17..d99d69b75d 100644 --- a/src/lib/dns/rdata/generic/txt_16.h +++ b/src/lib/dns/rdata/generic/txt_16.h @@ -14,16 +14,13 @@ // BEGIN_HEADER_GUARD +#include + #include +#include -#include #include -#include -#include -#include -#include - // BEGIN_ISC_NAMESPACE // BEGIN_COMMON_DECLARATIONS @@ -31,9 +28,20 @@ // BEGIN_RDATA_NAMESPACE -#include +template class TXTLikeImpl; -typedef TXT_LIKE<16> TXT; +class TXT : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + TXT& operator=(const TXT& source); + ~TXT(); + +private: + typedef TXTLikeImpl TXTImpl; + TXTImpl* impl_; +}; // END_RDATA_NAMESPACE // END_ISC_NAMESPACE From 10cfb9ccd5b2eb489b14804e0ea9a73c80e697e6 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Mon, 15 Aug 2011 07:24:27 -0400 Subject: [PATCH 514/974] [1140] typeCode for SPF fixed --- src/lib/dns/rdata/generic/spf_99.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/rdata/generic/spf_99.h b/src/lib/dns/rdata/generic/spf_99.h index c42bfac0c4..956adb9d64 100644 --- a/src/lib/dns/rdata/generic/spf_99.h +++ b/src/lib/dns/rdata/generic/spf_99.h @@ -39,7 +39,7 @@ public: ~SPF(); private: - typedef TXTLikeImpl SPFImpl; + typedef TXTLikeImpl SPFImpl; SPFImpl* impl_; }; From a51d6b87331f0fc991b9926a9101e081668ebbcb Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 15 Aug 2011 14:50:45 +0200 Subject: [PATCH 515/974] [1067] some style fixes --- src/lib/datasrc/client.h | 1 + src/lib/datasrc/database.cc | 14 ++++++++------ src/lib/datasrc/database.h | 10 ++++++++-- src/lib/datasrc/iterator.h | 1 + src/lib/datasrc/memory_datasrc.cc | 7 ++++--- src/lib/datasrc/sqlite3_accessor.h | 1 + 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index 6a3936ad2c..c43092db12 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -150,6 +150,7 @@ public: /// \param name A domain name for which the search is performed. /// \return A \c FindResult object enclosing the search result (see above). virtual FindResult findZone(const isc::dns::Name& name) const = 0; + /// \brief Returns an iterator to the given zone /// /// This allows for traversing the whole zone. The returned object can diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 7a1aff881d..b17059f581 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -111,6 +111,7 @@ public: // Prepare data for the next time getData(); } + virtual isc::dns::ConstRRsetPtr getNextRRset() { if (!ready_) { isc_throw(isc::Unexpected, "Iterating past the zone end"); @@ -120,14 +121,14 @@ public: ready_ = false; return (ConstRRsetPtr()); } - string nameStr(name_), rtypeStr(rtype_), ttl(ttl_); - Name name(nameStr); - RRType rtype(rtypeStr); + string name_str(name_), rtype_str(rtype_), ttl(ttl_); + Name name(name_str); + RRType rtype(rtype_str); RRsetPtr rrset(new RRset(name, class_, rtype, RRTTL(ttl))); - while (data_ready_ && name_ == nameStr && rtypeStr == rtype_) { + while (data_ready_ && name_ == name_str && rtype_str == rtype_) { if (ttl_ != ttl) { - isc_throw(DataSourceError, "TTLs in rrset " + nameStr + "/" + - rtypeStr + " differ"); + isc_throw(DataSourceError, "TTLs in rrset " + name_str + "/" + + rtype_str + " differ"); } rrset->addRdata(rdata::createRdata(rtype, class_, rdata_)); getData(); @@ -144,6 +145,7 @@ private: ttl_ = data[2]; rdata_ = data[3]; } + // The context const DatabaseAccessor::IteratorContextPtr context_; // Class of the zone diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index ca4978f33d..c7205525ad 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -74,6 +74,7 @@ public: * an opaque handle. */ virtual std::pair getZone(const isc::dns::Name& name) const = 0; + /** * \brief This holds the internal context of ZoneIterator for databases * @@ -96,14 +97,15 @@ public: * Virtual destructor, so any descendand class is destroyed correctly. */ virtual ~IteratorContext() { } + /** * \brief Function to provide next resource record * * This function should provide data about the next resource record * from the iterated zone. The data are not converted yet. * - * The order of RRs is not strictly set, but the RRs for single RRset - * must not be interlieved with any other RRs (eg. RRsets must be + * \note The order of RRs is not strictly set, but the RRs for single + * RRset must not be interleaved with any other RRs (eg. RRsets must be * "together"). * * \param data The data are to be returned by this parameter. They are @@ -114,7 +116,9 @@ public: */ virtual bool getNext(std::string data[4]) = 0; }; + typedef boost::shared_ptr IteratorContextPtr; + /** * \brief Creates an iterator context for given zone. * @@ -250,6 +254,7 @@ public: * should use it as a ZoneFinder only. */ virtual FindResult findZone(const isc::dns::Name& name) const; + /** * \brief Get the zone iterator * @@ -268,6 +273,7 @@ public: * \return Shared pointer to the iterator (it will never be NULL) */ virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const; + private: /// \brief Our database. const boost::shared_ptr database_; diff --git a/src/lib/datasrc/iterator.h b/src/lib/datasrc/iterator.h index 3e3ce1455e..0102fcb9e5 100644 --- a/src/lib/datasrc/iterator.h +++ b/src/lib/datasrc/iterator.h @@ -38,6 +38,7 @@ public: * descendant is called. */ virtual ~ ZoneIterator() { } + /** * \brief Get next RRset from the zone. * diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 5f7fce3b40..d4fcaa5799 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -736,6 +736,7 @@ public: dom_iterator_ = node_->getData()->begin(); } } + virtual ConstRRsetPtr getNextRRset() { if (!ready_) { isc_throw(Unexpected, "Iterating past the zone end"); @@ -757,12 +758,12 @@ public: if (node_ == NULL) { // That's all, folks ready_ = false; - return ConstRRsetPtr(); + return (ConstRRsetPtr()); } // The iterator points to the next yet unused RRset now ConstRRsetPtr result(dom_iterator_->second); // This one is used, move it to the next time for next call - ++ dom_iterator_; + ++dom_iterator_; return (result); } @@ -789,7 +790,7 @@ InMemoryClient::getIterator(const Name& name) const { isc_throw(Unexpected, "The zone at " + name.toText() + " is not InMemoryZoneFinder"); } - return ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name)); + return (ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name))); } } // end of namespace datasrc diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index e108a54f0d..044017270e 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -90,6 +90,7 @@ public: * element and the zone id in the second if it was. */ virtual std::pair getZone(const isc::dns::Name& name) const; + /// \brief Implementation of DatabaseAbstraction::getIteratorContext virtual IteratorContextPtr getIteratorContext(const isc::dns::Name&, int id) const; From f3e53fe5cba59946ddcf24be423eece1ab596769 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Mon, 15 Aug 2011 09:04:38 -0400 Subject: [PATCH 516/974] [1138] DHCID implementation --- src/lib/dns/Makefile.am | 2 + src/lib/dns/rdata/in_1/dhcid_49.cc | 94 ++++++++++++++++++++++++++++++ src/lib/dns/rdata/in_1/dhcid_49.h | 45 ++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 src/lib/dns/rdata/in_1/dhcid_49.cc create mode 100644 src/lib/dns/rdata/in_1/dhcid_49.h diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 4a0173cb17..bc4b8a8e24 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -23,6 +23,8 @@ EXTRA_DIST += rdata/generic/cname_5.cc EXTRA_DIST += rdata/generic/cname_5.h EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h +EXTRA_DIST += rdata/generic/dhcid_49.cc +EXTRA_DIST += rdata/generic/dhcid_49.h EXTRA_DIST += rdata/generic/dname_39.cc EXTRA_DIST += rdata/generic/dname_39.h EXTRA_DIST += rdata/generic/dnskey_48.cc diff --git a/src/lib/dns/rdata/in_1/dhcid_49.cc b/src/lib/dns/rdata/in_1/dhcid_49.cc new file mode 100644 index 0000000000..ce3b6193eb --- /dev/null +++ b/src/lib/dns/rdata/in_1/dhcid_49.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +DHCID::DHCID(const string& dhcid_str) { + istringstream iss(dhcid_str); + stringbuf digestbuf; + + iss >> &digestbuf; + decodeHex(digestbuf.str(), digest_); + + // RFC4701 states DNS software should consider the RDATA section to be opaque, + // but there must be at least three bytes in the data: + // < 2 octets > Identifier type code + // < 1 octet > Digest type code + if (digest_.size() < 3) { + isc_throw(InvalidRdataLength, "DHCID length " << digest_.size() << + " too short, need at least 3 bytes"); + } +} + +DHCID::DHCID(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 3) { + isc_throw(InvalidRdataLength, "DHCID length " << rdata_len << + " too short, need at least 3 bytes"); + } + + digest_.resize(rdata_len); + buffer.readData(&digest_[0], rdata_len); +} + +DHCID::DHCID(const DHCID& other) : Rdata(), digest_(other.digest_) +{} + +void +DHCID::toWire(OutputBuffer& buffer) const { + buffer.writeData(&digest_[0], digest_.size()); +} + +void +DHCID::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&digest_[0], digest_.size()); +} + +string +DHCID::toText() const { + return (encodeHex(digest_)); +} + +int +DHCID::compare(const Rdata& other) const { + const DHCID& other_dhcid = dynamic_cast(other); + + size_t this_len = digest_.size(); + size_t other_len = other_dhcid.digest_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&digest_[0], &other_dhcid.digest_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/dhcid_49.h b/src/lib/dns/rdata/in_1/dhcid_49.h new file mode 100644 index 0000000000..9792bb6c48 --- /dev/null +++ b/src/lib/dns/rdata/in_1/dhcid_49.h @@ -0,0 +1,45 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_HEADER_GUARD + +#include +#include + +#include + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class DHCID : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + //We can use the default destructor. + //virtual ~DHCID() {} +private: + std::vector digest_; // opaque data at least 3 octets long +}; +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: From c4131b7a0c4a6d666a35847f8cce3d099b7a9949 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 15 Aug 2011 15:22:06 +0200 Subject: [PATCH 517/974] [1130] a couple of style fixes --- src/lib/dns/rdata/generic/naptr_35.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/dns/rdata/generic/naptr_35.cc b/src/lib/dns/rdata/generic/naptr_35.cc index ede110ba53..52683311c6 100644 --- a/src/lib/dns/rdata/generic/naptr_35.cc +++ b/src/lib/dns/rdata/generic/naptr_35.cc @@ -39,7 +39,7 @@ namespace { /// \param input_iterator From which the skipping started void skipLeftSpaces(const std::string& input_str, - std::string::const_iterator& input_iterator) + std::string::const_iterator& input_iterator) { if (input_iterator >= input_str.end()) { isc_throw(InvalidRdataText, @@ -48,7 +48,7 @@ skipLeftSpaces(const std::string& input_str, if (!isspace(*input_iterator)) { isc_throw(InvalidRdataText, - "Invalid NAPTR text format, fields are not separated by space."); + "Invalid NAPTR text format, fields are not separated by space."); } // Skip white spaces while (input_iterator < input_str.end() && isspace(*input_iterator)) { @@ -65,7 +65,7 @@ skipLeftSpaces(const std::string& input_str, /// \return A std::string that contains the extracted std::string getNextCharacterString(const std::string& input_str, - std::string::const_iterator& input_iterator) + std::string::const_iterator& input_iterator) { string result; @@ -76,15 +76,15 @@ getNextCharacterString(const std::string& input_str, field is missing."); } - // Whether the is seperated with doulble quotes symbol(") - bool quotes_seperated = (*input_iterator == '"'); + // Whether the is separated with double quotes (") + bool quotes_separated = (*input_iterator == '"'); - if (quotes_seperated) { + if (quotes_separated) { ++input_iterator; } while(input_iterator < input_str.end()){ - if (quotes_seperated) { + if (quotes_separated) { // If the is seperated with quotes symbol and // another quotes symbol is encountered, it is the end of the // @@ -129,7 +129,7 @@ getNextCharacterString(InputBuffer& buffer, size_t len) { return (string(buf, buf + str_len)); } -} // Anonymouse namespace +} // Anonymous namespace NAPTR::NAPTR(InputBuffer& buffer, size_t len): replacement_(".") From edbcbf0ab15f140b96efab5fae808b35e705cf67 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Mon, 15 Aug 2011 09:46:57 -0400 Subject: [PATCH 518/974] [1138] Makefile.am: dhcid location fixed --- src/lib/dns/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index bc4b8a8e24..5519c529a3 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -23,8 +23,6 @@ EXTRA_DIST += rdata/generic/cname_5.cc EXTRA_DIST += rdata/generic/cname_5.h EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h -EXTRA_DIST += rdata/generic/dhcid_49.cc -EXTRA_DIST += rdata/generic/dhcid_49.h EXTRA_DIST += rdata/generic/dname_39.cc EXTRA_DIST += rdata/generic/dname_39.h EXTRA_DIST += rdata/generic/dnskey_48.cc @@ -59,6 +57,8 @@ EXTRA_DIST += rdata/in_1/a_1.cc EXTRA_DIST += rdata/in_1/a_1.h EXTRA_DIST += rdata/in_1/aaaa_28.cc EXTRA_DIST += rdata/in_1/aaaa_28.h +EXTRA_DIST += rdata/in_1/dhcid_49.cc +EXTRA_DIST += rdata/in_1/dhcid_49.h EXTRA_DIST += rdata/in_1/srv_33.cc EXTRA_DIST += rdata/in_1/srv_33.h #EXTRA_DIST += rdata/template.cc From 6f5ca0bd47ff6a9b1670f38d6a68a1a7b1a01a5c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 15 Aug 2011 16:53:07 +0200 Subject: [PATCH 519/974] [1067] Lower the sleep time Both the TTL for items in cache and the sleep is lowered by 1 second, which should give the same result as before, but is faster. If I put the seconds I waited for the test together, I'd get half an hour already. --- src/lib/datasrc/tests/cache_unittest.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/tests/cache_unittest.cc b/src/lib/datasrc/tests/cache_unittest.cc index 96beae072a..1325f64f37 100644 --- a/src/lib/datasrc/tests/cache_unittest.cc +++ b/src/lib/datasrc/tests/cache_unittest.cc @@ -202,15 +202,15 @@ TEST_F(CacheTest, retrieveFail) { } TEST_F(CacheTest, expire) { - // Insert "foo" with a duration of 2 seconds; sleep 3. The + // Insert "foo" with a duration of 1 seconds; sleep 2. The // record should not be returned from the cache even though it's // at the top of the cache. RRsetPtr aaaa(new RRset(Name("foo"), RRClass::IN(), RRType::AAAA(), RRTTL(0))); aaaa->addRdata(in::AAAA("2001:db8:3:bb::5")); - cache.addPositive(aaaa, 0, 2); + cache.addPositive(aaaa, 0, 1); - sleep(3); + sleep(2); RRsetPtr r; uint32_t f; From fb441884baa9994093ed380aded84e707c3d34b5 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 15 Aug 2011 16:54:30 +0200 Subject: [PATCH 520/974] [1067] Rename getIteratorContext to getAllRecords This will be better for consistency, when we change the searchForRecords to return an iterator object, it will be called getRecords. --- src/lib/datasrc/database.cc | 2 +- src/lib/datasrc/database.h | 6 +++--- src/lib/datasrc/sqlite3_accessor.cc | 2 +- src/lib/datasrc/sqlite3_accessor.h | 4 ++-- src/lib/datasrc/tests/database_unittest.cc | 8 ++++---- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index e3eaf977ea..2fedbe9c16 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -393,7 +393,7 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const { } // Request the context DatabaseAccessor::IteratorContextPtr - context(database_->getIteratorContext(name, zone.second)); + context(database_->getAllRecords(name, zone.second)); // It must not return NULL, that's a bug of the implementation if (context == DatabaseAccessor::IteratorContextPtr()) { isc_throw(isc::Unexpected, "Iterator context null at " + diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 749b0ae9e3..1b2f2c0a6b 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -120,7 +120,7 @@ public: typedef boost::shared_ptr IteratorContextPtr; /** - * \brief Creates an iterator context for given zone. + * \brief Creates an iterator context for the whole zone. * * This should create a new iterator context to be used by * DatabaseConnection's ZoneIterator. It can be created based on the name @@ -136,8 +136,8 @@ public: * \param id The ID of the zone, returned from getZone(). * \return Newly created iterator context. Must not be NULL. */ - virtual IteratorContextPtr getIteratorContext(const isc::dns::Name& name, - int id) const + virtual IteratorContextPtr getAllRecords(const isc::dns::Name& name, + int id) const { /* * This is a compromise. We need to document the parameters in doxygen, diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 3b6ee92383..58ce68aa1e 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -375,7 +375,7 @@ private: }; DatabaseAccessor::IteratorContextPtr -SQLite3Database::getIteratorContext(const isc::dns::Name&, int id) const { +SQLite3Database::getAllRecords(const isc::dns::Name&, int id) const { return (IteratorContextPtr(new Context(shared_from_this(), id))); } diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 0c05779125..f75207a6da 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -91,8 +91,8 @@ public: */ virtual std::pair getZone(const isc::dns::Name& name) const; - /// \brief Implementation of DatabaseAbstraction::getIteratorContext - virtual IteratorContextPtr getIteratorContext(const isc::dns::Name&, + /// \brief Implementation of DatabaseAbstraction::getAllRecords + virtual IteratorContextPtr getAllRecords(const isc::dns::Name&, int id) const; /** * \brief Start a new search for the given name in the given zone. diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 2277d1d952..6f9c7d71ba 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -171,7 +171,7 @@ private: } }; public: - virtual IteratorContextPtr getIteratorContext(const Name&, int id) const { + virtual IteratorContextPtr getAllRecords(const Name&, int id) const { if (id == 42) { return (IteratorContextPtr(new MockIteratorContext())); } else if (id == 13) { @@ -391,10 +391,10 @@ private: } }; -// This tests the default getIteratorContext behaviour, throwing NotImplemented -TEST(DatabaseConnectionTest, getIteratorContext) { +// This tests the default getAllRecords behaviour, throwing NotImplemented +TEST(DatabaseConnectionTest, getAllRecords) { // The parameters don't matter - EXPECT_THROW(NopAccessor().getIteratorContext(Name("."), 1), + EXPECT_THROW(NopAccessor().getAllRecords(Name("."), 1), isc::NotImplemented); } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index a3f627ab82..454638fd7c 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -110,7 +110,7 @@ TEST_F(SQLite3Access, iterator) { // Get the iterator context DatabaseAccessor::IteratorContextPtr - context(db->getIteratorContext(Name("example2.com"), 1)); + context(db->getAllRecords(Name("example2.com"), 1)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); From 0f7dd030eb47912112b8774424a62c5561af16a1 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 15 Aug 2011 16:56:18 +0200 Subject: [PATCH 521/974] [1067] Rename Iterator to DatabaseIterator For consistency --- src/lib/datasrc/database.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 2fedbe9c16..14e5fa76e7 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -323,9 +323,9 @@ namespace { * Otherwise we just return what we have and keep the row as the one ahead * for next time. */ -class Iterator : public ZoneIterator { +class DatabaseIterator : public ZoneIterator { public: - Iterator(const DatabaseAccessor::IteratorContextPtr& context, + DatabaseIterator(const DatabaseAccessor::IteratorContextPtr& context, const RRClass& rrclass) : context_(context), class_(rrclass), @@ -404,7 +404,7 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const { // actual zone class from the connection, as the DatabaseClient // doesn't know it and the iterator needs it (so it wouldn't query // it each time) - return (ZoneIteratorPtr(new Iterator(context, RRClass::IN()))); + return (ZoneIteratorPtr(new DatabaseIterator(context, RRClass::IN()))); } } From 59c8ea50e972e7753c96f6bcf46fec48e694daa2 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 15 Aug 2011 17:01:16 +0200 Subject: [PATCH 522/974] [1067] Eliminate duplicate function It turned out two branches needed to solve the same problem, so both got a helper function. --- src/lib/datasrc/sqlite3_accessor.cc | 69 +++++++++++++---------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 58ce68aa1e..3b523cd1e9 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -329,13 +329,32 @@ SQLite3Database::getZone(const isc::dns::Name& name) const { } namespace { - -std::string -getstr(const unsigned char* str) { - return - (std::string(static_cast(static_cast(str)))); +// This helper function converts from the unsigned char* type (used by +// sqlite3) to char* (wanted by std::string). Technically these types +// might not be directly convertable +// In case sqlite3_column_text() returns NULL, we just make it an +// empty string. +// The sqlite3parameters value is only used to check the error code if +// ucp == NULL +const char* +convertToPlainChar(const unsigned char* ucp, + SQLite3Parameters* dbparameters) { + if (ucp == NULL) { + // The field can really be NULL, in which case we return an + // empty string, or sqlite may have run out of memory, in + // which case we raise an error + if (dbparameters != NULL && + sqlite3_errcode(dbparameters->db_) == SQLITE_NOMEM) { + isc_throw(DataSourceError, + "Sqlite3 backend encountered a memory allocation " + "error in sqlite3_column_text()"); + } else { + return (""); + } + } + const void* p = ucp; + return (static_cast(p)); } - } class SQLite3Database::Context : public DatabaseAccessor::IteratorContext { @@ -354,11 +373,14 @@ public: bool getNext(std::string data[4]) { // If there's another row, get it if (sqlite3_step(statement) == SQLITE_ROW) { - data[0] = getstr(sqlite3_column_text(statement, 0)); - data[1] = getstr(sqlite3_column_text(statement, 1)); + data[0] = convertToPlainChar(sqlite3_column_text(statement, 0), + database_->dbparameters_); + data[1] = convertToPlainChar(sqlite3_column_text(statement, 1), + database_->dbparameters_); data[2] = boost::lexical_cast( sqlite3_column_int(statement, 2)); - data[3] = getstr(sqlite3_column_text(statement, 3)); + data[3] = convertToPlainChar(sqlite3_column_text(statement, 3), + database_->dbparameters_); return (true); } return (false); @@ -396,35 +418,6 @@ SQLite3Database::searchForRecords(int zone_id, const std::string& name) { } } -namespace { -// This helper function converts from the unsigned char* type (used by -// sqlite3) to char* (wanted by std::string). Technically these types -// might not be directly convertable -// In case sqlite3_column_text() returns NULL, we just make it an -// empty string. -// The sqlite3parameters value is only used to check the error code if -// ucp == NULL -const char* -convertToPlainChar(const unsigned char* ucp, - SQLite3Parameters* dbparameters) { - if (ucp == NULL) { - // The field can really be NULL, in which case we return an - // empty string, or sqlite may have run out of memory, in - // which case we raise an error - if (dbparameters != NULL && - sqlite3_errcode(dbparameters->db_) == SQLITE_NOMEM) { - isc_throw(DataSourceError, - "Sqlite3 backend encountered a memory allocation " - "error in sqlite3_column_text()"); - } else { - return (""); - } - } - const void* p = ucp; - return (static_cast(p)); -} -} - bool SQLite3Database::getNextRecord(std::string columns[], size_t column_count) { if (column_count != COLUMN_COUNT) { From d9f4f26b0f2c73eddd07b2a4368ae1b238944b80 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 15 Aug 2011 17:06:45 +0200 Subject: [PATCH 523/974] [1067] More error checking for sqlite3_step --- src/lib/datasrc/sqlite3_accessor.cc | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 3b523cd1e9..e7aa5380a2 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -319,13 +319,18 @@ SQLite3Database::getZone(const isc::dns::Name& name) const { result = std::pair(true, sqlite3_column_int(dbparameters_-> q_zone_, 0)); - } else { + return (result); + } else if (rc == SQLITE_DONE) { result = std::pair(false, 0); + // Free resources + sqlite3_reset(dbparameters_->q_zone_); + return (result); } - // Free resources - sqlite3_reset(dbparameters_->q_zone_); - return (result); + isc_throw(DataSourceError, "Unexpected failure in sqlite3_step: " << + sqlite3_errmsg(dbparameters_->db_)); + // Compilers might not realize isc_throw always throws + return (std::pair(false, 0)); } namespace { @@ -372,7 +377,8 @@ public: } bool getNext(std::string data[4]) { // If there's another row, get it - if (sqlite3_step(statement) == SQLITE_ROW) { + int rc(sqlite3_step(statement)); + if (rc == SQLITE_ROW) { data[0] = convertToPlainChar(sqlite3_column_text(statement, 0), database_->dbparameters_); data[1] = convertToPlainChar(sqlite3_column_text(statement, 1), @@ -382,6 +388,10 @@ public: data[3] = convertToPlainChar(sqlite3_column_text(statement, 3), database_->dbparameters_); return (true); + } else if (rc != SQLITE_DONE) { + isc_throw(DataSourceError, + "Unexpected failure in sqlite3_step: " << + sqlite3_errmsg(database_->dbparameters_->db_)); } return (false); } From 3ce7b09732207eac03998fa5e267672760e475c9 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 15 Aug 2011 17:24:29 +0200 Subject: [PATCH 524/974] [1063] a couple of style fixes --- src/lib/datasrc/database.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 6afd3dce85..287602ab1f 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -51,7 +51,7 @@ DatabaseClient::findZone(const Name& name) const { ZoneFinderPtr(new Finder(database_, zone.second, name)))); } - // Than super domains + // Then super domains // Start from 1, as 0 is covered above for (size_t i(1); i < name.getLabelCount(); ++i) { isc::dns::Name superdomain(name.split(i)); @@ -276,7 +276,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, if (result_rrset) { sig_store.appendSignatures(result_rrset); } - return std::pair(records_found, result_rrset); + return (std::pair(records_found, result_rrset)); } @@ -298,16 +298,17 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, try { // First, do we have any kind of delegation (NS/DNAME) here? Name origin(getOrigin()); - size_t originLabelCount(origin.getLabelCount()); - size_t currentLabelCount(name.getLabelCount()); + size_t origin_label_count(origin.getLabelCount()); + size_t current_label_count(name.getLabelCount()); // This is how many labels we remove to get origin - size_t removeLabels(currentLabelCount - originLabelCount); + size_t remove_labels(current_label_count - origin_label_count); + // Now go trough all superdomains from origin down - for (int i(removeLabels); i > 0; -- i) { + for (int i(remove_labels); i > 0; --i) { Name superdomain(name.split(i)); // Look if there's NS or DNAME (but ignore the NS in origin) found = getRRset(superdomain, NULL, false, true, - i != removeLabels); + i != remove_labels); if (found.second) { // We found something redirecting somewhere else // (it can be only NS or DNAME here) From b4a1bc9ba28398dbd5fdbe4ee4f118a2faf59efa Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Tue, 16 Aug 2011 10:57:17 +0800 Subject: [PATCH 525/974] [trac1114] implement afsdb rdata --- src/lib/dns/Makefile.am | 2 + src/lib/dns/rdata/generic/afsdb_18.cc | 168 ++++++++++++++ src/lib/dns/rdata/generic/afsdb_18.h | 74 ++++++ src/lib/dns/tests/Makefile.am | 1 + src/lib/dns/tests/rdata_afsdb_unittest.cc | 210 ++++++++++++++++++ src/lib/dns/tests/testdata/Makefile.am | 8 + .../tests/testdata/rdata_afsdb_fromWire1.spec | 3 + .../tests/testdata/rdata_afsdb_fromWire2.spec | 6 + .../tests/testdata/rdata_afsdb_fromWire3.spec | 4 + .../tests/testdata/rdata_afsdb_fromWire4.spec | 4 + .../tests/testdata/rdata_afsdb_fromWire5.spec | 4 + .../tests/testdata/rdata_afsdb_toWire1.spec | 4 + .../tests/testdata/rdata_afsdb_toWire2.spec | 8 + src/lib/util/python/gen_wiredata.py.in | 21 ++ 14 files changed, 517 insertions(+) create mode 100644 src/lib/dns/rdata/generic/afsdb_18.cc create mode 100644 src/lib/dns/rdata/generic/afsdb_18.h create mode 100644 src/lib/dns/tests/rdata_afsdb_unittest.cc create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 4a0173cb17..43737a9cb0 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -51,6 +51,8 @@ EXTRA_DIST += rdata/generic/soa_6.cc EXTRA_DIST += rdata/generic/soa_6.h EXTRA_DIST += rdata/generic/txt_16.cc EXTRA_DIST += rdata/generic/txt_16.h +EXTRA_DIST += rdata/generic/afsdb_18.cc +EXTRA_DIST += rdata/generic/afsdb_18.h EXTRA_DIST += rdata/hs_4/a_1.cc EXTRA_DIST += rdata/hs_4/a_1.h EXTRA_DIST += rdata/in_1/a_1.cc diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc new file mode 100644 index 0000000000..0aca23f133 --- /dev/null +++ b/src/lib/dns/rdata/generic/afsdb_18.cc @@ -0,0 +1,168 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace isc::util::str; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// \c afsdb_str must be formatted as follows: +/// \code +/// \endcode +/// where server name field must represent a valid domain name. +/// +/// An example of valid string is: +/// \code "1 server.example.com." \endcode +/// +/// Exceptions +/// +/// \exception InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \exception std::bad_alloc Memory allocation fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the string is invalid. +AFSDB::AFSDB(const std::string& afsdb_str) : + subtype_(0), server_(Name::ROOT_NAME()) +{ + istringstream iss(afsdb_str); + + try { + const uint32_t subtype = tokenToNum(getToken(iss)); + const Name servername(getToken(iss)); + string server; + + if (!iss.eof()) { + isc_throw(InvalidRdataText, "Unexpected input for AFSDB" + "RDATA: " << afsdb_str); + } + + subtype_ = subtype; + server_ = servername; + + } catch (const StringTokenError& ste) { + isc_throw(InvalidRdataText, "Invalid AFSDB text: " << + ste.what() << ": " << afsdb_str); + } +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \exception std::bad_alloc Memory allocation fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +AFSDB::AFSDB(InputBuffer& buffer, size_t) : + subtype_(buffer.readUint16()), server_(buffer) +{} + +/// \brief Copy constructor. +/// +/// \exception std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +AFSDB::AFSDB(const AFSDB& other) : + Rdata(), subtype_(other.subtype_), server_(other.server_) +{} + +AFSDB& +AFSDB::operator=(const AFSDB& source) { + subtype_ = source.subtype_; + server_ = source.server_; + + return (*this); +} + +/// \brief Convert the \c AFSDB to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c AFSDB(const std::string&))). +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c AFSDB object. +string +AFSDB::toText() const { + return (lexical_cast(subtype_) + " " + server_.toText()); +} + +/// \brief Render the \c AFSDB in the wire format without name compression. +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +AFSDB::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(subtype_); + server_.toWire(buffer); +} + +/// \brief Render the \c AFSDB in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, TYPE AFSDB is not "well-known", the server +/// field (domain name) will not be compressed. +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +AFSDB::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(subtype_); + renderer.writeName(server_, false); +} + +/// \brief Compare two instances of \c AFSDB RDATA. +/// +/// See documentation in \c Rdata. +int +AFSDB::compare(const Rdata& other) const { + const AFSDB& other_afsdb = dynamic_cast(other); + if (subtype_ < other_afsdb.subtype_) { + return (-1); + } else if (subtype_ > other_afsdb.subtype_) { + return (1); + } + + return (compareNames(server_, other_afsdb.server_)); +} + +const Name& +AFSDB::getServer() const { + return (server_); +} + +uint16_t +AFSDB::getSubtype() const { + return (subtype_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/afsdb_18.h b/src/lib/dns/rdata/generic/afsdb_18.h new file mode 100644 index 0000000000..4a4677502c --- /dev/null +++ b/src/lib/dns/rdata/generic/afsdb_18.h @@ -0,0 +1,74 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_HEADER_GUARD + +#include + +#include + +#include +#include + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +/// \brief \c rdata::AFSDB class represents the AFSDB RDATA as defined %in +/// RFC1183. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// AFSDB RDATA. +class AFSDB : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// This method never throws an exception. + AFSDB& operator=(const AFSDB& source); + /// + /// Specialized methods + /// + + /// \brief Return the value of the server field. + /// + /// \return A reference to a \c Name class object corresponding to the + /// internal server name. + /// + /// This method never throws an exception. + const Name& getServer() const; + + /// \brief Return the value of the subtype field. + /// + /// This method never throws an exception. + uint16_t getSubtype() const; + +private: + uint16_t subtype_; + Name server_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index bd6fbe2a6e..ab33a17b37 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -32,6 +32,7 @@ run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc run_unittests_SOURCES += rdata_txt_unittest.cc rdata_mx_unittest.cc run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc run_unittests_SOURCES += rdata_dname_unittest.cc +run_unittests_SOURCES += rdata_afsdb_unittest.cc run_unittests_SOURCES += rdata_opt_unittest.cc run_unittests_SOURCES += rdata_dnskey_unittest.cc run_unittests_SOURCES += rdata_ds_unittest.cc diff --git a/src/lib/dns/tests/rdata_afsdb_unittest.cc b/src/lib/dns/tests/rdata_afsdb_unittest.cc new file mode 100644 index 0000000000..7df8d83659 --- /dev/null +++ b/src/lib/dns/tests/rdata_afsdb_unittest.cc @@ -0,0 +1,210 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using isc::UnitTestUtil; +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +const char* const afsdb_text = "1 afsdb.example.com."; +const char* const afsdb_text2 = "0 root.example.com."; +const char* const too_long_label("012345678901234567890123456789" + "0123456789012345678901234567890123"); + +namespace { +class Rdata_AFSDB_Test : public RdataTest { +protected: + Rdata_AFSDB_Test() : + rdata_afsdb(string(afsdb_text)), rdata_afsdb2(string(afsdb_text2)) + {} + + const generic::AFSDB rdata_afsdb; + const generic::AFSDB rdata_afsdb2; + vector expected_wire; +}; + + +TEST_F(Rdata_AFSDB_Test, createFromText) { + EXPECT_EQ(1, rdata_afsdb.getSubtype()); + EXPECT_EQ(Name("afsdb.example.com."), rdata_afsdb.getServer()); + + EXPECT_EQ(0, rdata_afsdb2.getSubtype()); + EXPECT_EQ(Name("root.example.com."), rdata_afsdb2.getServer()); +} + +TEST_F(Rdata_AFSDB_Test, badText) { + // subtype is too large + EXPECT_THROW(const generic::AFSDB rdata_afsdb("99999999 afsdb.example.com."), + InvalidRdataText); + // incomplete text + EXPECT_THROW(const generic::AFSDB rdata_afsdb("10"), InvalidRdataText); + EXPECT_THROW(const generic::AFSDB rdata_afsdb("SPOON"), InvalidRdataText); + EXPECT_THROW(const generic::AFSDB rdata_afsdb("1root.example.com."), InvalidRdataText); + // number of fields (must be 2) is incorrect + EXPECT_THROW(const generic::AFSDB rdata_afsdb("10 afsdb. example.com."), + InvalidRdataText); + // bad name + EXPECT_THROW(const generic::AFSDB rdata_afsdb("1 afsdb.example.com." + + string(too_long_label)), TooLongLabel); +} + +TEST_F(Rdata_AFSDB_Test, assignment) { + generic::AFSDB copy((string(afsdb_text2))); + copy = rdata_afsdb; + EXPECT_EQ(0, copy.compare(rdata_afsdb)); + + // Check if the copied data is valid even after the original is deleted + generic::AFSDB* copy2 = new generic::AFSDB(rdata_afsdb); + generic::AFSDB copy3((string(afsdb_text2))); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_afsdb)); + + // Self assignment + copy = copy; + EXPECT_EQ(0, copy.compare(rdata_afsdb)); +} + +TEST_F(Rdata_AFSDB_Test, createFromWire) { + // uncompressed names + EXPECT_EQ(0, rdata_afsdb.compare( + *rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire1.wire"))); + // compressed name + EXPECT_EQ(0, rdata_afsdb.compare( + *rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire2.wire", 13))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire3.wire"), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire4.wire"), + InvalidRdataLength); + // bogus server name, the error should be detected in the name + // constructor + EXPECT_THROW(rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire5.wire"), + DNSMessageFORMERR); +} + +TEST_F(Rdata_AFSDB_Test, toWireBuffer) { + // construct actual data + rdata_afsdb.toWire(obuffer); + + // construct expected data + UnitTestUtil::readWireData("rdata_afsdb_toWire1.wire", expected_wire); + + // then compare them + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + &expected_wire[0], expected_wire.size()); + + // clear buffer for the next test + obuffer.clear(); + + // construct actual data + Name("example.com.").toWire(obuffer); + rdata_afsdb2.toWire(obuffer); + + // construct expected data + UnitTestUtil::readWireData("rdata_afsdb_toWire2.wire", expected_wire); + + // then compare them + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + obuffer.getData(), obuffer.getLength(), + &expected_wire[0], expected_wire.size()); +} + +TEST_F(Rdata_AFSDB_Test, toWireRenderer) { + // similar to toWireBuffer, but names in RDATA could be compressed due to + // preceding names. Actually they must not be compressed according to + // RFC3597, and this test checks that. + + // construct actual data + rdata_afsdb.toWire(renderer); + + // construct expected data + UnitTestUtil::readWireData("rdata_afsdb_toWire1.wire", expected_wire); + + // then compare them + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + renderer.getData(), renderer.getLength(), + &expected_wire[0], expected_wire.size()); + + // clear renderer for the next test + renderer.clear(); + + // construct actual data + Name("example.com.").toWire(obuffer); + rdata_afsdb2.toWire(renderer); + + // construct expected data + UnitTestUtil::readWireData("rdata_afsdb_toWire2.wire", expected_wire); + + // then compare them + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + renderer.getData(), renderer.getLength(), + &expected_wire[0], expected_wire.size()); +} + +TEST_F(Rdata_AFSDB_Test, toText) { + EXPECT_EQ(afsdb_text, rdata_afsdb.toText()); + EXPECT_EQ(afsdb_text2, rdata_afsdb2.toText()); +} + +TEST_F(Rdata_AFSDB_Test, compare) { + // check reflexivity + EXPECT_EQ(0, rdata_afsdb.compare(rdata_afsdb)); + + // name must be compared in case-insensitive manner + EXPECT_EQ(0, rdata_afsdb.compare(generic::AFSDB("1 " + "AFSDB.example.com."))); + + const generic::AFSDB small1("10 afsdb.example.com"); + const generic::AFSDB large1("65535 afsdb.example.com"); + const generic::AFSDB large2("256 afsdb.example.com"); + + // confirm these are compared as unsigned values + EXPECT_GT(0, rdata_afsdb.compare(large1)); + EXPECT_LT(0, large1.compare(rdata_afsdb)); + + // confirm these are compared in network byte order + EXPECT_GT(0, small1.compare(large2)); + EXPECT_LT(0, large2.compare(small1)); + + // another AFSDB whose server name is larger than that of rdata_afsdb. + const generic::AFSDB large3("256 zzzzz.example.com"); + EXPECT_GT(0, large2.compare(large3)); + EXPECT_LT(0, large3.compare(large2)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_afsdb.compare(*rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 743b5d2418..d93470eed2 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -30,6 +30,10 @@ BUILT_SOURCES += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire BUILT_SOURCES += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire BUILT_SOURCES += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire BUILT_SOURCES += rdata_rp_toWire1.wire rdata_rp_toWire2.wire +BUILT_SOURCES += rdata_afsdb_fromWire1.wire rdata_afsdb_fromWire2.wire +BUILT_SOURCES += rdata_afsdb_fromWire3.wire rdata_afsdb_fromWire4.wire +BUILT_SOURCES += rdata_afsdb_fromWire5.wire +BUILT_SOURCES += rdata_afsdb_toWire1.wire rdata_afsdb_toWire2.wire BUILT_SOURCES += rdata_soa_toWireUncompressed.wire BUILT_SOURCES += rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire BUILT_SOURCES += rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire @@ -99,6 +103,10 @@ EXTRA_DIST += rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec +EXTRA_DIST += rdata_afsdb_fromWire1.spec rdata_afsdb_fromWire2.spec +EXTRA_DIST += rdata_afsdb_fromWire3.spec rdata_afsdb_fromWire4.spec +EXTRA_DIST += rdata_afsdb_fromWire5.spec +EXTRA_DIST += rdata_afsdb_toWire1.spec rdata_afsdb_toWire2.spec EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec EXTRA_DIST += rdata_srv_fromWire EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec new file mode 100644 index 0000000000..f831313827 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec @@ -0,0 +1,3 @@ +[custom] +sections: afsdb +[afsdb] diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec new file mode 100644 index 0000000000..f33e768589 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec @@ -0,0 +1,6 @@ +[custom] +sections: name:afsdb +[name] +name: example.com +[afsdb] +server: afsdb.ptr=0 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec new file mode 100644 index 0000000000..993032f605 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec @@ -0,0 +1,4 @@ +[custom] +sections: afsdb +[afsdb] +rdlen: 3 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec new file mode 100644 index 0000000000..37abf134c5 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec @@ -0,0 +1,4 @@ +[custom] +sections: afsdb +[afsdb] +rdlen: 80 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec new file mode 100644 index 0000000000..0ea79dd173 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec @@ -0,0 +1,4 @@ +[custom] +sections: afsdb +[afsdb] +server: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec b/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec new file mode 100644 index 0000000000..19464589e1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec @@ -0,0 +1,4 @@ +[custom] +sections: afsdb +[afsdb] +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec b/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec new file mode 100644 index 0000000000..c80011a488 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec @@ -0,0 +1,8 @@ +[custom] +sections: name:afsdb +[name] +name: example.com. +[afsdb] +subtype: 0 +server: root.example.com +rdlen: -1 diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in index 8e1f0798bd..6a69c2915f 100755 --- a/src/lib/util/python/gen_wiredata.py.in +++ b/src/lib/util/python/gen_wiredata.py.in @@ -822,6 +822,27 @@ class RP(RR): f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text)) f.write('%s %s\n' % (mailbox_wire, text_wire)) +class AFSDB(RR): + '''Implements rendering AFSDB RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - subtype (16 bit int): The subtype field. + - server (string): The server field. + The string must be interpreted as a valid domain name. + ''' + subtype = 1 + server = 'afsdb.example.com' + def dump(self, f): + server_wire = encode_name(self.server) + if self.rdlen is None: + self.rdlen = 2 + len(server_wire) / 2 + else: + self.rdlen = int(self.rdlen) + self.dump_header(f, self.rdlen) + f.write('# SUBTYPE=%d SERVER=%s\n' % (self.subtype, self.server)) + f.write('%04x %s\n' % (self.subtype, server_wire)) + class NSECBASE(RR): '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for these RRs. The NSEC and NSEC3 classes will be inherited from this From 5de7909a21a077238567b64e489ed5345824b2a0 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 16 Aug 2011 14:19:01 +0900 Subject: [PATCH 526/974] [master] Revert trac930 because of failures on biuldbots: "[master] update the ChangeLog entry for trac928, trac929 and trac930" 004afad6ea3fba7c8dd7730428b50fd770daec66 "[trac930] revise the entry of ChangeLog for trac928, trac929 and trac930" f20be125d667bceea0d940fc5fabf87b2eef86cd "[trac930]" fcc707041d663b98c1992cdd1402cc183155d3c0 "[trac930]" da5d5926cb26ca8dbdae119c03687cd3415f6638 "[trac930] refactor unittests" 0314c7bb66b85775dea73c95463eed88e9e286c3 "[trac930] add comments about abstracts of the test scripts in their headers" b8cecbbd905c10d28bcb905def7160d9e406dac4 "[trac930] modify stats.py" 7a31e95e63013a298b449573cc5336bcd64a0419 "[trac930] modify b10-stats_test.py" e18a678b62d03729f065c40650d7183e2f260b22 "[trac930] remove tailing whitespaces." 1d1a87939a010bd16ed23cd817261e9a655bf98f "[trac930] raise StatsError including errors in the stats spec file" c6948a6df9aeedd3753bc4c5e3a553088cd98f63 "[trac930] rename the function name" db0371fc9e5c7a85ab524ab7bc0b8169b9ba0486 "[trac930] remove a unnecessary x bit from stats_httpd.py.in" e906efc3747f052128eef50bed0107a0d53546c8 "[trac930] modify logging" d86a9dceaddf5a2cee44170e6e677f492df5e0ea "[trac930] modify the update_modues function" 4c2732cbf0bb7384ed61ab3604855f143a0c6c5d "[trac930]" aaffb9c83c0fe59d9c7d590c5bea559ed8876269 "[trac930] remove unnecessary a white space" e8a22472e58bfc7df4a661d665152fe4d70454a6 "[trac930] add a test pattern which the set command with a non-existent item" 2c22d334a05ec1e77299a6c55252f1d1c33082af "[trac930] modify parse_spec function" 8a24b9066537caf373d0cfc11dca855eb6c3e4d9 "[trac930] fix conflicts with trac1021" 7275c59de54593d3baca81345226dda2d3a19c30 "[trac930] add changes because query counter names described in the specfile are changed." bcf37a11b08922d69d02fa2ea1b280b2fa2c21e0 "[trac930] add the logging when the validation of statistics data fails" a142fa6302e1e0ea2ad1c9faf59d6a70a53a6489 "[trac930] Add unittests to test sumitStatistics with the validation of statistics data and add mock ModuleSpec class" ae8748f77a0261623216b1a11f9d979f555fe892 "[trac930] Add prototypes of validator_typea and registerStatisticsValidator" d0d5a67123b8009e89e84515eee4f93b37ec8497 "[trac930]" a9a976d2a5871f1501018d697d3afd299ceec5da "[trac930] add the helper functions which are used around the registration of the function to validate the statistics data." df9a8f921f0d20bd70c519218335357297bffa7d "[trac930] add new messages into the message file of Auth and Boss" e95625332a20fb50afe43da2db0cab507efe8ebe "[trac930] add statistics validation for bob" 28cad73dff9dae43a38ad7dafbee406c690fb77c "[trac930]" 4de3a5bdf367d87247cb9138f8929ab4798f014e "[trac930] remove unneeded empty TODO comments" aa108cc824539a1d32a4aa2f46f9e58171074a9e "[trac930] add new entry for #928-#930" 691328d91b4c4d15ace467ca47a3c987a9fb52b9 "[trac930] refurbish the unittests for new stats module, new stats httpd module" c06463cf96ea7401325a208af8ba457e661d1cec "[trac930] modify Stats" c074f6e0b72c3facf6b325b17dea1ca13a2788cc "[trac930]" daa1d6dd07292142d3dec5928583b0ab1da89adf "[trac930] update spec file of stats module" e7b4337aeaa760947e8e7906e64077ad7aaadc66 "[trac930] update argument name and argument format of set command in auth module and boss module" 0b235902f38d611606d44661506f32baf266fdda "[trac930] remove description about removing statistics data by stats module" c19a295eb4125b4d2a391de65972271002412258 "[trac930] add a column "Owner" in the table tag" 9261da8717a433cf20218af08d3642fbeffb7d4b "[trac930] remove descriptions about "stats-schema.spec" and add description about new" d4078d52343247b07c47370b497927a3a47a4f9a "[trac930] add utilities and mock-up modules for unittests of" 1aa728ddf691657611680385c920e3a7bd5fee12 "[trac930] remove unneeded mockups, fake modules and dummy data" 1768e822df82943f075ebed023b72d225b3b0216 "[trac930] remove unneeded specfile "stats-schema.spec"" 326885a3f98c49a848a67dc48db693b8bcc7b508 --- ChangeLog | 7 - configure.ac | 7 + doc/guide/bind10-guide.html | 30 +- doc/guide/bind10-guide.xml | 30 +- src/bin/auth/auth_messages.mes | 3 - src/bin/auth/auth_srv.cc | 24 - src/bin/auth/statistics.cc | 32 +- src/bin/auth/statistics.h | 20 - src/bin/auth/tests/statistics_unittest.cc | 74 +- src/bin/bind10/bind10_messages.mes | 4 - src/bin/bind10/bind10_src.py.in | 25 +- src/bin/bind10/tests/bind10_test.py.in | 23 +- src/bin/stats/Makefile.am | 4 +- src/bin/stats/b10-stats-httpd.8 | 6 +- src/bin/stats/b10-stats-httpd.xml | 10 +- src/bin/stats/b10-stats.8 | 4 + src/bin/stats/b10-stats.xml | 6 + src/bin/stats/stats-httpd-xsl.tpl | 1 - src/bin/stats/stats-schema.spec | 86 ++ src/bin/stats/stats.py.in | 600 ++++----- src/bin/stats/stats.spec | 75 +- src/bin/stats/stats_httpd.py.in | 230 ++-- src/bin/stats/stats_messages.mes | 21 +- src/bin/stats/tests/Makefile.am | 10 +- src/bin/stats/tests/b10-stats-httpd_test.py | 663 ++++------ src/bin/stats/tests/b10-stats_test.py | 1159 +++++++++--------- src/bin/stats/tests/fake_select.py | 43 + src/bin/stats/tests/fake_socket.py | 70 ++ src/bin/stats/tests/fake_time.py | 47 + src/bin/stats/tests/http/Makefile.am | 6 + src/bin/stats/tests/http/__init__.py | 0 src/bin/stats/tests/http/server.py | 96 ++ src/bin/stats/tests/isc/Makefile.am | 8 + src/bin/stats/tests/isc/__init__.py | 0 src/bin/stats/tests/isc/cc/Makefile.am | 7 + src/bin/stats/tests/isc/cc/__init__.py | 1 + src/bin/stats/tests/isc/cc/session.py | 148 +++ src/bin/stats/tests/isc/config/Makefile.am | 7 + src/bin/stats/tests/isc/config/__init__.py | 1 + src/bin/stats/tests/isc/config/ccsession.py | 249 ++++ src/bin/stats/tests/isc/log/Makefile.am | 7 + src/bin/stats/tests/isc/log/__init__.py | 33 + src/bin/stats/tests/isc/util/Makefile.am | 7 + src/bin/stats/tests/isc/util/__init__.py | 0 src/bin/stats/tests/isc/util/process.py | 21 + src/bin/stats/tests/test_utils.py | 291 ----- src/bin/stats/tests/testdata/Makefile.am | 1 + src/bin/stats/tests/testdata/stats_test.spec | 19 + src/bin/tests/Makefile.am | 2 +- tests/system/bindctl/tests.sh | 16 +- 50 files changed, 2265 insertions(+), 1969 deletions(-) create mode 100644 src/bin/stats/stats-schema.spec mode change 100644 => 100755 src/bin/stats/stats_httpd.py.in create mode 100644 src/bin/stats/tests/fake_select.py create mode 100644 src/bin/stats/tests/fake_socket.py create mode 100644 src/bin/stats/tests/fake_time.py create mode 100644 src/bin/stats/tests/http/Makefile.am create mode 100644 src/bin/stats/tests/http/__init__.py create mode 100644 src/bin/stats/tests/http/server.py create mode 100644 src/bin/stats/tests/isc/Makefile.am create mode 100644 src/bin/stats/tests/isc/__init__.py create mode 100644 src/bin/stats/tests/isc/cc/Makefile.am create mode 100644 src/bin/stats/tests/isc/cc/__init__.py create mode 100644 src/bin/stats/tests/isc/cc/session.py create mode 100644 src/bin/stats/tests/isc/config/Makefile.am create mode 100644 src/bin/stats/tests/isc/config/__init__.py create mode 100644 src/bin/stats/tests/isc/config/ccsession.py create mode 100644 src/bin/stats/tests/isc/log/Makefile.am create mode 100644 src/bin/stats/tests/isc/log/__init__.py create mode 100644 src/bin/stats/tests/isc/util/Makefile.am create mode 100644 src/bin/stats/tests/isc/util/__init__.py create mode 100644 src/bin/stats/tests/isc/util/process.py delete mode 100644 src/bin/stats/tests/test_utils.py create mode 100644 src/bin/stats/tests/testdata/Makefile.am create mode 100644 src/bin/stats/tests/testdata/stats_test.spec diff --git a/ChangeLog b/ChangeLog index 94b9a22f48..56bf8e97d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,3 @@ -279. [func] naokikambe - Statistics items are specified by each module's spec file. - Stats module can read these through the config manager. Stats - module and stats httpd report statistics data and statistics - schema by each module via both bindctl and HTTP/XML. - (Trac #928,#929,#930, git f20be125d667bceea0d940fc5fabf87b2eef86cd) - 278. [doc] jelte Add logging configuration documentation to the guide. (Trac #1011, git TODO) diff --git a/configure.ac b/configure.ac index ee990eb412..6e129b6093 100644 --- a/configure.ac +++ b/configure.ac @@ -801,6 +801,13 @@ AC_CONFIG_FILES([Makefile src/bin/zonemgr/tests/Makefile src/bin/stats/Makefile src/bin/stats/tests/Makefile + src/bin/stats/tests/isc/Makefile + src/bin/stats/tests/isc/cc/Makefile + src/bin/stats/tests/isc/config/Makefile + src/bin/stats/tests/isc/util/Makefile + src/bin/stats/tests/isc/log/Makefile + src/bin/stats/tests/testdata/Makefile + src/bin/stats/tests/http/Makefile src/bin/usermgr/Makefile src/bin/tests/Makefile src/lib/Makefile diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html index 4415d42550..5754cf001e 100644 --- a/doc/guide/bind10-guide.html +++ b/doc/guide/bind10-guide.html @@ -664,30 +664,24 @@ This may be a temporary setting until then.

- This stats daemon provides commands to identify if it is - running, show specified or all statistics data, show specified - or all statistics data schema, and set specified statistics - data. + This stats daemon provides commands to identify if it is running, + show specified or all statistics data, set values, remove data, + and reset data. For example, using bindctl:

 > Stats show
 {
-    "Auth": {
-        "queries.tcp": 1749,
-        "queries.udp": 867868
-    },
-    "Boss": {
-        "boot_time": "2011-01-20T16:59:03Z"
-    },
-    "Stats": {
-        "boot_time": "2011-01-20T16:59:05Z",
-        "last_update_time": "2011-01-20T17:04:05Z",
-        "lname": "4d3869d9_a@jreed.example.net",
-        "report_time": "2011-01-20T17:04:06Z",
-        "timestamp": 1295543046.823504
-    }
+    "auth.queries.tcp": 1749,
+    "auth.queries.udp": 867868,
+    "bind10.boot_time": "2011-01-20T16:59:03Z",
+    "report_time": "2011-01-20T17:04:06Z",
+    "stats.boot_time": "2011-01-20T16:59:05Z",
+    "stats.last_update_time": "2011-01-20T17:04:05Z",
+    "stats.lname": "4d3869d9_a@jreed.example.net",
+    "stats.start_time": "2011-01-20T16:59:05Z",
+    "stats.timestamp": 1295543046.823504
 }
        

Chapter 14. Logging

diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 297400cca6..ef66f3d3fb 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1453,30 +1453,24 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - This stats daemon provides commands to identify if it is - running, show specified or all statistics data, show specified - or all statistics data schema, and set specified statistics - data. + This stats daemon provides commands to identify if it is running, + show specified or all statistics data, set values, remove data, + and reset data. For example, using bindctl: > Stats show { - "Auth": { - "queries.tcp": 1749, - "queries.udp": 867868 - }, - "Boss": { - "boot_time": "2011-01-20T16:59:03Z" - }, - "Stats": { - "boot_time": "2011-01-20T16:59:05Z", - "last_update_time": "2011-01-20T17:04:05Z", - "lname": "4d3869d9_a@jreed.example.net", - "report_time": "2011-01-20T17:04:06Z", - "timestamp": 1295543046.823504 - } + "auth.queries.tcp": 1749, + "auth.queries.udp": 867868, + "bind10.boot_time": "2011-01-20T16:59:03Z", + "report_time": "2011-01-20T17:04:06Z", + "stats.boot_time": "2011-01-20T16:59:05Z", + "stats.last_update_time": "2011-01-20T17:04:05Z", + "stats.lname": "4d3869d9_a@jreed.example.net", + "stats.start_time": "2011-01-20T16:59:05Z", + "stats.timestamp": 1295543046.823504 } diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes index 1ffa6871ea..9f04b76264 100644 --- a/src/bin/auth/auth_messages.mes +++ b/src/bin/auth/auth_messages.mes @@ -257,7 +257,4 @@ request. The zone manager component has been informed of the request, but has returned an error response (which is included in the message). The NOTIFY request will not be honored. -% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified -An error was encountered when the authoritiative server specified -statistics data which is invalid for the auth specification file. diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index c9dac88e99..5a3144283a 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -125,10 +125,6 @@ public: /// The TSIG keyring const shared_ptr* keyring_; - - /// Bind the ModuleSpec object in config_session_ with - /// isc:config::ModuleSpec::validateStatistics. - void registerStatisticsValidator(); private: std::string db_file_; @@ -143,9 +139,6 @@ private: /// Increment query counter void incCounter(const int protocol); - - // validateStatistics - bool validateStatistics(isc::data::ConstElementPtr data) const; }; AuthSrvImpl::AuthSrvImpl(const bool use_cache, @@ -324,7 +317,6 @@ AuthSrv::setXfrinSession(AbstractSession* xfrin_session) { void AuthSrv::setConfigSession(ModuleCCSession* config_session) { impl_->config_session_ = config_session; - impl_->registerStatisticsValidator(); } void @@ -678,22 +670,6 @@ AuthSrvImpl::incCounter(const int protocol) { } } -void -AuthSrvImpl::registerStatisticsValidator() { - counters_.registerStatisticsValidator( - boost::bind(&AuthSrvImpl::validateStatistics, this, _1)); -} - -bool -AuthSrvImpl::validateStatistics(isc::data::ConstElementPtr data) const { - if (config_session_ == NULL) { - return (false); - } - return ( - config_session_->getModuleSpec().validateStatistics( - data, true)); -} - ConstElementPtr AuthSrvImpl::setDbFile(ConstElementPtr config) { ConstElementPtr answer = isc::config::createAnswer(); diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc index e62719f7e2..76e50074fc 100644 --- a/src/bin/auth/statistics.cc +++ b/src/bin/auth/statistics.cc @@ -37,14 +37,11 @@ public: void inc(const AuthCounters::CounterType type); bool submitStatistics() const; void setStatisticsSession(isc::cc::AbstractSession* statistics_session); - void registerStatisticsValidator - (AuthCounters::validator_type validator); // Currently for testing purpose only uint64_t getCounter(const AuthCounters::CounterType type) const; private: std::vector counters_; isc::cc::AbstractSession* statistics_session_; - AuthCounters::validator_type validator_; }; AuthCountersImpl::AuthCountersImpl() : @@ -70,25 +67,16 @@ AuthCountersImpl::submitStatistics() const { } std::stringstream statistics_string; statistics_string << "{\"command\": [\"set\"," - << "{ \"owner\": \"Auth\"," - << " \"data\":" - << "{ \"queries.udp\": " + << "{ \"stats_data\": " + << "{ \"auth.queries.udp\": " << counters_.at(AuthCounters::COUNTER_UDP_QUERY) - << ", \"queries.tcp\": " + << ", \"auth.queries.tcp\": " << counters_.at(AuthCounters::COUNTER_TCP_QUERY) << " }" << "}" << "]}"; isc::data::ConstElementPtr statistics_element = isc::data::Element::fromJSON(statistics_string); - // validate the statistics data before send - if (validator_) { - if (!validator_( - statistics_element->get("command")->get(1)->get("data"))) { - LOG_ERROR(auth_logger, AUTH_INVALID_STATISTICS_DATA); - return (false); - } - } try { // group_{send,recv}msg() can throw an exception when encountering // an error, and group_recvmsg() will throw an exception on timeout. @@ -117,13 +105,6 @@ AuthCountersImpl::setStatisticsSession statistics_session_ = statistics_session; } -void -AuthCountersImpl::registerStatisticsValidator - (AuthCounters::validator_type validator) -{ - validator_ = validator; -} - // Currently for testing purpose only uint64_t AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const { @@ -158,10 +139,3 @@ uint64_t AuthCounters::getCounter(const AuthCounters::CounterType type) const { return (impl_->getCounter(type)); } - -void -AuthCounters::registerStatisticsValidator - (AuthCounters::validator_type validator) const -{ - return (impl_->registerStatisticsValidator(validator)); -} diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h index c930414c65..5bf643656d 100644 --- a/src/bin/auth/statistics.h +++ b/src/bin/auth/statistics.h @@ -131,26 +131,6 @@ public: /// \return the value of the counter specified by \a type. /// uint64_t getCounter(const AuthCounters::CounterType type) const; - - /// \brief A type of validation function for the specification in - /// isc::config::ModuleSpec. - /// - /// This type might be useful for not only statistics - /// specificatoin but also for config_data specification and for - /// commnad. - /// - typedef boost::function - validator_type; - - /// \brief Register a function type of the statistics validation - /// function for AuthCounters. - /// - /// This method never throws an exception. - /// - /// \param validator A function type of the validation of - /// statistics specification. - /// - void registerStatisticsValidator(AuthCounters::validator_type validator) const; }; #endif // __STATISTICS_H diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc index 98e573b495..9a3dded837 100644 --- a/src/bin/auth/tests/statistics_unittest.cc +++ b/src/bin/auth/tests/statistics_unittest.cc @@ -16,8 +16,6 @@ #include -#include - #include #include @@ -78,13 +76,6 @@ protected: } MockSession statistics_session_; AuthCounters counters; - // no need to be inherited from the original class here. - class MockModuleSpec { - public: - bool validateStatistics(ConstElementPtr, const bool valid) const - { return (valid); } - }; - MockModuleSpec module_spec_; }; void @@ -190,7 +181,7 @@ TEST_F(AuthCountersTest, submitStatisticsWithException) { statistics_session_.setThrowSessionTimeout(false); } -TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) { +TEST_F(AuthCountersTest, submitStatistics) { // Submit statistics data. // Validate if it submits correct data. @@ -210,69 +201,12 @@ TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) { // Command is "set". EXPECT_EQ("set", statistics_session_.sent_msg->get("command") ->get(0)->stringValue()); - EXPECT_EQ("Auth", statistics_session_.sent_msg->get("command") - ->get(1)->get("owner")->stringValue()); ConstElementPtr statistics_data = statistics_session_.sent_msg ->get("command")->get(1) - ->get("data"); + ->get("stats_data"); // UDP query counter is 2 and TCP query counter is 1. - EXPECT_EQ(2, statistics_data->get("queries.udp")->intValue()); - EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue()); + EXPECT_EQ(2, statistics_data->get("auth.queries.udp")->intValue()); + EXPECT_EQ(1, statistics_data->get("auth.queries.tcp")->intValue()); } -TEST_F(AuthCountersTest, submitStatisticsWithValidator) { - - //a validator for the unittest - AuthCounters::validator_type validator; - ConstElementPtr el; - - // Submit statistics data with correct statistics validator. - validator = boost::bind( - &AuthCountersTest::MockModuleSpec::validateStatistics, - &module_spec_, _1, true); - - EXPECT_TRUE(validator(el)); - - // register validator to AuthCounters - counters.registerStatisticsValidator(validator); - - // Counters should be initialized to 0. - EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY)); - EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY)); - - // UDP query counter is set to 2. - counters.inc(AuthCounters::COUNTER_UDP_QUERY); - counters.inc(AuthCounters::COUNTER_UDP_QUERY); - // TCP query counter is set to 1. - counters.inc(AuthCounters::COUNTER_TCP_QUERY); - - // checks the value returned by submitStatistics - EXPECT_TRUE(counters.submitStatistics()); - - // Destination is "Stats". - EXPECT_EQ("Stats", statistics_session_.msg_destination); - // Command is "set". - EXPECT_EQ("set", statistics_session_.sent_msg->get("command") - ->get(0)->stringValue()); - EXPECT_EQ("Auth", statistics_session_.sent_msg->get("command") - ->get(1)->get("owner")->stringValue()); - ConstElementPtr statistics_data = statistics_session_.sent_msg - ->get("command")->get(1) - ->get("data"); - // UDP query counter is 2 and TCP query counter is 1. - EXPECT_EQ(2, statistics_data->get("queries.udp")->intValue()); - EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue()); - - // Submit statistics data with incorrect statistics validator. - validator = boost::bind( - &AuthCountersTest::MockModuleSpec::validateStatistics, - &module_spec_, _1, false); - - EXPECT_FALSE(validator(el)); - - counters.registerStatisticsValidator(validator); - - // checks the value returned by submitStatistics - EXPECT_FALSE(counters.submitStatistics()); -} } diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 4debcdb3ec..4bac069098 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -198,7 +198,3 @@ the message channel. % BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited An unknown child process has exited. The PID is printed, but no further action will be taken by the boss process. - -% BIND10_INVALID_STATISTICS_DATA invalid specification of statistics data specified -An error was encountered when the boss module specified -statistics data which is invalid for the boss specification file. diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 3deba6172b..b497f7c922 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -85,7 +85,7 @@ isc.util.process.rename(sys.argv[0]) # number, and the overall BIND 10 version number (set in configure.ac). VERSION = "bind10 20110223 (BIND 10 @PACKAGE_VERSION@)" -# This is for boot_time of Boss +# This is for bind10.boottime of stats module _BASETIME = time.gmtime() class RestartSchedule: @@ -318,22 +318,13 @@ class BoB: answer = isc.config.ccsession.create_answer(0) elif command == "sendstats": # send statistics data to the stats daemon immediately - statistics_data = { - 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) - } - valid = self.ccs.get_module_spec().validate_statistics( - True, statistics_data) - if valid: - cmd = isc.config.ccsession.create_command( - 'set', { "owner": "Boss", - "data": statistics_data }) - seq = self.cc_session.group_sendmsg(cmd, 'Stats') - self.cc_session.group_recvmsg(True, seq) - answer = isc.config.ccsession.create_answer(0) - else: - logger.fatal(BIND10_INVALID_STATISTICS_DATA); - answer = isc.config.ccsession.create_answer( - 1, "specified statistics data is invalid") + cmd = isc.config.ccsession.create_command( + 'set', { "stats_data": { + 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + }}) + seq = self.cc_session.group_sendmsg(cmd, 'Stats') + self.cc_session.group_recvmsg(True, seq) + answer = isc.config.ccsession.create_answer(0) elif command == "ping": answer = isc.config.ccsession.create_answer(0, "pong") elif command == "show_processes": diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index af7b6f49ef..077190c865 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -137,27 +137,9 @@ class TestBoB(unittest.TestCase): def group_sendmsg(self, msg, group): (self.msg, self.group) = (msg, group) def group_recvmsg(self, nonblock, seq): pass - class DummyModuleCCSession(): - module_spec = isc.config.module_spec.ModuleSpec({ - "module_name": "Boss", - "statistics": [ - { - "item_name": "boot_time", - "item_type": "string", - "item_optional": False, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "Boot time", - "item_description": "A date time when bind10 process starts initially", - "item_format": "date-time" - } - ] - }) - def get_module_spec(self): - return self.module_spec bob = BoB() bob.verbose = True bob.cc_session = DummySession() - bob.ccs = DummyModuleCCSession() # a bad command self.assertEqual(bob.command_handler(-1, None), isc.config.ccsession.create_answer(1, "bad command")) @@ -171,9 +153,8 @@ class TestBoB(unittest.TestCase): self.assertEqual(bob.cc_session.group, "Stats") self.assertEqual(bob.cc_session.msg, isc.config.ccsession.create_command( - "set", { "owner": "Boss", - "data": { - "boot_time": time.strftime("%Y-%m-%dT%H:%M:%SZ", _BASETIME) + 'set', { "stats_data": { + 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) }})) # "ping" command self.assertEqual(bob.command_handler("ping", None), diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index 49cadad4c9..e830f65d60 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -5,7 +5,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@ pkglibexec_SCRIPTS = b10-stats b10-stats-httpd b10_statsdir = $(pkgdatadir) -b10_stats_DATA = stats.spec stats-httpd.spec +b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl pyexec_DATA = stats_messages.py stats_httpd_messages.py @@ -16,7 +16,7 @@ CLEANFILES += stats_httpd_messages.py stats_httpd_messages.pyc man_MANS = b10-stats.8 b10-stats-httpd.8 EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml -EXTRA_DIST += stats.spec stats-httpd.spec +EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl EXTRA_DIST += stats_messages.mes stats_httpd_messages.mes diff --git a/src/bin/stats/b10-stats-httpd.8 b/src/bin/stats/b10-stats-httpd.8 index 1206e1d791..ed4aafa6c6 100644 --- a/src/bin/stats/b10-stats-httpd.8 +++ b/src/bin/stats/b10-stats-httpd.8 @@ -36,7 +36,7 @@ b10-stats-httpd \- BIND 10 HTTP server for HTTP/XML interface of statistics .PP \fBb10\-stats\-httpd\fR -is a standalone HTTP server\&. It is intended for HTTP/XML interface for statistics module\&. This server process runs as a process separated from the process of the BIND 10 Stats daemon (\fBb10\-stats\fR)\&. The server is initially executed by the BIND 10 boss process (\fBbind10\fR) and eventually exited by it\&. The server is intended to be server requests by HTTP clients like web browsers and third\-party modules\&. When the server is asked, it requests BIND 10 statistics data or its schema from +is a standalone HTTP server\&. It is intended for HTTP/XML interface for statistics module\&. This server process runs as a process separated from the process of the BIND 10 Stats daemon (\fBb10\-stats\fR)\&. The server is initially executed by the BIND 10 boss process (\fBbind10\fR) and eventually exited by it\&. The server is intended to be server requests by HTTP clients like web browsers and third\-party modules\&. When the server is asked, it requests BIND 10 statistics data from \fBb10\-stats\fR, and it sends the data back in Python dictionary format and the server converts it into XML format\&. The server sends it to the HTTP client\&. The server can send three types of document, which are XML (Extensible Markup Language), XSD (XML Schema definition) and XSL (Extensible Stylesheet Language)\&. The XML document is the statistics data of BIND 10, The XSD document is the data schema of it, and The XSL document is the style sheet to be showed for the web browsers\&. There is different URL for each document\&. But please note that you would be redirected to the URL of XML document if you request the URL of the root document\&. For example, you would be redirected to http://127\&.0\&.0\&.1:8000/bind10/statistics/xml if you request http://127\&.0\&.0\&.1:8000/\&. Please see the manual and the spec file of \fBb10\-stats\fR for more details about the items of BIND 10 statistics\&. The server uses CC session in communication with @@ -66,6 +66,10 @@ bindctl(1)\&. Please see the manual of bindctl(1) about how to configure the settings\&. .PP +/usr/local/share/bind10\-devel/stats\-schema\&.spec +\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via +bindctl(1)\&. +.PP /usr/local/share/bind10\-devel/stats\-httpd\-xml\&.tpl \(em the template file of XML document\&. diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml index c8df9b8a6e..34c704f509 100644 --- a/src/bin/stats/b10-stats-httpd.xml +++ b/src/bin/stats/b10-stats-httpd.xml @@ -57,7 +57,7 @@ by the BIND 10 boss process (bind10) and eventually exited by it. The server is intended to be server requests by HTTP clients like web browsers and third-party modules. When the server is - asked, it requests BIND 10 statistics data or its schema from + asked, it requests BIND 10 statistics data from b10-stats, and it sends the data back in Python dictionary format and the server converts it into XML format. The server sends it to the HTTP client. The server can send three types of document, @@ -112,6 +112,12 @@ of bindctl1 about how to configure the settings. + /usr/local/share/bind10-devel/stats-schema.spec + + — This is a spec file for data schema of + of BIND 10 statistics. This schema cannot be configured + via bindctl1. + /usr/local/share/bind10-devel/stats-httpd-xml.tpl @@ -132,7 +138,7 @@ CONFIGURATION AND COMMANDS - The configurable setting in + The configurable setting in stats-httpd.spec is: diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8 index 2c75cbcc0e..f69e4d37fa 100644 --- a/src/bin/stats/b10-stats.8 +++ b/src/bin/stats/b10-stats.8 @@ -66,6 +66,10 @@ switches to verbose mode\&. It sends verbose messages to STDOUT\&. \fBb10\-stats\fR\&. It contains commands for \fBb10\-stats\fR\&. They can be invoked via bindctl(1)\&. +.PP +/usr/local/share/bind10\-devel/stats\-schema\&.spec +\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via +bindctl(1)\&. .SH "SEE ALSO" .PP diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index bd2400a2d5..f0c472dd29 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -95,6 +95,12 @@ invoked via bindctl1. + /usr/local/share/bind10-devel/stats-schema.spec + + — This is a spec file for data schema of + of BIND 10 statistics. This schema cannot be configured + via bindctl1. + diff --git a/src/bin/stats/stats-httpd-xsl.tpl b/src/bin/stats/stats-httpd-xsl.tpl index a1f6406a5a..01ffdc681b 100644 --- a/src/bin/stats/stats-httpd-xsl.tpl +++ b/src/bin/stats/stats-httpd-xsl.tpl @@ -44,7 +44,6 @@ td.title {

BIND 10 Statistics

Owner Title Value
DummyFoo
- diff --git a/src/bin/stats/stats-schema.spec b/src/bin/stats/stats-schema.spec new file mode 100644 index 0000000000..52528657e8 --- /dev/null +++ b/src/bin/stats/stats-schema.spec @@ -0,0 +1,86 @@ +{ + "module_spec": { + "module_name": "Stats", + "module_description": "Statistics data schema", + "config_data": [ + { + "item_name": "report_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Report time", + "item_description": "A date time when stats module reports", + "item_format": "date-time" + }, + { + "item_name": "bind10.boot_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "bind10.BootTime", + "item_description": "A date time when bind10 process starts initially", + "item_format": "date-time" + }, + { + "item_name": "stats.boot_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "stats.BootTime", + "item_description": "A date time when the stats module starts initially or when the stats module restarts", + "item_format": "date-time" + }, + { + "item_name": "stats.start_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "stats.StartTime", + "item_description": "A date time when the stats module starts collecting data or resetting values last time", + "item_format": "date-time" + }, + { + "item_name": "stats.last_update_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "stats.LastUpdateTime", + "item_description": "The latest date time when the stats module receives from other modules like auth server or boss process and so on", + "item_format": "date-time" + }, + { + "item_name": "stats.timestamp", + "item_type": "real", + "item_optional": false, + "item_default": 0.0, + "item_title": "stats.Timestamp", + "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)" + }, + { + "item_name": "stats.lname", + "item_type": "string", + "item_optional": false, + "item_default": "", + "item_title": "stats.LocalName", + "item_description": "A localname of stats module given via CC protocol" + }, + { + "item_name": "auth.queries.tcp", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "auth.queries.tcp", + "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" + }, + { + "item_name": "auth.queries.udp", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "auth.queries.udp", + "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially" + } + ], + "commands": [] + } +} diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 9f24c67a9f..ce3d9f4612 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -15,17 +15,16 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -""" -Statistics daemon in BIND 10 - -""" import sys; sys.path.append ('@@PYTHONPATH@@') import os +import signal +import select from time import time, strftime, gmtime from optparse import OptionParser, OptionValueError +from collections import defaultdict +from isc.config.ccsession import ModuleCCSession, create_answer +from isc.cc import Session, SessionError -import isc -import isc.util.process import isc.log from stats_messages import * @@ -36,140 +35,211 @@ logger = isc.log.Logger("stats") # have #1074 DBG_STATS_MESSAGING = 30 -# This is for boot_time of Stats -_BASETIME = gmtime() - # for setproctitle +import isc.util.process isc.util.process.rename() # If B10_FROM_SOURCE is set in the environment, we use data files # from a directory relative to that, otherwise we use the ones # installed on the system if "B10_FROM_SOURCE" in os.environ: - SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + os.sep + "stats.spec" + BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" else: PREFIX = "@prefix@" DATAROOTDIR = "@datarootdir@" - SPECFILE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + os.sep + "stats.spec" - SPECFILE_LOCATION = SPECFILE_LOCATION.replace("${datarootdir}", DATAROOTDIR)\ - .replace("${prefix}", PREFIX) + BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) +SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats.spec" +SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec" -def get_timestamp(): +class Singleton(type): """ - get current timestamp + A abstract class of singleton pattern """ - return time() + # Because of singleton pattern: + # At the beginning of coding, one UNIX domain socket is needed + # for config manager, another socket is needed for stats module, + # then stats module might need two sockets. So I adopted the + # singleton pattern because I avoid creating multiple sockets in + # one stats module. But in the initial version stats module + # reports only via bindctl, so just one socket is needed. To use + # the singleton pattern is not important now. :( -def get_datetime(gmt=None): - """ - get current datetime - """ - if not gmt: gmt = gmtime() - return strftime("%Y-%m-%dT%H:%M:%SZ", gmt) + def __init__(self, *args, **kwargs): + type.__init__(self, *args, **kwargs) + self._instances = {} -def get_spec_defaults(spec): - """ - extracts the default values of the items from spec specified in - arg, and returns the dict-type variable which is a set of the item - names and the default values - """ - if type(spec) is not list: return {} - def _get_spec_defaults(spec): - item_type = spec['item_type'] - if item_type == "integer": - return int(spec.get('item_default', 0)) - elif item_type == "real": - return float(spec.get('item_default', 0.0)) - elif item_type == "boolean": - return bool(spec.get('item_default', False)) - elif item_type == "string": - return str(spec.get('item_default', "")) - elif item_type == "list": - return spec.get( - "item_default", - [ _get_spec_defaults(s) for s in spec["list_item_spec"] ]) - elif item_type == "map": - return spec.get( - "item_default", - dict([ (s["item_name"], _get_spec_defaults(s)) for s in spec["map_item_spec"] ]) ) - else: - return spec.get("item_default", None) - return dict([ (s['item_name'], _get_spec_defaults(s)) for s in spec ]) + def __call__(self, *args, **kwargs): + if args not in self._instances: + self._instances[args]={} + kw = tuple(kwargs.items()) + if kw not in self._instances[args]: + self._instances[args][kw] = type.__call__(self, *args, **kwargs) + return self._instances[args][kw] class Callback(): """ A Callback handler class """ - def __init__(self, command=None, args=(), kwargs={}): - self.command = command + def __init__(self, name=None, callback=None, args=(), kwargs={}): + self.name = name + self.callback = callback self.args = args self.kwargs = kwargs def __call__(self, *args, **kwargs): - if not args: args = self.args - if not kwargs: kwargs = self.kwargs - if self.command: return self.command(*args, **kwargs) + if not args: + args = self.args + if not kwargs: + kwargs = self.kwargs + if self.callback: + return self.callback(*args, **kwargs) -class StatsError(Exception): - """Exception class for Stats class""" - pass +class Subject(): + """ + A abstract subject class of observer pattern + """ + # Because of observer pattern: + # In the initial release, I'm also sure that observer pattern + # isn't definitely needed because the interface between gathering + # and reporting statistics data is single. However in the future + # release, the interfaces may be multiple, that is, multiple + # listeners may be needed. For example, one interface, which + # stats module has, is for between ''config manager'' and stats + # module, another interface is for between ''HTTP server'' and + # stats module, and one more interface is for between ''SNMP + # server'' and stats module. So by considering that stats module + # needs multiple interfaces in the future release, I adopted the + # observer pattern in stats module. But I don't have concrete + # ideas in case of multiple listener currently. -class Stats: - """ - Main class of stats module - """ def __init__(self): + self._listeners = [] + + def attach(self, listener): + if not listener in self._listeners: + self._listeners.append(listener) + + def detach(self, listener): + try: + self._listeners.remove(listener) + except ValueError: + pass + + def notify(self, event, modifier=None): + for listener in self._listeners: + if modifier != listener: + listener.update(event) + +class Listener(): + """ + A abstract listener class of observer pattern + """ + def __init__(self, subject): + self.subject = subject + self.subject.attach(self) + self.events = {} + + def update(self, name): + if name in self.events: + callback = self.events[name] + return callback() + + def add_event(self, event): + self.events[event.name]=event + +class SessionSubject(Subject, metaclass=Singleton): + """ + A concrete subject class which creates CC session object + """ + def __init__(self, session=None): + Subject.__init__(self) + self.session=session self.running = False + + def start(self): + self.running = True + self.notify('start') + + def stop(self): + self.running = False + self.notify('stop') + + def check(self): + self.notify('check') + +class CCSessionListener(Listener): + """ + A concrete listener class which creates SessionSubject object and + ModuleCCSession object + """ + def __init__(self, subject): + Listener.__init__(self, subject) + self.session = subject.session + self.boot_time = get_datetime() + # create ModuleCCSession object - self.mccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, - self.config_handler, - self.command_handler) - self.cc_session = self.mccs._session - # get module spec - self.module_name = self.mccs.get_module_spec().get_module_name() - self.modules = {} - self.statistics_data = {} + self.cc_session = ModuleCCSession(SPECFILE_LOCATION, + self.config_handler, + self.command_handler, + self.session) + + self.session = self.subject.session = self.cc_session._session + + # initialize internal data + self.stats_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION).get_config_spec() + self.stats_data = self.initialize_data(self.stats_spec) + + # add event handler invoked via SessionSubject object + self.add_event(Callback('start', self.start)) + self.add_event(Callback('stop', self.stop)) + self.add_event(Callback('check', self.check)) + # don't add 'command_' suffix to the special commands in + # order to prevent executing internal command via bindctl + # get commands spec - self.commands_spec = self.mccs.get_module_spec().get_commands_spec() + self.commands_spec = self.cc_session.get_module_spec().get_commands_spec() + # add event handler related command_handler of ModuleCCSession - self.callbacks = {} + # invoked via bindctl for cmd in self.commands_spec: - # add prefix "command_" - name = "command_" + cmd["command_name"] try: + # add prefix "command_" + name = "command_" + cmd["command_name"] callback = getattr(self, name) - kwargs = get_spec_defaults(cmd["command_args"]) - self.callbacks[name] = Callback(command=callback, kwargs=kwargs) - except AttributeError: - raise StatsError(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) - self.mccs.start() + kwargs = self.initialize_data(cmd["command_args"]) + self.add_event(Callback(name=name, callback=callback, args=(), kwargs=kwargs)) + except AttributeError as ae: + logger.error(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) def start(self): """ - Start stats module + start the cc chanel """ - self.running = True - logger.info(STATS_STARTING) - + # set initial value + self.stats_data['stats.boot_time'] = self.boot_time + self.stats_data['stats.start_time'] = get_datetime() + self.stats_data['stats.last_update_time'] = get_datetime() + self.stats_data['stats.lname'] = self.session.lname + self.cc_session.start() # request Bob to send statistics data logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) cmd = isc.config.ccsession.create_command("sendstats", None) - seq = self.cc_session.group_sendmsg(cmd, 'Boss') - self.cc_session.group_recvmsg(True, seq) + seq = self.session.group_sendmsg(cmd, 'Boss') + self.session.group_recvmsg(True, seq) - # initialized Statistics data - errors = self.update_statistics_data( - self.module_name, - lname=self.cc_session.lname, - boot_time=get_datetime(_BASETIME) - ) - if errors: - raise StatsError("stats spec file is incorrect: " - + ", ".join(errors)) + def stop(self): + """ + stop the cc chanel + """ + return self.cc_session.close() - while self.running: - self.mccs.check_command(False) + def check(self): + """ + check the cc chanel + """ + return self.cc_session.check_command(False) def config_handler(self, new_config): """ @@ -177,222 +247,174 @@ class Stats: """ logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_NEW_CONFIG, new_config) - # do nothing currently - return isc.config.create_answer(0) - def command_handler(self, command, kwargs): + # do nothing currently + return create_answer(0) + + def command_handler(self, command, *args, **kwargs): """ handle commands from the cc channel """ + # add 'command_' suffix in order to executing command via bindctl name = 'command_' + command - if name in self.callbacks: - callback = self.callbacks[name] - if kwargs: - return callback(**kwargs) - else: - return callback() + + if name in self.events: + event = self.events[name] + return event(*args, **kwargs) else: - logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command) - return isc.config.create_answer(1, "Unknown command: '"+str(command)+"'") + return self.command_unknown(command, args) - def update_modules(self): - """ - updates information of each module. This method gets each - module's information from the config manager and sets it into - self.modules. If its getting from the config manager fails, it - raises StatsError. - """ - modules = {} - seq = self.cc_session.group_sendmsg( - isc.config.ccsession.create_command( - isc.config.ccsession.COMMAND_GET_STATISTICS_SPEC), - 'ConfigManager') - (answer, env) = self.cc_session.group_recvmsg(False, seq) - if answer: - (rcode, value) = isc.config.ccsession.parse_answer(answer) - if rcode == 0: - for mod in value: - spec = { "module_name" : mod } - if value[mod] and type(value[mod]) is list: - spec["statistics"] = value[mod] - modules[mod] = isc.config.module_spec.ModuleSpec(spec) - else: - raise StatsError("Updating module spec fails: " + str(value)) - modules[self.module_name] = self.mccs.get_module_spec() - self.modules = modules - - def get_statistics_data(self, owner=None, name=None): - """ - returns statistics data which stats module has of each - module. If it can't find specified statistics data, it raises - StatsError. - """ - self.update_statistics_data() - if owner and name: - try: - return self.statistics_data[owner][name] - except KeyError: - pass - elif owner: - try: - return self.statistics_data[owner] - except KeyError: - pass - elif name: - pass - else: - return self.statistics_data - raise StatsError("No statistics data found: " - + "owner: " + str(owner) + ", " - + "name: " + str(name)) - - def update_statistics_data(self, owner=None, **data): - """ - change statistics date of specified module into specified - data. It updates information of each module first, and it - updates statistics data. If specified data is invalid for - statistics spec of specified owner, it returns a list of error - messeges. If there is no error or if neither owner nor data is - specified in args, it returns None. - """ - self.update_modules() - statistics_data = {} - for (name, module) in self.modules.items(): - value = get_spec_defaults(module.get_statistics_spec()) - if module.validate_statistics(True, value): - statistics_data[name] = value - for (name, value) in self.statistics_data.items(): - if name in statistics_data: - statistics_data[name].update(value) - else: - statistics_data[name] = value - self.statistics_data = statistics_data - if owner and data: - errors = [] - try: - if self.modules[owner].validate_statistics(False, data, errors): - self.statistics_data[owner].update(data) - return - except KeyError: - errors.append("unknown module name: " + str(owner)) - return errors - - def command_status(self): - """ - handle status command - """ - logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND) - return isc.config.create_answer( - 0, "Stats is up. (PID " + str(os.getpid()) + ")") - - def command_shutdown(self): + def command_shutdown(self, args): """ handle shutdown command """ logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND) - self.running = False - return isc.config.create_answer(0) + self.subject.running = False + return create_answer(0) - def command_show(self, owner=None, name=None): - """ - handle show command - """ - if owner or name: - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_SHOW_NAME_COMMAND, - str(owner)+", "+str(name)) - else: - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_SHOW_ALL_COMMAND) - errors = self.update_statistics_data( - self.module_name, - timestamp=get_timestamp(), - report_time=get_datetime() - ) - if errors: - raise StatsError("stats spec file is incorrect: " - + ", ".join(errors)) - try: - return isc.config.create_answer( - 0, self.get_statistics_data(owner, name)) - except StatsError: - return isc.config.create_answer( - 1, "specified arguments are incorrect: " \ - + "owner: " + str(owner) + ", name: " + str(name)) - - def command_showschema(self, owner=None, name=None): - """ - handle show command - """ - if owner or name: - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND, - str(owner)+", "+str(name)) - else: - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND) - self.update_modules() - schema = {} - schema_byname = {} - for mod in self.modules: - spec = self.modules[mod].get_statistics_spec() - schema_byname[mod] = {} - if spec: - schema[mod] = spec - for item in spec: - schema_byname[mod][item['item_name']] = item - if owner: - try: - if name: - return isc.config.create_answer(0, schema_byname[owner][name]) - else: - return isc.config.create_answer(0, schema[owner]) - except KeyError: - pass - else: - if name: - return isc.config.create_answer(1, "module name is not specified") - else: - return isc.config.create_answer(0, schema) - return isc.config.create_answer( - 1, "specified arguments are incorrect: " \ - + "owner: " + str(owner) + ", name: " + str(name)) - - def command_set(self, owner, data): + def command_set(self, args, stats_data={}): """ handle set command """ - errors = self.update_statistics_data(owner, **data) - if errors: - return isc.config.create_answer( - 1, "errors while setting statistics data: " \ - + ", ".join(errors)) - errors = self.update_statistics_data( - self.module_name, last_update_time=get_datetime() ) - if errors: - raise StatsError("stats spec file is incorrect: " - + ", ".join(errors)) - return isc.config.create_answer(0) + # 'args' must be dictionary type + self.stats_data.update(args['stats_data']) -if __name__ == "__main__": + # overwrite "stats.LastUpdateTime" + self.stats_data['stats.last_update_time'] = get_datetime() + + return create_answer(0) + + def command_remove(self, args, stats_item_name=''): + """ + handle remove command + """ + + # 'args' must be dictionary type + if args and args['stats_item_name'] in self.stats_data: + stats_item_name = args['stats_item_name'] + + logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_REMOVE_COMMAND, + stats_item_name) + + # just remove one item + self.stats_data.pop(stats_item_name) + + return create_answer(0) + + def command_show(self, args, stats_item_name=''): + """ + handle show command + """ + + # always overwrite 'report_time' and 'stats.timestamp' + # if "show" command invoked + self.stats_data['report_time'] = get_datetime() + self.stats_data['stats.timestamp'] = get_timestamp() + + # if with args + if args and args['stats_item_name'] in self.stats_data: + stats_item_name = args['stats_item_name'] + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOW_NAME_COMMAND, + stats_item_name) + return create_answer(0, {stats_item_name: self.stats_data[stats_item_name]}) + + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOW_ALL_COMMAND) + return create_answer(0, self.stats_data) + + def command_reset(self, args): + """ + handle reset command + """ + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_RESET_COMMAND) + + # re-initialize internal variables + self.stats_data = self.initialize_data(self.stats_spec) + + # reset initial value + self.stats_data['stats.boot_time'] = self.boot_time + self.stats_data['stats.start_time'] = get_datetime() + self.stats_data['stats.last_update_time'] = get_datetime() + self.stats_data['stats.lname'] = self.session.lname + + return create_answer(0) + + def command_status(self, args): + """ + handle status command + """ + logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND) + # just return "I'm alive." + return create_answer(0, "I'm alive.") + + def command_unknown(self, command, args): + """ + handle an unknown command + """ + logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command) + return create_answer(1, "Unknown command: '"+str(command)+"'") + + + def initialize_data(self, spec): + """ + initialize stats data + """ + def __get_init_val(spec): + if spec['item_type'] == 'null': + return None + elif spec['item_type'] == 'boolean': + return bool(spec.get('item_default', False)) + elif spec['item_type'] == 'string': + return str(spec.get('item_default', '')) + elif spec['item_type'] in set(['number', 'integer']): + return int(spec.get('item_default', 0)) + elif spec['item_type'] in set(['float', 'double', 'real']): + return float(spec.get('item_default', 0.0)) + elif spec['item_type'] in set(['list', 'array']): + return spec.get('item_default', + [ __get_init_val(s) for s in spec['list_item_spec'] ]) + elif spec['item_type'] in set(['map', 'object']): + return spec.get('item_default', + dict([ (s['item_name'], __get_init_val(s)) for s in spec['map_item_spec'] ]) ) + else: + return spec.get('item_default') + return dict([ (s['item_name'], __get_init_val(s)) for s in spec ]) + +def get_timestamp(): + """ + get current timestamp + """ + return time() + +def get_datetime(): + """ + get current datetime + """ + return strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + +def main(session=None): try: parser = OptionParser() - parser.add_option( - "-v", "--verbose", dest="verbose", action="store_true", - help="display more about what is going on") + parser.add_option("-v", "--verbose", dest="verbose", action="store_true", + help="display more about what is going on") (options, args) = parser.parse_args() if options.verbose: isc.log.init("b10-stats", "DEBUG", 99) - stats = Stats() - stats.start() + subject = SessionSubject(session=session) + listener = CCSessionListener(subject) + subject.start() + while subject.running: + subject.check() + subject.stop() + except OptionValueError as ove: logger.fatal(STATS_BAD_OPTION_VALUE, ove) - sys.exit(1) except SessionError as se: logger.fatal(STATS_CC_SESSION_ERROR, se) - sys.exit(1) - except StatsError as se: - logger.fatal(STATS_START_ERROR, se) - sys.exit(1) except KeyboardInterrupt as kie: logger.info(STATS_STOPPED_BY_KEYBOARD) + +if __name__ == "__main__": + main() diff --git a/src/bin/stats/stats.spec b/src/bin/stats/stats.spec index e716b62279..635eb486a1 100644 --- a/src/bin/stats/stats.spec +++ b/src/bin/stats/stats.spec @@ -6,51 +6,18 @@ "commands": [ { "command_name": "status", - "command_description": "Show status of the stats daemon", - "command_args": [] - }, - { - "command_name": "shutdown", - "command_description": "Shut down the stats module", + "command_description": "identify whether stats module is alive or not", "command_args": [] }, { "command_name": "show", - "command_description": "Show the specified/all statistics data", + "command_description": "show the specified/all statistics data", "command_args": [ { - "item_name": "owner", + "item_name": "stats_item_name", "item_type": "string", "item_optional": true, - "item_default": "", - "item_description": "module name of the owner of the statistics data" - }, - { - "item_name": "name", - "item_type": "string", - "item_optional": true, - "item_default": "", - "item_description": "statistics item name of the owner" - } - ] - }, - { - "command_name": "showschema", - "command_description": "show the specified/all statistics shema", - "command_args": [ - { - "item_name": "owner", - "item_type": "string", - "item_optional": true, - "item_default": "", - "item_description": "module name of the owner of the statistics data" - }, - { - "item_name": "name", - "item_type": "string", - "item_optional": true, - "item_default": "", - "item_description": "statistics item name of the owner" + "item_default": "" } ] }, @@ -59,21 +26,35 @@ "command_description": "set the value of specified name in statistics data", "command_args": [ { - "item_name": "owner", - "item_type": "string", - "item_optional": false, - "item_default": "", - "item_description": "module name of the owner of the statistics data" - }, - { - "item_name": "data", + "item_name": "stats_data", "item_type": "map", "item_optional": false, "item_default": {}, - "item_description": "statistics data set of the owner", "map_item_spec": [] } ] + }, + { + "command_name": "remove", + "command_description": "remove the specified name from statistics data", + "command_args": [ + { + "item_name": "stats_item_name", + "item_type": "string", + "item_optional": false, + "item_default": "" + } + ] + }, + { + "command_name": "reset", + "command_description": "reset all statistics data to default values except for several constant names", + "command_args": [] + }, + { + "command_name": "shutdown", + "command_description": "Shut down the stats module", + "command_args": [] } ], "statistics": [ @@ -119,7 +100,7 @@ "item_default": "", "item_title": "Local Name", "item_description": "A localname of stats module given via CC protocol" - } + } ] } } diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in old mode 100644 new mode 100755 index f8a09e5610..74298cf288 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -57,6 +57,7 @@ else: BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd.spec" +SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec" XML_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xml.tpl" XSD_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsd.tpl" XSL_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsl.tpl" @@ -68,7 +69,7 @@ XSD_URL_PATH = '/bind10/statistics/xsd' XSL_URL_PATH = '/bind10/statistics/xsl' # TODO: This should be considered later. XSD_NAMESPACE = 'http://bind10.isc.org' + XSD_URL_PATH -DEFAULT_CONFIG = dict(version=0, listen_on=[('127.0.0.1', 8000)]) +DEFAULT_CONFIG = dict(listen_on=[('127.0.0.1', 8000)]) # Assign this process name isc.util.process.rename() @@ -160,6 +161,8 @@ class StatsHttpd: self.httpd = [] self.open_mccs() self.load_config() + self.load_templates() + self.open_httpd() def open_mccs(self): """Opens a ModuleCCSession object""" @@ -168,6 +171,10 @@ class StatsHttpd: self.mccs = isc.config.ModuleCCSession( SPECFILE_LOCATION, self.config_handler, self.command_handler) self.cc_session = self.mccs._session + # read spec file of stats module and subscribe 'Stats' + self.stats_module_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION) + self.stats_config_spec = self.stats_module_spec.get_config_spec() + self.stats_module_name = self.stats_module_spec.get_module_name() def close_mccs(self): """Closes a ModuleCCSession object""" @@ -201,41 +208,45 @@ class StatsHttpd: for addr in self.http_addrs: self.httpd.append(self._open_httpd(addr)) - def _open_httpd(self, server_address): - httpd = None + def _open_httpd(self, server_address, address_family=None): try: - # get address family for the server_address before - # creating HttpServer object - address_family = socket.getaddrinfo(*server_address)[0][0] - HttpServer.address_family = address_family + # try IPv6 at first + if address_family is not None: + HttpServer.address_family = address_family + elif socket.has_ipv6: + HttpServer.address_family = socket.AF_INET6 httpd = HttpServer( server_address, HttpHandler, self.xml_handler, self.xsd_handler, self.xsl_handler, self.write_log) - logger.info(STATHTTPD_STARTED, server_address[0], - server_address[1]) - return httpd except (socket.gaierror, socket.error, OverflowError, TypeError) as err: - if httpd: - httpd.server_close() - raise HttpServerError( - "Invalid address %s, port %s: %s: %s" % - (server_address[0], server_address[1], - err.__class__.__name__, err)) + # try IPv4 next + if HttpServer.address_family == socket.AF_INET6: + httpd = self._open_httpd(server_address, socket.AF_INET) + else: + raise HttpServerError( + "Invalid address %s, port %s: %s: %s" % + (server_address[0], server_address[1], + err.__class__.__name__, err)) + else: + logger.info(STATHTTPD_STARTED, server_address[0], + server_address[1]) + return httpd def close_httpd(self): """Closes sockets for HTTP""" - while len(self.httpd)>0: - ht = self.httpd.pop() + if len(self.httpd) == 0: + return + for ht in self.httpd: logger.info(STATHTTPD_CLOSING, ht.server_address[0], ht.server_address[1]) ht.server_close() + self.httpd = [] def start(self): """Starts StatsHttpd objects to run. Waiting for client requests by using select.select functions""" - self.open_httpd() self.mccs.start() self.running = True while self.running: @@ -299,9 +310,9 @@ class StatsHttpd: except HttpServerError as err: logger.error(STATHTTPD_SERVER_ERROR, err) # restore old config - self.load_config(old_config) - self.open_httpd() - return isc.config.ccsession.create_answer(1, str(err)) + self.config_handler(old_config) + return isc.config.ccsession.create_answer( + 1, "[b10-stats-httpd] %s" % err) else: return isc.config.ccsession.create_answer(0) @@ -330,7 +341,8 @@ class StatsHttpd: the data which obtains from it""" try: seq = self.cc_session.group_sendmsg( - isc.config.ccsession.create_command('show'), 'Stats') + isc.config.ccsession.create_command('show'), + self.stats_module_name) (answer, env) = self.cc_session.group_recvmsg(False, seq) if answer: (rcode, value) = isc.config.ccsession.parse_answer(answer) @@ -345,82 +357,34 @@ class StatsHttpd: raise StatsHttpdError("Stats module: %s" % str(value)) def get_stats_spec(self): - """Requests statistics data to the Stats daemon and returns - the data which obtains from it""" - try: - seq = self.cc_session.group_sendmsg( - isc.config.ccsession.create_command('showschema'), 'Stats') - (answer, env) = self.cc_session.group_recvmsg(False, seq) - if answer: - (rcode, value) = isc.config.ccsession.parse_answer(answer) - if rcode == 0: - return value - else: - raise StatsHttpdError("Stats module: %s" % str(value)) - except (isc.cc.session.SessionTimeout, - isc.cc.session.SessionError) as err: - raise StatsHttpdError("%s: %s" % - (err.__class__.__name__, err)) + """Just returns spec data""" + return self.stats_config_spec - def xml_handler(self): - """Handler which requests to Stats daemon to obtain statistics - data and returns the body of XML document""" - xml_list=[] - for (mod, spec) in self.get_stats_data().items(): - if not spec: continue - elem1 = xml.etree.ElementTree.Element(str(mod)) - for (k, v) in spec.items(): - elem2 = xml.etree.ElementTree.Element(str(k)) - elem2.text = str(v) - elem1.append(elem2) - # The coding conversion is tricky. xml..tostring() of Python 3.2 - # returns bytes (not string) regardless of the coding, while - # tostring() of Python 3.1 returns a string. To support both - # cases transparently, we first make sure tostring() returns - # bytes by specifying utf-8 and then convert the result to a - # plain string (code below assume it). - xml_list.append( - str(xml.etree.ElementTree.tostring(elem1, encoding='utf-8'), - encoding='us-ascii')) - xml_string = "".join(xml_list) - self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute( - xml_string=xml_string, - xsd_namespace=XSD_NAMESPACE, - xsd_url_path=XSD_URL_PATH, - xsl_url_path=XSL_URL_PATH) - assert self.xml_body is not None - return self.xml_body - - def xsd_handler(self): - """Handler which just returns the body of XSD document""" + def load_templates(self): + """Setup the bodies of XSD and XSL documents to be responds to + HTTP clients. Before that it also creates XML tag structures by + using xml.etree.ElementTree.Element class and substitutes + concrete strings with parameters embed in the string.Template + object.""" # for XSD xsd_root = xml.etree.ElementTree.Element("all") # started with "all" tag - for (mod, spec) in self.get_stats_spec().items(): - if not spec: continue - alltag = xml.etree.ElementTree.Element("all") - for item in spec: - element = xml.etree.ElementTree.Element( - "element", - dict( name=item["item_name"], - type=item["item_type"] if item["item_type"].lower() != 'real' else 'float', - minOccurs="1", - maxOccurs="1" ), - ) - annotation = xml.etree.ElementTree.Element("annotation") - appinfo = xml.etree.ElementTree.Element("appinfo") - documentation = xml.etree.ElementTree.Element("documentation") - appinfo.text = item["item_title"] - documentation.text = item["item_description"] - annotation.append(appinfo) - annotation.append(documentation) - element.append(annotation) - alltag.append(element) - - complextype = xml.etree.ElementTree.Element("complexType") - complextype.append(alltag) - mod_element = xml.etree.ElementTree.Element("element", { "name" : mod }) - mod_element.append(complextype) - xsd_root.append(mod_element) + for item in self.get_stats_spec(): + element = xml.etree.ElementTree.Element( + "element", + dict( name=item["item_name"], + type=item["item_type"] if item["item_type"].lower() != 'real' else 'float', + minOccurs="1", + maxOccurs="1" ), + ) + annotation = xml.etree.ElementTree.Element("annotation") + appinfo = xml.etree.ElementTree.Element("appinfo") + documentation = xml.etree.ElementTree.Element("documentation") + appinfo.text = item["item_title"] + documentation.text = item["item_description"] + annotation.append(appinfo) + annotation.append(documentation) + element.append(annotation) + xsd_root.append(element) # The coding conversion is tricky. xml..tostring() of Python 3.2 # returns bytes (not string) regardless of the coding, while # tostring() of Python 3.1 returns a string. To support both @@ -434,33 +398,25 @@ class StatsHttpd: xsd_namespace=XSD_NAMESPACE ) assert self.xsd_body is not None - return self.xsd_body - def xsl_handler(self): - """Handler which just returns the body of XSL document""" # for XSL xsd_root = xml.etree.ElementTree.Element( "xsl:template", dict(match="*")) # started with xml:template tag - for (mod, spec) in self.get_stats_spec().items(): - if not spec: continue - for item in spec: - tr = xml.etree.ElementTree.Element("tr") - td0 = xml.etree.ElementTree.Element("td") - td0.text = str(mod) - td1 = xml.etree.ElementTree.Element( - "td", { "class" : "title", - "title" : item["item_description"] }) - td1.text = item["item_title"] - td2 = xml.etree.ElementTree.Element("td") - xsl_valueof = xml.etree.ElementTree.Element( - "xsl:value-of", - dict(select=mod+'/'+item["item_name"])) - td2.append(xsl_valueof) - tr.append(td0) - tr.append(td1) - tr.append(td2) - xsd_root.append(tr) + for item in self.get_stats_spec(): + tr = xml.etree.ElementTree.Element("tr") + td1 = xml.etree.ElementTree.Element( + "td", { "class" : "title", + "title" : item["item_description"] }) + td1.text = item["item_title"] + td2 = xml.etree.ElementTree.Element("td") + xsl_valueof = xml.etree.ElementTree.Element( + "xsl:value-of", + dict(select=item["item_name"])) + td2.append(xsl_valueof) + tr.append(td1) + tr.append(td2) + xsd_root.append(tr) # The coding conversion is tricky. xml..tostring() of Python 3.2 # returns bytes (not string) regardless of the coding, while # tostring() of Python 3.1 returns a string. To support both @@ -473,15 +429,47 @@ class StatsHttpd: xsl_string=xsl_string, xsd_namespace=XSD_NAMESPACE) assert self.xsl_body is not None + + def xml_handler(self): + """Handler which requests to Stats daemon to obtain statistics + data and returns the body of XML document""" + xml_list=[] + for (k, v) in self.get_stats_data().items(): + (k, v) = (str(k), str(v)) + elem = xml.etree.ElementTree.Element(k) + elem.text = v + # The coding conversion is tricky. xml..tostring() of Python 3.2 + # returns bytes (not string) regardless of the coding, while + # tostring() of Python 3.1 returns a string. To support both + # cases transparently, we first make sure tostring() returns + # bytes by specifying utf-8 and then convert the result to a + # plain string (code below assume it). + xml_list.append( + str(xml.etree.ElementTree.tostring(elem, encoding='utf-8'), + encoding='us-ascii')) + xml_string = "".join(xml_list) + self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute( + xml_string=xml_string, + xsd_namespace=XSD_NAMESPACE, + xsd_url_path=XSD_URL_PATH, + xsl_url_path=XSL_URL_PATH) + assert self.xml_body is not None + return self.xml_body + + def xsd_handler(self): + """Handler which just returns the body of XSD document""" + return self.xsd_body + + def xsl_handler(self): + """Handler which just returns the body of XSL document""" return self.xsl_body def open_template(self, file_name): """It opens a template file, and it loads all lines to a string variable and returns string. Template object includes the variable. Limitation of a file size isn't needed there.""" - f = open(file_name, 'r') - lines = "".join(f.readlines()) - f.close() + lines = "".join( + open(file_name, 'r').readlines()) assert lines is not None return string.Template(lines) diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes index cfffb3adb8..9ad07cf493 100644 --- a/src/bin/stats/stats_messages.mes +++ b/src/bin/stats/stats_messages.mes @@ -28,6 +28,16 @@ control bus. A likely problem is that the message bus daemon This debug message is printed when the stats module has received a configuration update from the configuration manager. +% STATS_RECEIVED_REMOVE_COMMAND received command to remove %1 +A remove command for the given name was sent to the stats module, and +the given statistics value will now be removed. It will not appear in +statistics reports until it appears in a statistics update from a +module again. + +% STATS_RECEIVED_RESET_COMMAND received command to reset all statistics +The stats module received a command to clear all collected statistics. +The data is cleared until it receives an update from the modules again. + % STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics The stats module received a command to show all statistics that it has collected. @@ -62,15 +72,4 @@ installation problem, where the specification file stats.spec is from a different version of BIND 10 than the stats module itself. Please check your installation. -% STATS_STARTING starting -The stats module will be now starting. -% STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND received command to show all statistics schema -The stats module received a command to show all statistics schemas of all modules. - -% STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND received command to show statistics schema for %1 -The stats module received a command to show the specified statistics schema of the specified module. - -% STATS_START_ERROR stats module error: %1 -An internal error occurred while starting the stats module. The stats -module will be now shutting down. diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index 368e90c700..dad6c48bbc 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -1,7 +1,8 @@ +SUBDIRS = isc http testdata PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ PYTESTS = b10-stats_test.py b10-stats-httpd_test.py -EXTRA_DIST = $(PYTESTS) test_utils.py -CLEANFILES = test_utils.pyc +EXTRA_DIST = $(PYTESTS) fake_time.py fake_socket.py fake_select.py +CLEANFILES = fake_time.pyc fake_socket.pyc fake_select.pyc # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. @@ -13,16 +14,15 @@ endif # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif for pytest in $(PYTESTS) ; do \ 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/bin/stats:$(abs_top_builddir)/src/bin/stats/tests:$(abs_top_builddir)/src/bin/msgq:$(abs_top_builddir)/src/lib/python/isc/config \ + env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \ B10_FROM_SOURCE=$(abs_top_srcdir) \ - CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/tests/testdata \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 8c84277930..6d72dc2f38 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -13,251 +13,147 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -""" -In each of these tests we start several virtual components. They are -not the real components, no external processes are started. They are -just simple mock objects running each in its own thread and pretending -to be bind10 modules. This helps testing the stats http server in a -close to real environment. -""" - import unittest import os -import imp -import socket -import errno -import select +import http.server import string -import time -import threading -import http.client -import xml.etree.ElementTree +import fake_select +import imp +import sys +import fake_socket + +import isc.cc -import isc import stats_httpd -import stats -from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC - -# set test name for logger -isc.log.init("b10-stats-httpd_test") +stats_httpd.socket = fake_socket +stats_httpd.select = fake_select DUMMY_DATA = { - 'Boss' : { - "boot_time": "2011-03-04T11:59:06Z" - }, - 'Auth' : { - "queries.tcp": 2, - "queries.udp": 3 - }, - 'Stats' : { - "report_time": "2011-03-04T11:59:19Z", - "boot_time": "2011-03-04T11:59:06Z", - "last_update_time": "2011-03-04T11:59:07Z", - "lname": "4d70d40a_c@host", - "timestamp": 1299239959.560846 - } + "auth.queries.tcp": 10000, + "auth.queries.udp": 12000, + "bind10.boot_time": "2011-03-04T11:59:05Z", + "report_time": "2011-03-04T11:59:19Z", + "stats.boot_time": "2011-03-04T11:59:06Z", + "stats.last_update_time": "2011-03-04T11:59:07Z", + "stats.lname": "4d70d40a_c@host", + "stats.start_time": "2011-03-04T11:59:06Z", + "stats.timestamp": 1299239959.560846 } +def push_answer(stats_httpd): + stats_httpd.cc_session.group_sendmsg( + { 'result': + [ 0, DUMMY_DATA ] }, "Stats") + +def pull_query(stats_httpd): + (msg, env) = stats_httpd.cc_session.group_recvmsg() + if 'result' in msg: + (ret, arg) = isc.config.ccsession.parse_answer(msg) + else: + (ret, arg) = isc.config.ccsession.parse_command(msg) + return (ret, arg, env) + class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" def setUp(self): - self.base = BaseModules() - self.stats_server = ThreadingServerManager(MyStats) - self.stats = self.stats_server.server - self.stats_server.run() - - def tearDown(self): - self.stats_server.shutdown() - self.base.shutdown() + self.stats_httpd = stats_httpd.StatsHttpd() + self.assertTrue(type(self.stats_httpd.httpd) is list) + self.httpd = self.stats_httpd.httpd def test_do_GET(self): - (address, port) = ('127.0.0.1', 65450) - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.stats_httpd = statshttpd_server.server - self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - self.assertTrue(type(self.stats_httpd.httpd) is list) - self.assertEqual(len(self.stats_httpd.httpd), 0) - statshttpd_server.run() - client = http.client.HTTPConnection(address, port) - client._http_vsn_str = 'HTTP/1.0\n' - client.connect() + for ht in self.httpd: + self._test_do_GET(ht._handler) + + def _test_do_GET(self, handler): # URL is '/bind10/statistics/xml' - client.putrequest('GET', stats_httpd.XML_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.getheader("Content-type"), "text/xml") - self.assertTrue(int(response.getheader("Content-Length")) > 0) - self.assertEqual(response.status, 200) - root = xml.etree.ElementTree.parse(response).getroot() - self.assertTrue(root.tag.find('stats_data') > 0) - for (k,v) in root.attrib.items(): - if k.find('schemaLocation') > 0: - self.assertEqual(v, stats_httpd.XSD_NAMESPACE + ' ' + stats_httpd.XSD_URL_PATH) - for mod in DUMMY_DATA: - for (item, value) in DUMMY_DATA[mod].items(): - self.assertIsNotNone(root.find(mod + '/' + item)) + handler.path = stats_httpd.XML_URL_PATH + push_answer(self.stats_httpd) + handler.do_GET() + (ret, arg, env) = pull_query(self.stats_httpd) + self.assertEqual(ret, "show") + self.assertIsNone(arg) + self.assertTrue('group' in env) + self.assertEqual(env['group'], 'Stats') + self.assertEqual(handler.response.code, 200) + self.assertEqual(handler.response.headers["Content-type"], "text/xml") + self.assertTrue(handler.response.headers["Content-Length"] > 0) + self.assertTrue(handler.response.wrote_headers) + self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) + self.assertTrue(handler.response.body.find(stats_httpd.XSD_URL_PATH)>0) + for (k, v) in DUMMY_DATA.items(): + self.assertTrue(handler.response.body.find(str(k))>0) + self.assertTrue(handler.response.body.find(str(v))>0) # URL is '/bind10/statitics/xsd' - client.putrequest('GET', stats_httpd.XSD_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.getheader("Content-type"), "text/xml") - self.assertTrue(int(response.getheader("Content-Length")) > 0) - self.assertEqual(response.status, 200) - root = xml.etree.ElementTree.parse(response).getroot() - url_xmlschema = '{http://www.w3.org/2001/XMLSchema}' - tags = [ url_xmlschema + t for t in [ 'element', 'complexType', 'all', 'element' ] ] - xsdpath = '/'.join(tags) - self.assertTrue(root.tag.find('schema') > 0) - self.assertTrue(hasattr(root, 'attrib')) - self.assertTrue('targetNamespace' in root.attrib) - self.assertEqual(root.attrib['targetNamespace'], - stats_httpd.XSD_NAMESPACE) - for elm in root.findall(xsdpath): - self.assertIsNotNone(elm.attrib['name']) - self.assertTrue(elm.attrib['name'] in DUMMY_DATA) + handler.path = stats_httpd.XSD_URL_PATH + handler.do_GET() + self.assertEqual(handler.response.code, 200) + self.assertEqual(handler.response.headers["Content-type"], "text/xml") + self.assertTrue(handler.response.headers["Content-Length"] > 0) + self.assertTrue(handler.response.wrote_headers) + self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) + for (k, v) in DUMMY_DATA.items(): + self.assertTrue(handler.response.body.find(str(k))>0) # URL is '/bind10/statitics/xsl' - client.putrequest('GET', stats_httpd.XSL_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.getheader("Content-type"), "text/xml") - self.assertTrue(int(response.getheader("Content-Length")) > 0) - self.assertEqual(response.status, 200) - root = xml.etree.ElementTree.parse(response).getroot() - url_trans = '{http://www.w3.org/1999/XSL/Transform}' - url_xhtml = '{http://www.w3.org/1999/xhtml}' - xslpath = url_trans + 'template/' + url_xhtml + 'tr' - self.assertEqual(root.tag, url_trans + 'stylesheet') - for tr in root.findall(xslpath): - tds = tr.findall(url_xhtml + 'td') - self.assertIsNotNone(tds) - self.assertEqual(type(tds), list) - self.assertTrue(len(tds) > 2) - self.assertTrue(hasattr(tds[0], 'text')) - self.assertTrue(tds[0].text in DUMMY_DATA) - valueof = tds[2].find(url_trans + 'value-of') - self.assertIsNotNone(valueof) - self.assertTrue(hasattr(valueof, 'attrib')) - self.assertIsNotNone(valueof.attrib) - self.assertTrue('select' in valueof.attrib) - self.assertTrue(valueof.attrib['select'] in \ - [ tds[0].text+'/'+item for item in DUMMY_DATA[tds[0].text].keys() ]) + handler.path = stats_httpd.XSL_URL_PATH + handler.do_GET() + self.assertEqual(handler.response.code, 200) + self.assertEqual(handler.response.headers["Content-type"], "text/xml") + self.assertTrue(handler.response.headers["Content-Length"] > 0) + self.assertTrue(handler.response.wrote_headers) + self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) + for (k, v) in DUMMY_DATA.items(): + self.assertTrue(handler.response.body.find(str(k))>0) # 302 redirect - client._http_vsn_str = 'HTTP/1.1' - client.putrequest('GET', '/') - client.putheader('Host', address) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 302) - self.assertEqual(response.getheader('Location'), - "http://%s:%d%s" % (address, port, stats_httpd.XML_URL_PATH)) + handler.path = '/' + handler.headers = {'Host': 'my.host.domain'} + handler.do_GET() + self.assertEqual(handler.response.code, 302) + self.assertEqual(handler.response.headers["Location"], + "http://my.host.domain%s" % stats_httpd.XML_URL_PATH) - # # 404 NotFound - client._http_vsn_str = 'HTTP/1.0' - client.putrequest('GET', '/path/to/foo/bar') - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 404) + # 404 NotFound + handler.path = '/path/to/foo/bar' + handler.headers = {} + handler.do_GET() + self.assertEqual(handler.response.code, 404) - client.close() - statshttpd_server.shutdown() - - def test_do_GET_failed1(self): # failure case(connection with Stats is down) - (address, port) = ('127.0.0.1', 65451) - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - statshttpd = statshttpd_server.server - statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - statshttpd_server.run() - self.assertTrue(self.stats_server.server.running) - self.stats_server.shutdown() - self.assertFalse(self.stats_server.server.running) - statshttpd.cc_session.set_timeout(milliseconds=TIMEOUT_SEC/1000) - client = http.client.HTTPConnection(address, port) - client.connect() + handler.path = stats_httpd.XML_URL_PATH + push_answer(self.stats_httpd) + self.assertFalse(self.stats_httpd.cc_session._socket._closed) + self.stats_httpd.cc_session._socket._closed = True + handler.do_GET() + self.stats_httpd.cc_session._socket._closed = False + self.assertEqual(handler.response.code, 500) + self.stats_httpd.cc_session._clear_queues() - # request XML - client.putrequest('GET', stats_httpd.XML_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 500) - - # request XSD - client.putrequest('GET', stats_httpd.XSD_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 500) - - # request XSL - client.putrequest('GET', stats_httpd.XSL_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 500) - - client.close() - statshttpd_server.shutdown() - - def test_do_GET_failed2(self): - # failure case(connection with Stats is down) - (address, port) = ('127.0.0.1', 65452) - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.stats_httpd = statshttpd_server.server - self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - statshttpd_server.run() - self.stats.mccs.set_command_handler( - lambda cmd, args: \ - isc.config.ccsession.create_answer(1, "I have an error.") - ) - client = http.client.HTTPConnection(address, port) - client.connect() - - # request XML - client.putrequest('GET', stats_httpd.XML_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 500) - - # request XSD - client.putrequest('GET', stats_httpd.XSD_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 500) - - # request XSL - client.putrequest('GET', stats_httpd.XSL_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 500) - - client.close() - statshttpd_server.shutdown() + # failure case(Stats module returns err) + handler.path = stats_httpd.XML_URL_PATH + self.stats_httpd.cc_session.group_sendmsg( + { 'result': [ 1, "I have an error." ] }, "Stats") + self.assertFalse(self.stats_httpd.cc_session._socket._closed) + self.stats_httpd.cc_session._socket._closed = False + handler.do_GET() + self.assertEqual(handler.response.code, 500) + self.stats_httpd.cc_session._clear_queues() def test_do_HEAD(self): - (address, port) = ('127.0.0.1', 65453) - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.stats_httpd = statshttpd_server.server - self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - statshttpd_server.run() - client = http.client.HTTPConnection(address, port) - client.connect() - client.putrequest('HEAD', stats_httpd.XML_URL_PATH) - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 200) + for ht in self.httpd: + self._test_do_HEAD(ht._handler) - client.putrequest('HEAD', '/path/to/foo/bar') - client.endheaders() - response = client.getresponse() - self.assertEqual(response.status, 404) - client.close() - statshttpd_server.shutdown() + def _test_do_HEAD(self, handler): + handler.path = '/path/to/foo/bar' + handler.do_HEAD() + self.assertEqual(handler.response.code, 404) class TestHttpServerError(unittest.TestCase): """Tests for HttpServerError exception""" + def test_raises(self): try: raise stats_httpd.HttpServerError('Nothing') @@ -266,16 +162,17 @@ class TestHttpServerError(unittest.TestCase): class TestHttpServer(unittest.TestCase): """Tests for HttpServer class""" - def setUp(self): - self.base = BaseModules() - - def tearDown(self): - self.base.shutdown() def test_httpserver(self): - statshttpd = stats_httpd.StatsHttpd() - self.assertEqual(type(statshttpd.httpd), list) - self.assertEqual(len(statshttpd.httpd), 0) + self.stats_httpd = stats_httpd.StatsHttpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(ht.server_address in self.stats_httpd.http_addrs) + self.assertEqual(ht.xml_handler, self.stats_httpd.xml_handler) + self.assertEqual(ht.xsd_handler, self.stats_httpd.xsd_handler) + self.assertEqual(ht.xsl_handler, self.stats_httpd.xsl_handler) + self.assertEqual(ht.log_writer, self.stats_httpd.write_log) + self.assertTrue(isinstance(ht._handler, stats_httpd.HttpHandler)) + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) class TestStatsHttpdError(unittest.TestCase): """Tests for StatsHttpdError exception""" @@ -290,173 +187,130 @@ class TestStatsHttpd(unittest.TestCase): """Tests for StatsHttpd class""" def setUp(self): - self.base = BaseModules() - self.stats_server = ThreadingServerManager(MyStats) - self.stats = self.stats_server.server - self.stats_server.run() + fake_socket._CLOSED = False + fake_socket.has_ipv6 = True self.stats_httpd = stats_httpd.StatsHttpd() - # checking IPv6 enabled on this platform - self.ipv6_enabled = True - try: - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - sock.bind(("::1",8000)) - sock.close() - except socket.error: - self.ipv6_enabled = False - def tearDown(self): self.stats_httpd.stop() - self.stats_server.shutdown() - self.base.shutdown() def test_init(self): - self.assertEqual(self.stats_httpd.running, False) - self.assertEqual(self.stats_httpd.poll_intval, 0.5) - self.assertEqual(self.stats_httpd.httpd, []) - self.assertEqual(type(self.stats_httpd.mccs), isc.config.ModuleCCSession) - self.assertEqual(type(self.stats_httpd.cc_session), isc.cc.Session) - self.assertEqual(len(self.stats_httpd.config), 2) - self.assertTrue('listen_on' in self.stats_httpd.config) - self.assertEqual(len(self.stats_httpd.config['listen_on']), 1) - self.assertTrue('address' in self.stats_httpd.config['listen_on'][0]) - self.assertTrue('port' in self.stats_httpd.config['listen_on'][0]) - self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) - - def test_openclose_mccs(self): - statshttpd = stats_httpd.StatsHttpd() - statshttpd.close_mccs() - self.assertEqual(statshttpd.mccs, None) - statshttpd.open_mccs() - self.assertIsNotNone(statshttpd.mccs) - statshttpd.mccs = None - self.assertEqual(statshttpd.mccs, None) - self.assertEqual(statshttpd.close_mccs(), None) + self.assertFalse(self.stats_httpd.mccs.get_socket()._closed) + self.assertEqual(self.stats_httpd.mccs.get_socket().fileno(), + id(self.stats_httpd.mccs.get_socket())) + for ht in self.stats_httpd.httpd: + self.assertFalse(ht.socket._closed) + self.assertEqual(ht.socket.fileno(), id(ht.socket)) + fake_socket._CLOSED = True + self.assertRaises(isc.cc.session.SessionError, + stats_httpd.StatsHttpd) + fake_socket._CLOSED = False def test_mccs(self): - self.assertIsNotNone(self.stats_httpd.mccs.get_socket()) + self.stats_httpd.open_mccs() self.assertTrue( - isinstance(self.stats_httpd.mccs.get_socket(), socket.socket)) + isinstance(self.stats_httpd.mccs.get_socket(), fake_socket.socket)) self.assertTrue( isinstance(self.stats_httpd.cc_session, isc.cc.session.Session)) - self.statistics_spec = self.stats_httpd.get_stats_spec() - for mod in DUMMY_DATA: - self.assertTrue(mod in self.statistics_spec) - for cfg in self.statistics_spec[mod]: - self.assertTrue('item_name' in cfg) - self.assertTrue(cfg['item_name'] in DUMMY_DATA[mod]) - self.assertTrue(len(self.statistics_spec[mod]), len(DUMMY_DATA[mod])) - self.stats_httpd.close_mccs() - self.assertIsNone(self.stats_httpd.mccs) + self.assertTrue( + isinstance(self.stats_httpd.stats_module_spec, isc.config.ModuleSpec)) + for cfg in self.stats_httpd.stats_config_spec: + self.assertTrue('item_name' in cfg) + self.assertTrue(cfg['item_name'] in DUMMY_DATA) + self.assertTrue(len(self.stats_httpd.stats_config_spec), len(DUMMY_DATA)) + + def test_load_config(self): + self.stats_httpd.load_config() + self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) def test_httpd(self): # dual stack (addresses is ipv4 and ipv6) - if self.ipv6_enabled: - self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) - self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ] - self.assertTrue( - stats_httpd.HttpServer.address_family in set([socket.AF_INET, socket.AF_INET6])) - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + fake_socket.has_ipv6 = True + self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ] + self.assertTrue( + stats_httpd.HttpServer.address_family in set([fake_socket.AF_INET, fake_socket.AF_INET6])) + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() # dual stack (address is ipv6) - if self.ipv6_enabled: - self.stats_httpd.http_addrs = [ ('::1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + fake_socket.has_ipv6 = True + self.stats_httpd.http_addrs = [ ('::1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() # dual stack (address is ipv4) - if self.ipv6_enabled: - self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + fake_socket.has_ipv6 = True + self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() # only-ipv4 single stack - if not self.ipv6_enabled: - self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + fake_socket.has_ipv6 = False + self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() # only-ipv4 single stack (force set ipv6 ) - if not self.ipv6_enabled: - self.stats_httpd.http_addrs = [ ('::1', 8000) ] - self.assertRaises(stats_httpd.HttpServerError, - self.stats_httpd.open_httpd) + fake_socket.has_ipv6 = False + self.stats_httpd.http_addrs = [ ('::1', 8000) ] + self.assertRaises(stats_httpd.HttpServerError, + self.stats_httpd.open_httpd) # hostname self.stats_httpd.http_addrs = [ ('localhost', 8000) ] self.stats_httpd.open_httpd() for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, socket.socket)) + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) self.stats_httpd.close_httpd() self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ] - self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - self.assertEqual(type(self.stats_httpd.httpd), list) - self.assertEqual(len(self.stats_httpd.httpd), 0) + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) self.stats_httpd.close_httpd() # over flow of port number self.stats_httpd.http_addrs = [ ('', 80000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - # negative self.stats_httpd.http_addrs = [ ('', -8000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - # alphabet self.stats_httpd.http_addrs = [ ('', 'ABCDE') ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - # Address already in use - self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.statshttpd_server.server.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) - self.statshttpd_server.run() - self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) - self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - self.statshttpd_server.shutdown() - - def test_running(self): - self.assertFalse(self.stats_httpd.running) - self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.stats_httpd = self.statshttpd_server.server - self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65455 }]}) - self.statshttpd_server.run() - self.assertTrue(self.stats_httpd.running) - self.statshttpd_server.shutdown() - self.assertFalse(self.stats_httpd.running) - - # failure case + def test_start(self): + self.stats_httpd.cc_session.group_sendmsg( + { 'command': [ "shutdown" ] }, "StatsHttpd") + self.stats_httpd.start() self.stats_httpd = stats_httpd.StatsHttpd() - self.stats_httpd.cc_session.close() self.assertRaises( - isc.cc.session.SessionError, self.stats_httpd.start) + fake_select.error, self.stats_httpd.start) - def test_select_failure(self): - def raise_select_except(*args): - raise select.error('dummy error') - def raise_select_except_with_errno(*args): - raise select.error(errno.EINTR) - (address, port) = ('127.0.0.1', 65456) - stats_httpd.select.select = raise_select_except - statshttpd = stats_httpd.StatsHttpd() - statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - self.assertRaises(select.error, statshttpd.start) - statshttpd.stop() - stats_httpd.select.select = raise_select_except_with_errno - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - statshttpd = statshttpd_server.server - statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - statshttpd_server.run() - statshttpd_server.shutdown() + def test_stop(self): + # success case + fake_socket._CLOSED = False + self.stats_httpd.stop() + self.assertFalse(self.stats_httpd.running) + self.assertIsNone(self.stats_httpd.mccs) + for ht in self.stats_httpd.httpd: + self.assertTrue(ht.socket._closed) + self.assertTrue(self.stats_httpd.cc_session._socket._closed) + # failure case + self.stats_httpd.cc_session._socket._closed = False + self.stats_httpd.open_mccs() + self.stats_httpd.cc_session._socket._closed = True + self.stats_httpd.stop() # No excetion raises + self.stats_httpd.cc_session._socket._closed = False def test_open_template(self): # successful conditions @@ -509,40 +363,38 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual( self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)), isc.config.ccsession.create_answer( - 1, "Unknown known config: _UNKNOWN_KEY_")) - + 1, "Unknown known config: _UNKNOWN_KEY_")) self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="127.0.0.1",port=8000)])), + dict(listen_on=[dict(address="::2",port=8000)])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "127.0.0.1") + self.assertTrue(addr["address"] == "::2") self.assertTrue(addr["port"] == 8000) - if self.ipv6_enabled: - self.assertEqual( - self.stats_httpd.config_handler( - dict(listen_on=[dict(address="::1",port=8000)])), - isc.config.ccsession.create_answer(0)) - self.assertTrue("listen_on" in self.stats_httpd.config) - for addr in self.stats_httpd.config["listen_on"]: - self.assertTrue("address" in addr) - self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "::1") - self.assertTrue(addr["port"] == 8000) - self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="127.0.0.1",port=54321)])), + dict(listen_on=[dict(address="::1",port=80)])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "127.0.0.1") + self.assertTrue(addr["address"] == "::1") + self.assertTrue(addr["port"] == 80) + + self.assertEqual( + self.stats_httpd.config_handler( + dict(listen_on=[dict(address="1.2.3.4",port=54321)])), + isc.config.ccsession.create_answer(0)) + self.assertTrue("listen_on" in self.stats_httpd.config) + for addr in self.stats_httpd.config["listen_on"]: + self.assertTrue("address" in addr) + self.assertTrue("port" in addr) + self.assertTrue(addr["address"] == "1.2.3.4") self.assertTrue(addr["port"] == 54321) (ret, arg) = isc.config.ccsession.parse_answer( self.stats_httpd.config_handler( @@ -552,11 +404,10 @@ class TestStatsHttpd(unittest.TestCase): def test_xml_handler(self): orig_get_stats_data = stats_httpd.StatsHttpd.get_stats_data - stats_httpd.StatsHttpd.get_stats_data = lambda x: \ - { 'Dummy' : { 'foo':'bar' } } + stats_httpd.StatsHttpd.get_stats_data = lambda x: {'foo':'bar'} xml_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XML_TEMPLATE_LOCATION).substitute( - xml_string='bar', + xml_string='bar', xsd_namespace=stats_httpd.XSD_NAMESPACE, xsd_url_path=stats_httpd.XSD_URL_PATH, xsl_url_path=stats_httpd.XSL_URL_PATH) @@ -564,8 +415,7 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual(type(xml_body1), str) self.assertEqual(type(xml_body2), str) self.assertEqual(xml_body1, xml_body2) - stats_httpd.StatsHttpd.get_stats_data = lambda x: \ - { 'Dummy' : {'bar':'foo'} } + stats_httpd.StatsHttpd.get_stats_data = lambda x: {'bar':'foo'} xml_body2 = stats_httpd.StatsHttpd().xml_handler() self.assertNotEqual(xml_body1, xml_body2) stats_httpd.StatsHttpd.get_stats_data = orig_get_stats_data @@ -573,41 +423,35 @@ class TestStatsHttpd(unittest.TestCase): def test_xsd_handler(self): orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - { "Dummy" : - [{ - "item_name": "foo", - "item_type": "string", - "item_optional": False, - "item_default": "bar", - "item_description": "foo is bar", - "item_title": "Foo" - }] - } + [{ + "item_name": "foo", + "item_type": "string", + "item_optional": False, + "item_default": "bar", + "item_description": "foo is bar", + "item_title": "Foo" + }] xsd_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XSD_TEMPLATE_LOCATION).substitute( - xsd_string=\ - '' \ + xsd_string='' \ + '' \ + 'Foo' \ + 'foo is bar' \ - + '' \ - + '', + + '', xsd_namespace=stats_httpd.XSD_NAMESPACE) xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() self.assertEqual(type(xsd_body1), str) self.assertEqual(type(xsd_body2), str) self.assertEqual(xsd_body1, xsd_body2) stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - { "Dummy" : - [{ - "item_name": "bar", - "item_type": "string", - "item_optional": False, - "item_default": "foo", - "item_description": "bar is foo", - "item_title": "bar" - }] - } + [{ + "item_name": "bar", + "item_type": "string", + "item_optional": False, + "item_default": "foo", + "item_description": "bar is foo", + "item_title": "bar" + }] xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() self.assertNotEqual(xsd_body1, xsd_body2) stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec @@ -615,22 +459,19 @@ class TestStatsHttpd(unittest.TestCase): def test_xsl_handler(self): orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - { "Dummy" : - [{ - "item_name": "foo", - "item_type": "string", - "item_optional": False, - "item_default": "bar", - "item_description": "foo is bar", - "item_title": "Foo" - }] - } + [{ + "item_name": "foo", + "item_type": "string", + "item_optional": False, + "item_default": "bar", + "item_description": "foo is bar", + "item_title": "Foo" + }] xsl_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XSL_TEMPLATE_LOCATION).substitute( xsl_string='' \ - + '' \ + '' \ - + '' \ + + '' \ + '', xsd_namespace=stats_httpd.XSD_NAMESPACE) xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() @@ -638,16 +479,14 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual(type(xsl_body2), str) self.assertEqual(xsl_body1, xsl_body2) stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - { "Dummy" : - [{ - "item_name": "bar", - "item_type": "string", - "item_optional": False, - "item_default": "foo", - "item_description": "bar is foo", - "item_title": "bar" - }] - } + [{ + "item_name": "bar", + "item_type": "string", + "item_optional": False, + "item_default": "foo", + "item_description": "bar is foo", + "item_title": "bar" + }] xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() self.assertNotEqual(xsl_body1, xsl_body2) stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec @@ -661,6 +500,8 @@ class TestStatsHttpd(unittest.TestCase): imp.reload(stats_httpd) os.environ["B10_FROM_SOURCE"] = tmppath imp.reload(stats_httpd) + stats_httpd.socket = fake_socket + stats_httpd.select = fake_select if __name__ == "__main__": unittest.main() diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 7cf4f7ede0..a42c81d136 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -13,582 +13,649 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -""" -In each of these tests we start several virtual components. They are -not the real components, no external processes are started. They are -just simple mock objects running each in its own thread and pretending -to be bind10 modules. This helps testing the stats module in a close -to real environment. -""" - -import unittest +# +# Tests for the stats module +# import os -import threading -import io +import sys import time +import unittest import imp - +from isc.cc.session import Session, SessionError +from isc.config.ccsession import ModuleCCSession, ModuleCCSessionError +from fake_time import time, strftime, gmtime import stats -import isc.cc.session -from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, TIMEOUT_SEC +stats.time = time +stats.strftime = strftime +stats.gmtime = gmtime +from stats import SessionSubject, CCSessionListener, get_timestamp, get_datetime +from fake_time import _TEST_TIME_SECS, _TEST_TIME_STRF -# set test name for logger -isc.log.init("b10-stats_test") - -class TestUtilties(unittest.TestCase): - items = [ - { 'item_name': 'test_int1', 'item_type': 'integer', 'item_default': 12345 }, - { 'item_name': 'test_real1', 'item_type': 'real', 'item_default': 12345.6789 }, - { 'item_name': 'test_bool1', 'item_type': 'boolean', 'item_default': True }, - { 'item_name': 'test_str1', 'item_type': 'string', 'item_default': 'ABCD' }, - { 'item_name': 'test_list1', 'item_type': 'list', 'item_default': [1,2,3], - 'list_item_spec' : [ { 'item_name': 'one', 'item_type': 'integer' }, - { 'item_name': 'two', 'item_type': 'integer' }, - { 'item_name': 'three', 'item_type': 'integer' } ] }, - { 'item_name': 'test_map1', 'item_type': 'map', 'item_default': {'a':1,'b':2,'c':3}, - 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'integer'}, - { 'item_name': 'b', 'item_type': 'integer'}, - { 'item_name': 'c', 'item_type': 'integer'} ] }, - { 'item_name': 'test_int2', 'item_type': 'integer' }, - { 'item_name': 'test_real2', 'item_type': 'real' }, - { 'item_name': 'test_bool2', 'item_type': 'boolean' }, - { 'item_name': 'test_str2', 'item_type': 'string' }, - { 'item_name': 'test_list2', 'item_type': 'list', - 'list_item_spec' : [ { 'item_name': 'one', 'item_type': 'integer' }, - { 'item_name': 'two', 'item_type': 'integer' }, - { 'item_name': 'three', 'item_type': 'integer' } ] }, - { 'item_name': 'test_map2', 'item_type': 'map', - 'map_item_spec' : [ { 'item_name': 'A', 'item_type': 'integer'}, - { 'item_name': 'B', 'item_type': 'integer'}, - { 'item_name': 'C', 'item_type': 'integer'} ] }, - { 'item_name': 'test_none', 'item_type': 'none' } - ] - - def setUp(self): - self.const_timestamp = 1308730448.965706 - self.const_timetuple = (2011, 6, 22, 8, 14, 8, 2, 173, 0) - self.const_datetime = '2011-06-22T08:14:08Z' - stats.time = lambda : self.const_timestamp - stats.gmtime = lambda : self.const_timetuple - - def test_get_spec_defaults(self): - self.assertEqual( - stats.get_spec_defaults(self.items), { - 'test_int1' : 12345 , - 'test_real1' : 12345.6789 , - 'test_bool1' : True , - 'test_str1' : 'ABCD' , - 'test_list1' : [1,2,3] , - 'test_map1' : {'a':1,'b':2,'c':3}, - 'test_int2' : 0 , - 'test_real2' : 0.0, - 'test_bool2' : False, - 'test_str2' : "", - 'test_list2' : [0,0,0], - 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 }, - 'test_none' : None }) - self.assertEqual(stats.get_spec_defaults(None), {}) - self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}]) - - def test_get_timestamp(self): - self.assertEqual(stats.get_timestamp(), self.const_timestamp) - - def test_get_datetime(self): - self.assertEqual(stats.get_datetime(), self.const_datetime) - self.assertNotEqual(stats.get_datetime( - (2011, 6, 22, 8, 23, 40, 2, 173, 0)), self.const_datetime) - -class TestCallback(unittest.TestCase): - def setUp(self): - self.dummy_func = lambda *x, **y : (x, y) - self.dummy_args = (1,2,3) - self.dummy_kwargs = {'a':1,'b':2,'c':3} - self.cback1 = stats.Callback( - command=self.dummy_func, - args=self.dummy_args, - kwargs=self.dummy_kwargs - ) - self.cback2 = stats.Callback( - args=self.dummy_args, - kwargs=self.dummy_kwargs - ) - self.cback3 = stats.Callback( - command=self.dummy_func, - kwargs=self.dummy_kwargs - ) - self.cback4 = stats.Callback( - command=self.dummy_func, - args=self.dummy_args - ) - - def test_init(self): - self.assertEqual((self.cback1.command, self.cback1.args, self.cback1.kwargs), - (self.dummy_func, self.dummy_args, self.dummy_kwargs)) - self.assertEqual((self.cback2.command, self.cback2.args, self.cback2.kwargs), - (None, self.dummy_args, self.dummy_kwargs)) - self.assertEqual((self.cback3.command, self.cback3.args, self.cback3.kwargs), - (self.dummy_func, (), self.dummy_kwargs)) - self.assertEqual((self.cback4.command, self.cback4.args, self.cback4.kwargs), - (self.dummy_func, self.dummy_args, {})) - - def test_call(self): - self.assertEqual(self.cback1(), (self.dummy_args, self.dummy_kwargs)) - self.assertEqual(self.cback1(100, 200), ((100, 200), self.dummy_kwargs)) - self.assertEqual(self.cback1(a=100, b=200), (self.dummy_args, {'a':100, 'b':200})) - self.assertEqual(self.cback2(), None) - self.assertEqual(self.cback3(), ((), self.dummy_kwargs)) - self.assertEqual(self.cback3(100, 200), ((100, 200), self.dummy_kwargs)) - self.assertEqual(self.cback3(a=100, b=200), ((), {'a':100, 'b':200})) - self.assertEqual(self.cback4(), (self.dummy_args, {})) - self.assertEqual(self.cback4(100, 200), ((100, 200), {})) - self.assertEqual(self.cback4(a=100, b=200), (self.dummy_args, {'a':100, 'b':200})) +if "B10_FROM_SOURCE" in os.environ: + TEST_SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] +\ + "/src/bin/stats/tests/testdata/stats_test.spec" +else: + TEST_SPECFILE_LOCATION = "./testdata/stats_test.spec" class TestStats(unittest.TestCase): + def setUp(self): - self.base = BaseModules() - self.stats = stats.Stats() - self.const_timestamp = 1308730448.965706 - self.const_datetime = '2011-06-22T08:14:08Z' - self.const_default_datetime = '1970-01-01T00:00:00Z' + self.session = Session() + self.subject = SessionSubject(session=self.session) + self.listener = CCSessionListener(self.subject) + self.stats_spec = self.listener.cc_session.get_module_spec().get_config_spec() + self.module_name = self.listener.cc_session.get_module_spec().get_module_name() + self.stats_data = { + 'report_time' : get_datetime(), + 'bind10.boot_time' : "1970-01-01T00:00:00Z", + 'stats.timestamp' : get_timestamp(), + 'stats.lname' : self.session.lname, + 'auth.queries.tcp': 0, + 'auth.queries.udp': 0, + "stats.boot_time": get_datetime(), + "stats.start_time": get_datetime(), + "stats.last_update_time": get_datetime() + } + # check starting + self.assertFalse(self.subject.running) + self.subject.start() + self.assertTrue(self.subject.running) + self.assertEqual(len(self.session.message_queue), 0) + self.assertEqual(self.module_name, 'Stats') def tearDown(self): - self.base.shutdown() + # check closing + self.subject.stop() + self.assertFalse(self.subject.running) + self.subject.detach(self.listener) + self.listener.stop() + self.session.close() - def test_init(self): - self.assertEqual(self.stats.module_name, 'Stats') - self.assertFalse(self.stats.running) - self.assertTrue('command_show' in self.stats.callbacks) - self.assertTrue('command_status' in self.stats.callbacks) - self.assertTrue('command_shutdown' in self.stats.callbacks) - self.assertTrue('command_show' in self.stats.callbacks) - self.assertTrue('command_showschema' in self.stats.callbacks) - self.assertTrue('command_set' in self.stats.callbacks) + def test_local_func(self): + """ + Test for local function + + """ + # test for result_ok + self.assertEqual(type(result_ok()), dict) + self.assertEqual(result_ok(), {'result': [0]}) + self.assertEqual(result_ok(1), {'result': [1]}) + self.assertEqual(result_ok(0,'OK'), {'result': [0, 'OK']}) + self.assertEqual(result_ok(1,'Not good'), {'result': [1, 'Not good']}) + self.assertEqual(result_ok(None,"It's None"), {'result': [None, "It's None"]}) + self.assertNotEqual(result_ok(), {'RESULT': [0]}) - def test_init_undefcmd(self): - spec_str = """\ -{ - "module_spec": { - "module_name": "Stats", - "module_description": "Stats daemon", - "config_data": [], - "commands": [ - { - "command_name": "_undef_command_", - "command_description": "a undefined command in stats", - "command_args": [] - } - ], - "statistics": [] - } -} -""" - orig_spec_location = stats.SPECFILE_LOCATION - stats.SPECFILE_LOCATION = io.StringIO(spec_str) - self.assertRaises(stats.StatsError, stats.Stats) - stats.SPECFILE_LOCATION = orig_spec_location + # test for get_timestamp + self.assertEqual(get_timestamp(), _TEST_TIME_SECS) - def test_start(self): - # start without err - statsserver = ThreadingServerManager(MyStats) - statsd = statsserver.server - self.assertFalse(statsd.running) - statsserver.run() - self.assertTrue(statsd.running) - statsserver.shutdown() - self.assertFalse(statsd.running) + # test for get_datetime + self.assertEqual(get_datetime(), _TEST_TIME_STRF) - # start with err - statsd = stats.Stats() - statsd.update_statistics_data = lambda x,**y: ['an error'] - self.assertRaises(stats.StatsError, statsd.start) + def test_show_command(self): + """ + Test for show command + + """ + # test show command without arg + self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + # ignore under 0.9 seconds + self.assertEqual(result_ok(0, self.stats_data), result_data) + self.assertEqual(len(self.session.message_queue), 0) - def test_handlers(self): - # config_handler - self.assertEqual(self.stats.config_handler({'foo':'bar'}), - isc.config.create_answer(0)) + # test show command with arg + self.session.group_sendmsg({"command": [ "show", {"stats_item_name": "stats.lname"}]}, "Stats") + self.assertEqual(len(self.subject.session.message_queue), 1) + self.subject.check() + result_data = self.subject.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'stats.lname': self.stats_data['stats.lname']}), + result_data) + self.assertEqual(len(self.subject.session.message_queue), 0) - # command_handler - statsserver = ThreadingServerManager(MyStats) - statsserver.run() + # test show command with arg which has wrong name + self.session.group_sendmsg({"command": [ "show", {"stats_item_name": "stats.dummy"}]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + # ignore under 0.9 seconds + self.assertEqual(result_ok(0, self.stats_data), result_data) + self.assertEqual(len(self.session.message_queue), 0) + + def test_set_command(self): + """ + Test for set command + + """ + # test set command + self.stats_data['auth.queries.udp'] = 54321 + self.assertEqual(self.stats_data['auth.queries.udp'], 54321) + self.assertEqual(self.stats_data['auth.queries.tcp'], 0) + self.session.group_sendmsg({ "command": [ + "set", { + 'stats_data': {'auth.queries.udp': 54321 } + } ] }, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # test show command + self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, self.stats_data), result_data) + self.assertEqual(len(self.session.message_queue), 0) + + # test set command 2 + self.stats_data['auth.queries.udp'] = 0 + self.assertEqual(self.stats_data['auth.queries.udp'], 0) + self.assertEqual(self.stats_data['auth.queries.tcp'], 0) + self.session.group_sendmsg({ "command": [ "set", {'stats_data': {'auth.queries.udp': 0}} ]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # test show command 2 + self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, self.stats_data), result_data) + self.assertEqual(len(self.session.message_queue), 0) + + # test set command 3 + self.stats_data['auth.queries.tcp'] = 54322 + self.assertEqual(self.stats_data['auth.queries.udp'], 0) + self.assertEqual(self.stats_data['auth.queries.tcp'], 54322) + self.session.group_sendmsg({ "command": [ + "set", { + 'stats_data': {'auth.queries.tcp': 54322 } + } ] }, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # test show command 3 + self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, self.stats_data), result_data) + self.assertEqual(len(self.session.message_queue), 0) + + def test_remove_command(self): + """ + Test for remove command + + """ + self.session.group_sendmsg({"command": + [ "remove", {"stats_item_name": 'bind10.boot_time' }]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + self.assertEqual(self.stats_data.pop('bind10.boot_time'), "1970-01-01T00:00:00Z") + self.assertFalse('bind10.boot_time' in self.stats_data) + + # test show command with arg + self.session.group_sendmsg({"command": + [ "show", {"stats_item_name": 'bind10.boot_time'}]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertFalse('bind10.boot_time' in result_data['result'][1]) + self.assertEqual(result_ok(0, self.stats_data), result_data) + self.assertEqual(len(self.session.message_queue), 0) + + def test_reset_command(self): + """ + Test for reset command + + """ + self.session.group_sendmsg({"command": [ "reset" ] }, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # test show command + self.session.group_sendmsg({"command": [ "show" ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, self.stats_data), result_data) + self.assertEqual(len(self.session.message_queue), 0) + + def test_status_command(self): + """ + Test for status command + + """ + self.session.group_sendmsg({"command": [ "status" ] }, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(0, "I'm alive."), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + def test_unknown_command(self): + """ + Test for unknown command + + """ + self.session.group_sendmsg({"command": [ "hoge", None ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(1, "Unknown command: 'hoge'"), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + def test_shutdown_command(self): + """ + Test for shutdown command + + """ + self.session.group_sendmsg({"command": [ "shutdown", None ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.assertTrue(self.subject.running) + self.subject.check() + self.assertFalse(self.subject.running) + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + + def test_some_commands(self): + """ + Test for some commands in a row + + """ + # test set command + self.stats_data['bind10.boot_time'] = '2010-08-02T14:47:56Z' + self.assertEqual(self.stats_data['bind10.boot_time'], '2010-08-02T14:47:56Z') + self.session.group_sendmsg({ "command": [ + "set", { + 'stats_data': {'bind10.boot_time': '2010-08-02T14:47:56Z' } + }]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # check its value + self.session.group_sendmsg({ "command": [ + "show", { 'stats_item_name': 'bind10.boot_time' } + ] }, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'bind10.boot_time': '2010-08-02T14:47:56Z'}), + result_data) + self.assertEqual(result_ok(0, {'bind10.boot_time': self.stats_data['bind10.boot_time']}), + result_data) + self.assertEqual(len(self.session.message_queue), 0) + + # test set command 2nd + self.stats_data['auth.queries.udp'] = 98765 + self.assertEqual(self.stats_data['auth.queries.udp'], 98765) + self.session.group_sendmsg({ "command": [ + "set", { 'stats_data': { + 'auth.queries.udp': + self.stats_data['auth.queries.udp'] + } } + ] }, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # check its value + self.session.group_sendmsg({"command": [ + "show", {'stats_item_name': 'auth.queries.udp'} + ] }, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'auth.queries.udp': 98765}), + result_data) + self.assertEqual(result_ok(0, {'auth.queries.udp': self.stats_data['auth.queries.udp']}), + result_data) + self.assertEqual(len(self.session.message_queue), 0) + + # test set command 3 + self.stats_data['auth.queries.tcp'] = 4321 + self.session.group_sendmsg({"command": [ + "set", + {'stats_data': {'auth.queries.tcp': 4321 }} ]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # check value + self.session.group_sendmsg({"command": [ "show", {'stats_item_name': 'auth.queries.tcp'} ]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'auth.queries.tcp': 4321}), + result_data) + self.assertEqual(result_ok(0, {'auth.queries.tcp': self.stats_data['auth.queries.tcp']}), + result_data) + self.assertEqual(len(self.session.message_queue), 0) + + self.session.group_sendmsg({"command": [ "show", {'stats_item_name': 'auth.queries.udp'} ]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'auth.queries.udp': 98765}), + result_data) + self.assertEqual(result_ok(0, {'auth.queries.udp': self.stats_data['auth.queries.udp']}), + result_data) + self.assertEqual(len(self.session.message_queue), 0) + + # test set command 4 + self.stats_data['auth.queries.tcp'] = 67890 + self.session.group_sendmsg({"command": [ + "set", {'stats_data': {'auth.queries.tcp': 67890 }} ]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # test show command for all values + self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, self.stats_data), result_data) + self.assertEqual(len(self.session.message_queue), 0) + + def test_some_commands2(self): + """ + Test for some commands in a row using list-type value + + """ + self.stats_data['listtype'] = [1, 2, 3] + self.assertEqual(self.stats_data['listtype'], [1, 2, 3]) + self.session.group_sendmsg({ "command": [ + "set", {'stats_data': {'listtype': [1, 2, 3] }} + ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # check its value + self.session.group_sendmsg({ "command": [ + "show", { 'stats_item_name': 'listtype'} + ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'listtype': [1, 2, 3]}), + result_data) + self.assertEqual(result_ok(0, {'listtype': self.stats_data['listtype']}), + result_data) + self.assertEqual(len(self.session.message_queue), 0) + + # test set list-type value + self.assertEqual(self.stats_data['listtype'], [1, 2, 3]) + self.session.group_sendmsg({"command": [ + "set", {'stats_data': {'listtype': [3, 2, 1, 0] }} + ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # check its value + self.session.group_sendmsg({ "command": [ + "show", { 'stats_item_name': 'listtype' } + ] }, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'listtype': [3, 2, 1, 0]}), + result_data) + self.assertEqual(len(self.session.message_queue), 0) + + def test_some_commands3(self): + """ + Test for some commands in a row using dictionary-type value + + """ + self.stats_data['dicttype'] = {"a": 1, "b": 2, "c": 3} + self.assertEqual(self.stats_data['dicttype'], {"a": 1, "b": 2, "c": 3}) + self.session.group_sendmsg({ "command": [ + "set", { + 'stats_data': {'dicttype': {"a": 1, "b": 2, "c": 3} } + }]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # check its value + self.session.group_sendmsg({ "command": [ "show", { 'stats_item_name': 'dicttype' } ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'dicttype': {"a": 1, "b": 2, "c": 3}}), + result_data) + self.assertEqual(result_ok(0, {'dicttype': self.stats_data['dicttype']}), + result_data) + self.assertEqual(len(self.session.message_queue), 0) + + # test set list-type value + self.assertEqual(self.stats_data['dicttype'], {"a": 1, "b": 2, "c": 3}) + self.session.group_sendmsg({"command": [ + "set", {'stats_data': {'dicttype': {"a": 3, "b": 2, "c": 1, "d": 0} }} ]}, + "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + self.assertEqual(len(self.session.message_queue), 0) + + # check its value + self.session.group_sendmsg({ "command": [ "show", { 'stats_item_name': 'dicttype' }]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + result_data = self.session.get_message("Stats", None) + self.assertEqual(result_ok(0, {'dicttype': {"a": 3, "b": 2, "c": 1, "d": 0} }), + result_data) + self.assertEqual(len(self.session.message_queue), 0) + + def test_config_update(self): + """ + Test for config update + + """ + # test show command without arg + self.session.group_sendmsg({"command": [ "config_update", {"x-version":999} ]}, "Stats") + self.assertEqual(len(self.session.message_queue), 1) + self.subject.check() + self.assertEqual(result_ok(), + self.session.get_message("Stats", None)) + + def test_for_boss(self): + last_queue = self.session.old_message_queue.pop() self.assertEqual( - send_command( - 'show', 'Stats', - params={ 'owner' : 'Boss', - 'name' : 'boot_time' }), - (0, self.const_datetime)) + last_queue.msg, {'command': ['sendstats']}) self.assertEqual( - send_command( - 'set', 'Stats', - params={ 'owner' : 'Boss', - 'data' : { 'boot_time' : self.const_datetime } }), - (0, None)) - self.assertEqual( - send_command( - 'show', 'Stats', - params={ 'owner' : 'Boss', - 'name' : 'boot_time' }), - (0, self.const_datetime)) - self.assertEqual( - send_command('status', 'Stats'), - (0, "Stats is up. (PID " + str(os.getpid()) + ")")) + last_queue.env['group'], 'Boss') - (rcode, value) = send_command('show', 'Stats') - self.assertEqual(rcode, 0) - self.assertEqual(len(value), 3) - self.assertTrue('Boss' in value) - self.assertTrue('Stats' in value) - self.assertTrue('Auth' in value) - self.assertEqual(len(value['Stats']), 5) - self.assertEqual(len(value['Boss']), 1) - self.assertTrue('boot_time' in value['Boss']) - self.assertEqual(value['Boss']['boot_time'], self.const_datetime) - self.assertTrue('report_time' in value['Stats']) - self.assertTrue('boot_time' in value['Stats']) - self.assertTrue('last_update_time' in value['Stats']) - self.assertTrue('timestamp' in value['Stats']) - self.assertTrue('lname' in value['Stats']) - (rcode, value) = send_command('showschema', 'Stats') - self.assertEqual(rcode, 0) - self.assertEqual(len(value), 3) - self.assertTrue('Boss' in value) - self.assertTrue('Stats' in value) - self.assertTrue('Auth' in value) - self.assertEqual(len(value['Stats']), 5) - self.assertEqual(len(value['Boss']), 1) - for item in value['Boss']: - self.assertTrue(len(item) == 7) - self.assertTrue('item_name' in item) - self.assertTrue('item_type' in item) - self.assertTrue('item_optional' in item) - self.assertTrue('item_default' in item) - self.assertTrue('item_title' in item) - self.assertTrue('item_description' in item) - self.assertTrue('item_format' in item) - for item in value['Stats']: - self.assertTrue(len(item) == 6 or len(item) == 7) - self.assertTrue('item_name' in item) - self.assertTrue('item_type' in item) - self.assertTrue('item_optional' in item) - self.assertTrue('item_default' in item) - self.assertTrue('item_title' in item) - self.assertTrue('item_description' in item) - if len(item) == 7: - self.assertTrue('item_format' in item) +class TestStats2(unittest.TestCase): - self.assertEqual( - send_command('__UNKNOWN__', 'Stats'), - (1, "Unknown command: '__UNKNOWN__'")) + def setUp(self): + self.session = Session() + self.subject = SessionSubject(session=self.session) + self.listener = CCSessionListener(self.subject) + self.module_name = self.listener.cc_session.get_module_spec().get_module_name() + # check starting + self.assertFalse(self.subject.running) + self.subject.start() + self.assertTrue(self.subject.running) + self.assertEqual(len(self.session.message_queue), 0) + self.assertEqual(self.module_name, 'Stats') - statsserver.shutdown() + def tearDown(self): + # check closing + self.subject.stop() + self.assertFalse(self.subject.running) + self.subject.detach(self.listener) + self.listener.stop() - def test_update_modules(self): - self.assertEqual(len(self.stats.modules), 0) - self.stats.update_modules() - self.assertTrue('Stats' in self.stats.modules) - self.assertTrue('Boss' in self.stats.modules) - self.assertFalse('Dummy' in self.stats.modules) - my_statistics_data = stats.get_spec_defaults(self.stats.modules['Stats'].get_statistics_spec()) - self.assertTrue('report_time' in my_statistics_data) - self.assertTrue('boot_time' in my_statistics_data) - self.assertTrue('last_update_time' in my_statistics_data) - self.assertTrue('timestamp' in my_statistics_data) - self.assertTrue('lname' in my_statistics_data) - self.assertEqual(my_statistics_data['report_time'], self.const_default_datetime) - self.assertEqual(my_statistics_data['boot_time'], self.const_default_datetime) - self.assertEqual(my_statistics_data['last_update_time'], self.const_default_datetime) - self.assertEqual(my_statistics_data['timestamp'], 0.0) - self.assertEqual(my_statistics_data['lname'], "") - my_statistics_data = stats.get_spec_defaults(self.stats.modules['Boss'].get_statistics_spec()) - self.assertTrue('boot_time' in my_statistics_data) - self.assertEqual(my_statistics_data['boot_time'], self.const_default_datetime) - orig_parse_answer = stats.isc.config.ccsession.parse_answer - stats.isc.config.ccsession.parse_answer = lambda x: (99, 'error') - self.assertRaises(stats.StatsError, self.stats.update_modules) - stats.isc.config.ccsession.parse_answer = orig_parse_answer + def test_specfile(self): + """ + Test for specfile + + """ + if "B10_FROM_SOURCE" in os.environ: + self.assertEqual(stats.SPECFILE_LOCATION, + os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + \ + os.sep + "stats.spec") + self.assertEqual(stats.SCHEMA_SPECFILE_LOCATION, + os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + \ + os.sep + "stats-schema.spec") + imp.reload(stats) + # change path of SPECFILE_LOCATION + stats.SPECFILE_LOCATION = TEST_SPECFILE_LOCATION + stats.SCHEMA_SPECFILE_LOCATION = TEST_SPECFILE_LOCATION + self.assertEqual(stats.SPECFILE_LOCATION, TEST_SPECFILE_LOCATION) + self.subject = stats.SessionSubject(session=self.session) + self.session = self.subject.session + self.listener = stats.CCSessionListener(self.subject) - def test_get_statistics_data(self): - my_statistics_data = self.stats.get_statistics_data() - self.assertTrue('Stats' in my_statistics_data) - self.assertTrue('Boss' in my_statistics_data) - my_statistics_data = self.stats.get_statistics_data(owner='Stats') - self.assertTrue('report_time' in my_statistics_data) - self.assertTrue('boot_time' in my_statistics_data) - self.assertTrue('last_update_time' in my_statistics_data) - self.assertTrue('timestamp' in my_statistics_data) - self.assertTrue('lname' in my_statistics_data) - self.assertRaises(stats.StatsError, self.stats.get_statistics_data, owner='Foo') - my_statistics_data = self.stats.get_statistics_data(owner='Stats') - self.assertTrue('boot_time' in my_statistics_data) - my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='report_time') - self.assertEqual(my_statistics_data, self.const_default_datetime) - my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='boot_time') - self.assertEqual(my_statistics_data, self.const_default_datetime) - my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='last_update_time') - self.assertEqual(my_statistics_data, self.const_default_datetime) - my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='timestamp') - self.assertEqual(my_statistics_data, 0.0) - my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='lname') - self.assertEqual(my_statistics_data, '') - self.assertRaises(stats.StatsError, self.stats.get_statistics_data, - owner='Stats', name='Bar') - self.assertRaises(stats.StatsError, self.stats.get_statistics_data, - owner='Foo', name='Bar') - self.assertRaises(stats.StatsError, self.stats.get_statistics_data, - name='Bar') + self.assertEqual(self.listener.stats_spec, []) + self.assertEqual(self.listener.stats_data, {}) - def test_update_statistics_data(self): - self.stats.update_statistics_data(owner='Stats', lname='foo@bar') - self.assertTrue('Stats' in self.stats.statistics_data) - my_statistics_data = self.stats.statistics_data['Stats'] - self.assertEqual(my_statistics_data['lname'], 'foo@bar') - self.stats.update_statistics_data(owner='Stats', last_update_time=self.const_datetime) - self.assertTrue('Stats' in self.stats.statistics_data) - my_statistics_data = self.stats.statistics_data['Stats'] - self.assertEqual(my_statistics_data['last_update_time'], self.const_datetime) - self.assertEqual(self.stats.update_statistics_data(owner='Stats', lname=0.0), - ['0.0 should be a string']) - self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), - ['unknown module name: Dummy']) + self.assertEqual(self.listener.commands_spec, [ + { + "command_name": "status", + "command_description": "identify whether stats module is alive or not", + "command_args": [] + }, + { + "command_name": "the_dummy", + "command_description": "this is for testing", + "command_args": [] + }]) - def test_commands(self): - # status - self.assertEqual(self.stats.command_status(), - isc.config.create_answer( - 0, "Stats is up. (PID " + str(os.getpid()) + ")")) + def test_func_initialize_data(self): + """ + Test for initialize_data function + + """ + # prepare for sample data set + stats_spec = [ + { + "item_name": "none_sample", + "item_type": "null", + "item_default": "None" + }, + { + "item_name": "boolean_sample", + "item_type": "boolean", + "item_default": True + }, + { + "item_name": "string_sample", + "item_type": "string", + "item_default": "A something" + }, + { + "item_name": "int_sample", + "item_type": "integer", + "item_default": 9999999 + }, + { + "item_name": "real_sample", + "item_type": "real", + "item_default": 0.0009 + }, + { + "item_name": "list_sample", + "item_type": "list", + "item_default": [0, 1, 2, 3, 4], + "list_item_spec": [] + }, + { + "item_name": "map_sample", + "item_type": "map", + "item_default": {'name':'value'}, + "map_item_spec": [] + }, + { + "item_name": "other_sample", + "item_type": "__unknown__", + "item_default": "__unknown__" + } + ] + # data for comparison + stats_data = { + 'none_sample': None, + 'boolean_sample': True, + 'string_sample': 'A something', + 'int_sample': 9999999, + 'real_sample': 0.0009, + 'list_sample': [0, 1, 2, 3, 4], + 'map_sample': {'name':'value'}, + 'other_sample': '__unknown__' + } + self.assertEqual(self.listener.initialize_data(stats_spec), stats_data) - # shutdown - self.stats.running = True - self.assertEqual(self.stats.command_shutdown(), - isc.config.create_answer(0)) - self.assertFalse(self.stats.running) + def test_func_main(self): + # explicitly make failed + self.session.close() + stats.main(session=self.session) - def test_command_show(self): - self.assertEqual(self.stats.command_show(owner='Foo', name=None), - isc.config.create_answer( - 1, "specified arguments are incorrect: owner: Foo, name: None")) - self.assertEqual(self.stats.command_show(owner='Foo', name='_bar_'), - isc.config.create_answer( - 1, "specified arguments are incorrect: owner: Foo, name: _bar_")) - self.assertEqual(self.stats.command_show(owner='Foo', name='bar'), - isc.config.create_answer( - 1, "specified arguments are incorrect: owner: Foo, name: bar")) - self.assertEqual(self.stats.command_show(owner='Auth'), - isc.config.create_answer( - 0, {'queries.tcp': 0, 'queries.udp': 0})) - self.assertEqual(self.stats.command_show(owner='Auth', name='queries.udp'), - isc.config.create_answer( - 0, 0)) - orig_get_timestamp = stats.get_timestamp - orig_get_datetime = stats.get_datetime - stats.get_timestamp = lambda : self.const_timestamp - stats.get_datetime = lambda : self.const_datetime - self.assertEqual(stats.get_timestamp(), self.const_timestamp) - self.assertEqual(stats.get_datetime(), self.const_datetime) - self.assertEqual(self.stats.command_show(owner='Stats', name='report_time'), \ - isc.config.create_answer(0, self.const_datetime)) - self.assertEqual(self.stats.statistics_data['Stats']['timestamp'], self.const_timestamp) - self.assertEqual(self.stats.statistics_data['Stats']['boot_time'], self.const_default_datetime) - stats.get_timestamp = orig_get_timestamp - stats.get_datetime = orig_get_datetime - self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( - { "module_name": self.stats.module_name, - "statistics": [] } ) - self.assertRaises( - stats.StatsError, self.stats.command_show, owner='Foo', name='bar') - - def test_command_showchema(self): - (rcode, value) = isc.config.ccsession.parse_answer( - self.stats.command_showschema()) - self.assertEqual(rcode, 0) - self.assertEqual(len(value), 3) - self.assertTrue('Stats' in value) - self.assertTrue('Boss' in value) - self.assertTrue('Auth' in value) - self.assertFalse('__Dummy__' in value) - schema = value['Stats'] - self.assertEqual(len(schema), 5) - for item in schema: - self.assertTrue(len(item) == 6 or len(item) == 7) - self.assertTrue('item_name' in item) - self.assertTrue('item_type' in item) - self.assertTrue('item_optional' in item) - self.assertTrue('item_default' in item) - self.assertTrue('item_title' in item) - self.assertTrue('item_description' in item) - if len(item) == 7: - self.assertTrue('item_format' in item) - - schema = value['Boss'] - self.assertEqual(len(schema), 1) - for item in schema: - self.assertTrue(len(item) == 7) - self.assertTrue('item_name' in item) - self.assertTrue('item_type' in item) - self.assertTrue('item_optional' in item) - self.assertTrue('item_default' in item) - self.assertTrue('item_title' in item) - self.assertTrue('item_description' in item) - self.assertTrue('item_format' in item) - - schema = value['Auth'] - self.assertEqual(len(schema), 2) - for item in schema: - self.assertTrue(len(item) == 6) - self.assertTrue('item_name' in item) - self.assertTrue('item_type' in item) - self.assertTrue('item_optional' in item) - self.assertTrue('item_default' in item) - self.assertTrue('item_title' in item) - self.assertTrue('item_description' in item) - - (rcode, value) = isc.config.ccsession.parse_answer( - self.stats.command_showschema(owner='Stats')) - self.assertEqual(rcode, 0) - self.assertFalse('Stats' in value) - self.assertFalse('Boss' in value) - self.assertFalse('Auth' in value) - for item in value: - self.assertTrue(len(item) == 6 or len(item) == 7) - self.assertTrue('item_name' in item) - self.assertTrue('item_type' in item) - self.assertTrue('item_optional' in item) - self.assertTrue('item_default' in item) - self.assertTrue('item_title' in item) - self.assertTrue('item_description' in item) - if len(item) == 7: - self.assertTrue('item_format' in item) - - (rcode, value) = isc.config.ccsession.parse_answer( - self.stats.command_showschema(owner='Stats', name='report_time')) - self.assertEqual(rcode, 0) - self.assertFalse('Stats' in value) - self.assertFalse('Boss' in value) - self.assertFalse('Auth' in value) - self.assertTrue(len(value) == 7) - self.assertTrue('item_name' in value) - self.assertTrue('item_type' in value) - self.assertTrue('item_optional' in value) - self.assertTrue('item_default' in value) - self.assertTrue('item_title' in value) - self.assertTrue('item_description' in value) - self.assertTrue('item_format' in value) - self.assertEqual(value['item_name'], 'report_time') - self.assertEqual(value['item_format'], 'date-time') - - self.assertEqual(self.stats.command_showschema(owner='Foo'), - isc.config.create_answer( - 1, "specified arguments are incorrect: owner: Foo, name: None")) - self.assertEqual(self.stats.command_showschema(owner='Foo', name='bar'), - isc.config.create_answer( - 1, "specified arguments are incorrect: owner: Foo, name: bar")) - self.assertEqual(self.stats.command_showschema(owner='Auth'), - isc.config.create_answer( - 0, [{ - "item_default": 0, - "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially", - "item_name": "queries.tcp", - "item_optional": False, - "item_title": "Queries TCP", - "item_type": "integer" - }, - { - "item_default": 0, - "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially", - "item_name": "queries.udp", - "item_optional": False, - "item_title": "Queries UDP", - "item_type": "integer" - }])) - self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.tcp'), - isc.config.create_answer( - 0, { - "item_default": 0, - "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially", - "item_name": "queries.tcp", - "item_optional": False, - "item_title": "Queries TCP", - "item_type": "integer" - })) - - self.assertEqual(self.stats.command_showschema(owner='Stats', name='bar'), - isc.config.create_answer( - 1, "specified arguments are incorrect: owner: Stats, name: bar")) - self.assertEqual(self.stats.command_showschema(name='bar'), - isc.config.create_answer( - 1, "module name is not specified")) - - def test_command_set(self): - orig_get_datetime = stats.get_datetime - stats.get_datetime = lambda : self.const_datetime - (rcode, value) = isc.config.ccsession.parse_answer( - self.stats.command_set(owner='Boss', - data={ 'boot_time' : self.const_datetime })) - stats.get_datetime = orig_get_datetime - self.assertEqual(rcode, 0) - self.assertTrue(value is None) - self.assertEqual(self.stats.statistics_data['Boss']['boot_time'], - self.const_datetime) - self.assertEqual(self.stats.statistics_data['Stats']['last_update_time'], - self.const_datetime) - self.assertEqual(self.stats.command_set(owner='Stats', - data={ 'lname' : 'foo@bar' }), - isc.config.create_answer(0, None)) - self.stats.statistics_data['Stats'] = {} - self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( - { "module_name": self.stats.module_name, - "statistics": [] } ) - self.assertEqual(self.stats.command_set(owner='Stats', - data={ 'lname' : '_foo_@_bar_' }), - isc.config.create_answer( - 1, - "errors while setting statistics data: unknown item lname")) - self.stats.statistics_data['Stats'] = {} - self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( - { "module_name": self.stats.module_name } ) - self.assertEqual(self.stats.command_set(owner='Stats', - data={ 'lname' : '_foo_@_bar_' }), - isc.config.create_answer( - 1, - "errors while setting statistics data: No statistics specification")) - self.stats.statistics_data['Stats'] = {} - self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( - { "module_name": self.stats.module_name, - "statistics": [ - { - "item_name": "dummy", - "item_type": "string", - "item_optional": False, - "item_default": "", - "item_title": "Local Name", - "item_description": "brabra" - } ] } ) - self.assertRaises(stats.StatsError, - self.stats.command_set, owner='Stats', data={ 'dummy' : '_xxxx_yyyy_zzz_' }) - -class TestOSEnv(unittest.TestCase): def test_osenv(self): """ - test for the environ variable "B10_FROM_SOURCE" - "B10_FROM_SOURCE" is set in Makefile + test for not having environ "B10_FROM_SOURCE" """ - # test case having B10_FROM_SOURCE - self.assertTrue("B10_FROM_SOURCE" in os.environ) - self.assertEqual(stats.SPECFILE_LOCATION, \ - os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + \ - os.sep + "stats.spec") - # test case not having B10_FROM_SOURCE - path = os.environ["B10_FROM_SOURCE"] - os.environ.pop("B10_FROM_SOURCE") - self.assertFalse("B10_FROM_SOURCE" in os.environ) - # import stats again - imp.reload(stats) - # revert the changes - os.environ["B10_FROM_SOURCE"] = path - imp.reload(stats) + if "B10_FROM_SOURCE" in os.environ: + path = os.environ["B10_FROM_SOURCE"] + os.environ.pop("B10_FROM_SOURCE") + imp.reload(stats) + os.environ["B10_FROM_SOURCE"] = path + imp.reload(stats) -def test_main(): - unittest.main() +def result_ok(*args): + if args: + return { 'result': list(args) } + else: + return { 'result': [ 0 ] } if __name__ == "__main__": - test_main() + unittest.main() diff --git a/src/bin/stats/tests/fake_select.py b/src/bin/stats/tests/fake_select.py new file mode 100644 index 0000000000..ca0ca82619 --- /dev/null +++ b/src/bin/stats/tests/fake_select.py @@ -0,0 +1,43 @@ +# 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 mock-up module of select + +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" + +import fake_socket +import errno + +class error(Exception): + pass + +def select(rlst, wlst, xlst, timeout): + if type(timeout) != int and type(timeout) != float: + raise TypeError("Error: %s must be integer or float" + % timeout.__class__.__name__) + for s in rlst + wlst + xlst: + if type(s) != fake_socket.socket: + raise TypeError("Error: %s must be a dummy socket" + % s.__class__.__name__) + s._called = s._called + 1 + if s._called > 3: + raise error("Something is happened!") + elif s._called > 2: + raise error(errno.EINTR) + return (rlst, wlst, xlst) diff --git a/src/bin/stats/tests/fake_socket.py b/src/bin/stats/tests/fake_socket.py new file mode 100644 index 0000000000..4e3a4581a5 --- /dev/null +++ b/src/bin/stats/tests/fake_socket.py @@ -0,0 +1,70 @@ +# 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 mock-up module of socket + +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" + +import re + +AF_INET = 'AF_INET' +AF_INET6 = 'AF_INET6' +_ADDRFAMILY = AF_INET +has_ipv6 = True +_CLOSED = False + +class gaierror(Exception): + pass + +class error(Exception): + pass + +class socket: + + def __init__(self, family=None): + if family is None: + self.address_family = _ADDRFAMILY + else: + self.address_family = family + self._closed = _CLOSED + if self._closed: + raise error('socket is already closed!') + self._called = 0 + + def close(self): + self._closed = True + + def fileno(self): + return id(self) + + def bind(self, server_class): + (self.server_address, self.server_port) = server_class + if self.address_family not in set([AF_INET, AF_INET6]): + raise error("Address family not supported by protocol: %s" % self.address_family) + if self.address_family == AF_INET6 and not has_ipv6: + raise error("Address family not supported in this machine: %s has_ipv6: %s" + % (self.address_family, str(has_ipv6))) + if self.address_family == AF_INET and re.search(':', self.server_address) is not None: + raise gaierror("Address family for hostname not supported : %s %s" % (self.server_address, self.address_family)) + if self.address_family == AF_INET6 and re.search(':', self.server_address) is None: + raise error("Cannot assign requested address : %s" % str(self.server_address)) + if type(self.server_port) is not int: + raise TypeError("an integer is required: %s" % str(self.server_port)) + if self.server_port < 0 or self.server_port > 65535: + raise OverflowError("port number must be 0-65535.: %s" % str(self.server_port)) diff --git a/src/bin/stats/tests/fake_time.py b/src/bin/stats/tests/fake_time.py new file mode 100644 index 0000000000..65e02371d6 --- /dev/null +++ b/src/bin/stats/tests/fake_time.py @@ -0,0 +1,47 @@ +# Copyright (C) 2010 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. + +__version__ = "$Revision$" + +# This is a dummy time class against a Python standard time class. +# It is just testing use only. +# Other methods which time class has is not implemented. +# (This class isn't orderloaded for time class.) + +# These variables are constant. These are example. +_TEST_TIME_SECS = 1283364938.229088 +_TEST_TIME_STRF = '2010-09-01T18:15:38Z' + +def time(): + """ + This is a dummy time() method against time.time() + """ + # return float constant value + return _TEST_TIME_SECS + +def gmtime(): + """ + This is a dummy gmtime() method against time.gmtime() + """ + # always return nothing + return None + +def strftime(*arg): + """ + This is a dummy gmtime() method against time.gmtime() + """ + return _TEST_TIME_STRF + + diff --git a/src/bin/stats/tests/http/Makefile.am b/src/bin/stats/tests/http/Makefile.am new file mode 100644 index 0000000000..79263a98b4 --- /dev/null +++ b/src/bin/stats/tests/http/Makefile.am @@ -0,0 +1,6 @@ +EXTRA_DIST = __init__.py server.py +CLEANFILES = __init__.pyc server.pyc +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/http/__init__.py b/src/bin/stats/tests/http/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/bin/stats/tests/http/server.py b/src/bin/stats/tests/http/server.py new file mode 100644 index 0000000000..70ed6faa30 --- /dev/null +++ b/src/bin/stats/tests/http/server.py @@ -0,0 +1,96 @@ +# 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 mock-up module of http.server + +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" + +import fake_socket + +class DummyHttpResponse: + def __init__(self, path): + self.path = path + self.headers={} + self.log = "" + + def _write_log(self, msg): + self.log = self.log + msg + +class HTTPServer: + """ + A mock-up class of http.server.HTTPServer + """ + address_family = fake_socket.AF_INET + def __init__(self, server_class, handler_class): + self.socket = fake_socket.socket(self.address_family) + self.server_class = server_class + self.socket.bind(self.server_class) + self._handler = handler_class(None, None, self) + + def handle_request(self): + pass + + def server_close(self): + self.socket.close() + +class BaseHTTPRequestHandler: + """ + A mock-up class of http.server.BaseHTTPRequestHandler + """ + + def __init__(self, request, client_address, server): + self.path = "/path/to" + self.headers = {} + self.server = server + self.response = DummyHttpResponse(path=self.path) + self.response.write = self._write + self.wfile = self.response + + def send_response(self, code=0): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.code = code + + def send_header(self, key, value): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.headers[key] = value + + def end_headers(self): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.wrote_headers = True + + def send_error(self, code, message=None): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.code = code + self.response.body = message + + def address_string(self): + return 'dummyhost' + + def log_date_time_string(self): + return '[DD/MM/YYYY HH:MI:SS]' + + def _write(self, obj): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.body = obj.decode() + diff --git a/src/bin/stats/tests/isc/Makefile.am b/src/bin/stats/tests/isc/Makefile.am new file mode 100644 index 0000000000..d31395d404 --- /dev/null +++ b/src/bin/stats/tests/isc/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = cc config util log +EXTRA_DIST = __init__.py +CLEANFILES = __init__.pyc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/__init__.py b/src/bin/stats/tests/isc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/bin/stats/tests/isc/cc/Makefile.am b/src/bin/stats/tests/isc/cc/Makefile.am new file mode 100644 index 0000000000..67323b5f1b --- /dev/null +++ b/src/bin/stats/tests/isc/cc/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = __init__.py session.py +CLEANFILES = __init__.pyc session.pyc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/cc/__init__.py b/src/bin/stats/tests/isc/cc/__init__.py new file mode 100644 index 0000000000..9a3eaf6185 --- /dev/null +++ b/src/bin/stats/tests/isc/cc/__init__.py @@ -0,0 +1 @@ +from isc.cc.session import * diff --git a/src/bin/stats/tests/isc/cc/session.py b/src/bin/stats/tests/isc/cc/session.py new file mode 100644 index 0000000000..e16d6a9abc --- /dev/null +++ b/src/bin/stats/tests/isc/cc/session.py @@ -0,0 +1,148 @@ +# Copyright (C) 2010,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 mock-up module of isc.cc.session + +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" + +import sys +import fake_socket + +# set a dummy lname +_TEST_LNAME = '123abc@xxxx' + +class Queue(): + def __init__(self, msg=None, env={}): + self.msg = msg + self.env = env + + def dump(self): + return { 'msg': self.msg, 'env': self.env } + +class SessionError(Exception): + pass + +class SessionTimeout(Exception): + pass + +class Session: + def __init__(self, socket_file=None, verbose=False): + self._lname = _TEST_LNAME + self.message_queue = [] + self.old_message_queue = [] + try: + self._socket = fake_socket.socket() + except fake_socket.error as se: + raise SessionError(se) + self.verbose = verbose + + @property + def lname(self): + return self._lname + + def close(self): + self._socket.close() + + def _clear_queues(self): + while len(self.message_queue) > 0: + self.dequeue() + + def _next_sequence(self, que=None): + return len(self.message_queue) + + def enqueue(self, msg=None, env={}): + if self._socket._closed: + raise SessionError("Session has been closed.") + seq = self._next_sequence() + env.update({"seq": 0}) # fixed here + que = Queue(msg=msg, env=env) + self.message_queue.append(que) + if self.verbose: + sys.stdout.write("[Session] enqueue: " + str(que.dump()) + "\n") + return seq + + def dequeue(self): + if self._socket._closed: + raise SessionError("Session has been closed.") + que = None + try: + que = self.message_queue.pop(0) # always pop at index 0 + self.old_message_queue.append(que) + except IndexError: + que = Queue() + if self.verbose: + sys.stdout.write("[Session] dequeue: " + str(que.dump()) + "\n") + return que + + def get_queue(self, seq=None): + if self._socket._closed: + raise SessionError("Session has been closed.") + if seq is None: + seq = len(self.message_queue) - 1 + que = None + try: + que = self.message_queue[seq] + except IndexError: + raise IndexError + que = Queue() + if self.verbose: + sys.stdout.write("[Session] get_queue: " + str(que.dump()) + "\n") + return que + + def group_sendmsg(self, msg, group, instance="*", to="*"): + return self.enqueue(msg=msg, env={ + "type": "send", + "from": self._lname, + "to": to, + "group": group, + "instance": instance }) + + def group_recvmsg(self, nonblock=True, seq=0): + que = self.dequeue() + return que.msg, que.env + + def group_reply(self, routing, msg): + return self.enqueue(msg=msg, env={ + "type": "send", + "from": self._lname, + "to": routing["from"], + "group": routing["group"], + "instance": routing["instance"], + "reply": routing["seq"] }) + + def get_message(self, group, to='*'): + if self._socket._closed: + raise SessionError("Session has been closed.") + que = Queue() + for q in self.message_queue: + if q.env['group'] == group: + self.message_queue.remove(q) + self.old_message_queue.append(q) + que = q + if self.verbose: + sys.stdout.write("[Session] get_message: " + str(que.dump()) + "\n") + return q.msg + + def group_subscribe(self, group, instance = "*"): + if self._socket._closed: + raise SessionError("Session has been closed.") + + def group_unsubscribe(self, group, instance = "*"): + if self._socket._closed: + raise SessionError("Session has been closed.") diff --git a/src/bin/stats/tests/isc/config/Makefile.am b/src/bin/stats/tests/isc/config/Makefile.am new file mode 100644 index 0000000000..ffbecdae03 --- /dev/null +++ b/src/bin/stats/tests/isc/config/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = __init__.py ccsession.py +CLEANFILES = __init__.pyc ccsession.pyc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/config/__init__.py b/src/bin/stats/tests/isc/config/__init__.py new file mode 100644 index 0000000000..4c49e956aa --- /dev/null +++ b/src/bin/stats/tests/isc/config/__init__.py @@ -0,0 +1 @@ +from isc.config.ccsession import * diff --git a/src/bin/stats/tests/isc/config/ccsession.py b/src/bin/stats/tests/isc/config/ccsession.py new file mode 100644 index 0000000000..50f7c1b163 --- /dev/null +++ b/src/bin/stats/tests/isc/config/ccsession.py @@ -0,0 +1,249 @@ +# Copyright (C) 2010,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 mock-up module of isc.cc.session + +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" + +import json +import os +import time +from isc.cc.session import Session + +COMMAND_CONFIG_UPDATE = "config_update" + +def parse_answer(msg): + assert 'result' in msg + try: + return msg['result'][0], msg['result'][1] + except IndexError: + return msg['result'][0], None + +def create_answer(rcode, arg = None): + if arg is None: + return { 'result': [ rcode ] } + else: + return { 'result': [ rcode, arg ] } + +def parse_command(msg): + assert 'command' in msg + try: + return msg['command'][0], msg['command'][1] + except IndexError: + return msg['command'][0], None + +def create_command(command_name, params = None): + if params is None: + return {"command": [command_name]} + else: + return {"command": [command_name, params]} + +def module_spec_from_file(spec_file, check = True): + try: + file = open(spec_file) + json_str = file.read() + module_spec = json.loads(json_str) + file.close() + return ModuleSpec(module_spec['module_spec'], check) + except IOError as ioe: + raise ModuleSpecError("JSON read error: " + str(ioe)) + except ValueError as ve: + raise ModuleSpecError("JSON parse error: " + str(ve)) + except KeyError as err: + raise ModuleSpecError("Data definition has no module_spec element") + +class ModuleSpecError(Exception): + pass + +class ModuleSpec: + def __init__(self, module_spec, check = True): + # check only confi_data for testing + if check and "config_data" in module_spec: + _check_config_spec(module_spec["config_data"]) + self._module_spec = module_spec + + def get_config_spec(self): + return self._module_spec['config_data'] + + def get_commands_spec(self): + return self._module_spec['commands'] + + def get_module_name(self): + return self._module_spec['module_name'] + +def _check_config_spec(config_data): + # config data is a list of items represented by dicts that contain + # things like "item_name", depending on the type they can have + # specific subitems + """Checks a list that contains the configuration part of the + specification. Raises a ModuleSpecError if there is a + problem.""" + if type(config_data) != list: + raise ModuleSpecError("config_data is of type " + str(type(config_data)) + ", not a list of items") + for config_item in config_data: + _check_item_spec(config_item) + +def _check_item_spec(config_item): + """Checks the dict that defines one config item + (i.e. containing "item_name", "item_type", etc. + Raises a ModuleSpecError if there is an error""" + if type(config_item) != dict: + raise ModuleSpecError("item spec not a dict") + if "item_name" not in config_item: + raise ModuleSpecError("no item_name in config item") + if type(config_item["item_name"]) != str: + raise ModuleSpecError("item_name is not a string: " + str(config_item["item_name"])) + item_name = config_item["item_name"] + if "item_type" not in config_item: + raise ModuleSpecError("no item_type in config item") + item_type = config_item["item_type"] + if type(item_type) != str: + raise ModuleSpecError("item_type in " + item_name + " is not a string: " + str(type(item_type))) + if item_type not in ["integer", "real", "boolean", "string", "list", "map", "any"]: + raise ModuleSpecError("unknown item_type in " + item_name + ": " + item_type) + if "item_optional" in config_item: + if type(config_item["item_optional"]) != bool: + raise ModuleSpecError("item_default in " + item_name + " is not a boolean") + if not config_item["item_optional"] and "item_default" not in config_item: + raise ModuleSpecError("no default value for non-optional item " + item_name) + else: + raise ModuleSpecError("item_optional not in item " + item_name) + if "item_default" in config_item: + item_default = config_item["item_default"] + if (item_type == "integer" and type(item_default) != int) or \ + (item_type == "real" and type(item_default) != float) or \ + (item_type == "boolean" and type(item_default) != bool) or \ + (item_type == "string" and type(item_default) != str) or \ + (item_type == "list" and type(item_default) != list) or \ + (item_type == "map" and type(item_default) != dict): + raise ModuleSpecError("Wrong type for item_default in " + item_name) + # TODO: once we have check_type, run the item default through that with the list|map_item_spec + if item_type == "list": + if "list_item_spec" not in config_item: + raise ModuleSpecError("no list_item_spec in list item " + item_name) + if type(config_item["list_item_spec"]) != dict: + raise ModuleSpecError("list_item_spec in " + item_name + " is not a dict") + _check_item_spec(config_item["list_item_spec"]) + if item_type == "map": + if "map_item_spec" not in config_item: + raise ModuleSpecError("no map_item_sepc in map item " + item_name) + if type(config_item["map_item_spec"]) != list: + raise ModuleSpecError("map_item_spec in " + item_name + " is not a list") + for map_item in config_item["map_item_spec"]: + if type(map_item) != dict: + raise ModuleSpecError("map_item_spec element is not a dict") + _check_item_spec(map_item) + if 'item_format' in config_item and 'item_default' in config_item: + item_format = config_item["item_format"] + item_default = config_item["item_default"] + if not _check_format(item_default, item_format): + raise ModuleSpecError( + "Wrong format for " + str(item_default) + " in " + str(item_name)) + +def _check_format(value, format_name): + """Check if specified value and format are correct. Return True if + is is correct.""" + # TODO: should be added other format types if necessary + time_formats = { 'date-time' : "%Y-%m-%dT%H:%M:%SZ", + 'date' : "%Y-%m-%d", + 'time' : "%H:%M:%S" } + for fmt in time_formats: + if format_name == fmt: + try: + time.strptime(value, time_formats[fmt]) + return True + except (ValueError, TypeError): + break + return False + +class ModuleCCSessionError(Exception): + pass + +class DataNotFoundError(Exception): + pass + +class ConfigData: + def __init__(self, specification): + self.specification = specification + + def get_value(self, identifier): + """Returns a tuple where the first item is the value at the + given identifier, and the second item is absolutely False + even if the value is an unset default or not. Raises an + DataNotFoundError if the identifier is not found in the + specification file. + *** NOTE *** + There are some differences from the original method. This + method never handles local settings like the original + method. But these different behaviors aren't so big issues + for a mock-up method of stats_httpd because stats_httpd + calls this method at only first.""" + for config_map in self.get_module_spec().get_config_spec(): + if config_map['item_name'] == identifier: + if 'item_default' in config_map: + return config_map['item_default'], False + raise DataNotFoundError("item_name %s is not found in the specfile" % identifier) + + def get_module_spec(self): + return self.specification + +class ModuleCCSession(ConfigData): + def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None): + module_spec = module_spec_from_file(spec_file_name) + ConfigData.__init__(self, module_spec) + self._module_name = module_spec.get_module_name() + self.set_config_handler(config_handler) + self.set_command_handler(command_handler) + if not cc_session: + self._session = Session(verbose=True) + else: + self._session = cc_session + + def start(self): + pass + + def close(self): + self._session.close() + + def check_command(self, nonblock=True): + msg, env = self._session.group_recvmsg(nonblock) + if not msg or 'result' in msg: + return + cmd, arg = parse_command(msg) + answer = None + if cmd == COMMAND_CONFIG_UPDATE and self._config_handler: + answer = self._config_handler(arg) + elif env['group'] == self._module_name and self._command_handler: + answer = self._command_handler(cmd, arg) + if answer: + self._session.group_reply(env, answer) + + def set_config_handler(self, config_handler): + self._config_handler = config_handler + # should we run this right now since we've changed the handler? + + def set_command_handler(self, command_handler): + self._command_handler = command_handler + + def get_module_spec(self): + return self.specification + + def get_socket(self): + return self._session._socket + diff --git a/src/bin/stats/tests/isc/log/Makefile.am b/src/bin/stats/tests/isc/log/Makefile.am new file mode 100644 index 0000000000..457b9de1c2 --- /dev/null +++ b/src/bin/stats/tests/isc/log/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = __init__.py +CLEANFILES = __init__.pyc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/log/__init__.py b/src/bin/stats/tests/isc/log/__init__.py new file mode 100644 index 0000000000..641cf790c1 --- /dev/null +++ b/src/bin/stats/tests/isc/log/__init__.py @@ -0,0 +1,33 @@ +# 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 file is not installed. The log.so is installed into the right place. +# It is only to find it in the .libs directory when we run as a test or +# from the build directory. +# But as nobody gives us the builddir explicitly (and we can't use generation +# from .in file, as it would put us into the builddir and we wouldn't be found) +# we guess from current directory. Any idea for something better? This should +# be enough for the tests, but would it work for B10_FROM_SOURCE as well? +# Should we look there? Or define something in bind10_config? + +import os +import sys + +for base in sys.path[:]: + loglibdir = os.path.join(base, 'isc/log/.libs') + if os.path.exists(loglibdir): + sys.path.insert(0, loglibdir) + +from log import * diff --git a/src/bin/stats/tests/isc/util/Makefile.am b/src/bin/stats/tests/isc/util/Makefile.am new file mode 100644 index 0000000000..9c74354ca3 --- /dev/null +++ b/src/bin/stats/tests/isc/util/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = __init__.py process.py +CLEANFILES = __init__.pyc process.pyc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/util/__init__.py b/src/bin/stats/tests/isc/util/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/bin/stats/tests/isc/util/process.py b/src/bin/stats/tests/isc/util/process.py new file mode 100644 index 0000000000..0f764c1872 --- /dev/null +++ b/src/bin/stats/tests/isc/util/process.py @@ -0,0 +1,21 @@ +# Copyright (C) 2010 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 dummy function of isc.util.process.rename() +""" + +def rename(name=None): + pass diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py deleted file mode 100644 index e79db48951..0000000000 --- a/src/bin/stats/tests/test_utils.py +++ /dev/null @@ -1,291 +0,0 @@ -""" -Utilities and mock modules for unittests of statistics modules - -""" -import os -import io -import time -import sys -import threading -import tempfile - -import msgq -import isc.config.cfgmgr -import stats -import stats_httpd - -# TODO: consider appropriate timeout seconds -TIMEOUT_SEC = 0.05 - -def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC): - if not session: - cc_session = isc.cc.Session() - else: - cc_session = session - orig_timeout = cc_session.get_timeout() - cc_session.set_timeout(timeout * 1000) - command = isc.config.ccsession.create_command(command_name, params) - seq = cc_session.group_sendmsg(command, module_name) - try: - (answer, env) = cc_session.group_recvmsg(nonblock, seq) - if answer: - return isc.config.ccsession.parse_answer(answer) - except isc.cc.SessionTimeout: - pass - finally: - if not session: - cc_session.close() - else: - cc_session.set_timeout(orig_timeout) - -def send_shutdown(module_name): - return send_command("shutdown", module_name) - -class ThreadingServerManager: - def __init__(self, server_class): - self.server_class = server_class - self.server_class_name = server_class.__name__ - self.server = self.server_class() - self.server._thread = threading.Thread( - name=self.server_class_name, target=self.server.run) - self.server._thread.daemon = True - - def run(self): - self.server._thread.start() - self.server._started.wait() - self.server._started.clear() - # waiting for the server's being ready for listening - time.sleep(TIMEOUT_SEC) - - def shutdown(self): - self.server.shutdown() - self.server._thread.join(TIMEOUT_SEC) - -class MockMsgq: - def __init__(self): - self._started = threading.Event() - self.msgq = msgq.MsgQ(None) - result = self.msgq.setup() - if result: - sys.exit("Error on Msgq startup: %s" % result) - - def run(self): - self._started.set() - try: - self.msgq.run() - except Exception: - pass - finally: - self.shutdown() - - def shutdown(self): - self.msgq.shutdown() - -class MockCfgmgr: - def __init__(self): - self._started = threading.Event() - self.cfgmgr = isc.config.cfgmgr.ConfigManager( - os.environ['CONFIG_TESTDATA_PATH'], "b10-config.db") - self.cfgmgr.read_config() - - def run(self): - self._started.set() - try: - self.cfgmgr.run() - finally: - self.shutdown() - - def shutdown(self): - self.cfgmgr.running = False - -class MockBoss: - spec_str = """\ -{ - "module_spec": { - "module_name": "Boss", - "module_description": "Mock Master process", - "config_data": [], - "commands": [ - { - "command_name": "sendstats", - "command_description": "Send data to a statistics module at once", - "command_args": [] - } - ], - "statistics": [ - { - "item_name": "boot_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "Boot time", - "item_description": "A date time when bind10 process starts initially", - "item_format": "date-time" - } - ] - } -} -""" - _BASETIME = (2011, 6, 22, 8, 14, 8, 2, 173, 0) - - def __init__(self): - self._started = threading.Event() - self.running = False - self.spec_file = io.StringIO(self.spec_str) - # create ModuleCCSession object - self.mccs = isc.config.ModuleCCSession( - self.spec_file, - self.config_handler, - self.command_handler) - self.spec_file.close() - self.cc_session = self.mccs._session - self.got_command_name = '' - - def run(self): - self.mccs.start() - self.running = True - self._started.set() - while self.running: - self.mccs.check_command(False) - - def shutdown(self): - self.running = False - - def config_handler(self, new_config): - return isc.config.create_answer(0) - - def command_handler(self, command, *args, **kwargs): - self.got_command_name = command - if command == 'sendstats': - params = { "owner": "Boss", - "data": { - 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', self._BASETIME) - } - } - return send_command("set", "Stats", params=params, session=self.cc_session) - return isc.config.create_answer(1, "Unknown Command") - -class MockAuth: - spec_str = """\ -{ - "module_spec": { - "module_name": "Auth", - "module_description": "Mock Authoritative service", - "config_data": [], - "commands": [ - { - "command_name": "sendstats", - "command_description": "Send data to a statistics module at once", - "command_args": [] - } - ], - "statistics": [ - { - "item_name": "queries.tcp", - "item_type": "integer", - "item_optional": false, - "item_default": 0, - "item_title": "Queries TCP", - "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" - }, - { - "item_name": "queries.udp", - "item_type": "integer", - "item_optional": false, - "item_default": 0, - "item_title": "Queries UDP", - "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially" - } - ] - } -} -""" - def __init__(self): - self._started = threading.Event() - self.running = False - self.spec_file = io.StringIO(self.spec_str) - # create ModuleCCSession object - self.mccs = isc.config.ModuleCCSession( - self.spec_file, - self.config_handler, - self.command_handler) - self.spec_file.close() - self.cc_session = self.mccs._session - self.got_command_name = '' - self.queries_tcp = 3 - self.queries_udp = 2 - - def run(self): - self.mccs.start() - self.running = True - self._started.set() - while self.running: - self.mccs.check_command(False) - - def shutdown(self): - self.running = False - - def config_handler(self, new_config): - return isc.config.create_answer(0) - - def command_handler(self, command, *args, **kwargs): - self.got_command_name = command - if command == 'sendstats': - params = { "owner": "Auth", - "data": { 'queries.tcp': self.queries_tcp, - 'queries.udp': self.queries_udp } } - return send_command("set", "Stats", params=params, session=self.cc_session) - return isc.config.create_answer(1, "Unknown Command") - -class MyStats(stats.Stats): - def __init__(self): - self._started = threading.Event() - stats.Stats.__init__(self) - - def run(self): - self._started.set() - stats.Stats.start(self) - - def shutdown(self): - send_shutdown("Stats") - -class MyStatsHttpd(stats_httpd.StatsHttpd): - def __init__(self): - self._started = threading.Event() - stats_httpd.StatsHttpd.__init__(self) - - def run(self): - self._started.set() - stats_httpd.StatsHttpd.start(self) - - def shutdown(self): - send_shutdown("StatsHttpd") - -class BaseModules: - def __init__(self): - self.class_name = BaseModules.__name__ - - # Change value of BIND10_MSGQ_SOCKET_FILE in environment variables - os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='unix_socket.') - # MockMsgq - self.msgq = ThreadingServerManager(MockMsgq) - self.msgq.run() - # MockCfgmgr - self.cfgmgr = ThreadingServerManager(MockCfgmgr) - self.cfgmgr.run() - # MockBoss - self.boss = ThreadingServerManager(MockBoss) - self.boss.run() - # MockAuth - self.auth = ThreadingServerManager(MockAuth) - self.auth.run() - - def shutdown(self): - # MockAuth - self.auth.shutdown() - # MockBoss - self.boss.shutdown() - # MockCfgmgr - self.cfgmgr.shutdown() - # MockMsgq - self.msgq.shutdown() diff --git a/src/bin/stats/tests/testdata/Makefile.am b/src/bin/stats/tests/testdata/Makefile.am new file mode 100644 index 0000000000..1b8df6d736 --- /dev/null +++ b/src/bin/stats/tests/testdata/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = stats_test.spec diff --git a/src/bin/stats/tests/testdata/stats_test.spec b/src/bin/stats/tests/testdata/stats_test.spec new file mode 100644 index 0000000000..8136756440 --- /dev/null +++ b/src/bin/stats/tests/testdata/stats_test.spec @@ -0,0 +1,19 @@ +{ + "module_spec": { + "module_name": "Stats", + "module_description": "Stats daemon", + "config_data": [], + "commands": [ + { + "command_name": "status", + "command_description": "identify whether stats module is alive or not", + "command_args": [] + }, + { + "command_name": "the_dummy", + "command_description": "this is for testing", + "command_args": [] + } + ] + } +} diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am index 0dc5021302..56ff68b0c7 100644 --- a/src/bin/tests/Makefile.am +++ b/src/bin/tests/Makefile.am @@ -14,7 +14,7 @@ endif # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif diff --git a/tests/system/bindctl/tests.sh b/tests/system/bindctl/tests.sh index 49ef0f17b0..6923c4167c 100755 --- a/tests/system/bindctl/tests.sh +++ b/tests/system/bindctl/tests.sh @@ -24,10 +24,6 @@ SYSTEMTESTTOP=.. status=0 n=0 -# TODO: consider consistency with statistics definition in auth.spec -auth_queries_tcp="\" -auth_queries_udp="\" - echo "I:Checking b10-auth is working by default ($n)" $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1 # perform a simple check on the output (digcomp would be too much for this) @@ -44,8 +40,8 @@ echo 'Stats show --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # the server should have received 1 UDP and 1 TCP queries (TCP query was # sent from the server startup script) -grep $auth_queries_tcp".*\<1\>" bindctl.out.$n > /dev/null || status=1 -grep $auth_queries_udp".*\<1\>" bindctl.out.$n > /dev/null || status=1 +grep "\"auth.queries.tcp\": 1," bindctl.out.$n > /dev/null || status=1 +grep "\"auth.queries.udp\": 1," bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` @@ -77,8 +73,8 @@ echo 'Stats show ' | $RUN_BINDCTL \ --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # The statistics counters should have been reset while stop/start. -grep $auth_queries_tcp".*\<0\>" bindctl.out.$n > /dev/null || status=1 -grep $auth_queries_udp".*\<1\>" bindctl.out.$n > /dev/null || status=1 +grep "\"auth.queries.tcp\": 0," bindctl.out.$n > /dev/null || status=1 +grep "\"auth.queries.udp\": 1," bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` @@ -101,8 +97,8 @@ echo 'Stats show ' | $RUN_BINDCTL \ --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # The statistics counters shouldn't be reset due to hot-swapping datasource. -grep $auth_queries_tcp".*\<0\>" bindctl.out.$n > /dev/null || status=1 -grep $auth_queries_udp".*\<2\>" bindctl.out.$n > /dev/null || status=1 +grep "\"auth.queries.tcp\": 0," bindctl.out.$n > /dev/null || status=1 +grep "\"auth.queries.udp\": 2," bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` From afde75c1fe9ab3fa35acdf1a3b5f80ec389e1190 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 11:13:30 +0200 Subject: [PATCH 527/974] [1067] Add another column So we can unify the interface --- src/lib/datasrc/database.h | 5 +- src/lib/datasrc/sqlite3_accessor.cc | 2 +- src/lib/datasrc/tests/database_unittest.cc | 5 ++ .../tests/sqlite3_accessor_unittest.cc | 62 +++++++++++-------- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 1b2f2c0a6b..8376185344 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -221,11 +221,12 @@ public: SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPE ///< the RRSIG covers. In the current implementation, ///< this field is ignored. - RDATA_COLUMN = 3 ///< Full text representation of the record's RDATA + RDATA_COLUMN = 3, ///< Full text representation of the record's RDATA + DOMAIN_NAME = 4 ///< The domain name of this RR }; /// The number of fields the columns array passed to getNextRecord should have - static const size_t COLUMN_COUNT = 4; + static const size_t COLUMN_COUNT = 5; /** * \brief Returns a string identifying this dabase backend diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index e7aa5380a2..7fd0b86cbf 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -138,7 +138,7 @@ const char* const SCHEMA_LIST[] = { const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"; -const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata " +const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata, name " "FROM records WHERE zone_id=?1 AND name=?2"; const char* const q_iterate_str = "SELECT name, rdtype, ttl, rdata FROM records " diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 6f9c7d71ba..c6da13a617 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -282,6 +282,11 @@ private: // so we can immediately start adding new records. void addCurName(const std::string& name) { ASSERT_EQ(0, records.count(name)); + // Append the name to all of them + for (std::vector >::iterator + i(cur_name.begin()); i != cur_name.end(); ++ i) { + i->push_back(name); + } records[name] = cur_name; cur_name.clear(); } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 454638fd7c..d3dcbfa502 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -143,12 +143,14 @@ checkRecordRow(const std::string columns[], const std::string& field0, const std::string& field1, const std::string& field2, - const std::string& field3) + const std::string& field3, + const std::string& field4) { EXPECT_EQ(field0, columns[0]); EXPECT_EQ(field1, columns[1]); EXPECT_EQ(field2, columns[2]); EXPECT_EQ(field3, columns[3]); + EXPECT_EQ(field4, columns[4]); } TEST_F(SQLite3Access, getRecords) { @@ -163,83 +165,89 @@ TEST_F(SQLite3Access, getRecords) { // without search, getNext() should return false EXPECT_FALSE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "", "", "", ""); + checkRecordRow(columns, "", "", "", "", ""); db->searchForRecords(zone_id, "foo.bar."); EXPECT_FALSE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "", "", "", ""); + checkRecordRow(columns, "", "", "", "", ""); db->searchForRecords(zone_id, ""); EXPECT_FALSE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "", "", "", ""); + checkRecordRow(columns, "", "", "", "", ""); // Should error on a bad number of columns - EXPECT_THROW(db->getNextRecord(columns, 3), DataSourceError); - EXPECT_THROW(db->getNextRecord(columns, 5), DataSourceError); + EXPECT_THROW(db->getNextRecord(columns, 4), DataSourceError); + EXPECT_THROW(db->getNextRecord(columns, 6), DataSourceError); // now try some real searches db->searchForRecords(zone_id, "foo.example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "CNAME", "3600", "", - "cnametest.example.org."); + "cnametest.example.org.", "foo.example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "CNAME", "CNAME 5 3 3600 20100322084538 20100220084538 33495 " - "example.com. FAKEFAKEFAKEFAKE"); + "example.com. FAKEFAKEFAKEFAKE", "foo.example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "NSEC", "7200", "", - "mail.example.com. CNAME RRSIG NSEC"); + "mail.example.com. CNAME RRSIG NSEC", "foo.example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " - "example.com. FAKEFAKEFAKEFAKE"); + "example.com. FAKEFAKEFAKEFAKE", "foo.example.com."); EXPECT_FALSE(db->getNextRecord(columns, column_count)); // with no more records, the array should not have been modified checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " - "example.com. FAKEFAKEFAKEFAKE"); + "example.com. FAKEFAKEFAKEFAKE", "foo.example.com."); db->searchForRecords(zone_id, "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "SOA", "3600", "", "master.example.com. admin.example.com. " - "1234 3600 1800 2419200 7200"); + "1234 3600 1800 2419200 7200", "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "SOA", "SOA 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE"); + "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "NS", "1200", "", "dns01.example.com."); + checkRecordRow(columns, "NS", "1200", "", "dns01.example.com.", + "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "NS", "3600", "", "dns02.example.com."); + checkRecordRow(columns, "NS", "3600", "", "dns02.example.com.", + "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "NS", "1800", "", "dns03.example.com."); + checkRecordRow(columns, "NS", "1800", "", "dns03.example.com.", + "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "NS", "NS 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE"); + "33495 example.com. FAKEFAKEFAKEFAKE", + "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "MX", "3600", "", "10 mail.example.com."); + checkRecordRow(columns, "MX", "3600", "", "10 mail.example.com.", + "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "MX", "3600", "", - "20 mail.subzone.example.com."); + "20 mail.subzone.example.com.", "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "MX", "MX 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE"); + "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "NSEC", "7200", "", - "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"); + "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY", + "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 2 7200 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE"); + "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "DNSKEY", "3600", "", "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4q NGV7W" "cTD0WEiuV7IjXgHE36fCmS9QsUxSSOV o1I/FMxI2PJVqTYHkX" "FBS7AzLGsQYMU7UjBZ SotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g" - "0qcbW YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx"); + "0qcbW YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx", "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "DNSKEY", "3600", "", "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJ tbvzg" @@ -249,20 +257,20 @@ TEST_F(SQLite3Access, getRecords) { "qiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq 23TaOrVTjB7d1a/h31OD" "fiHAxFHrkY3t3D5J R9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86" "Acv RyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGf oIK/aKwEN" - "rsjcKZZj660b1M="); + "rsjcKZZj660b1M=", "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " - "4456 example.com. FAKEFAKEFAKEFAKE"); + "4456 example.com. FAKEFAKEFAKEFAKE", "example.com."); ASSERT_TRUE(db->getNextRecord(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE"); + "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); EXPECT_FALSE(db->getNextRecord(columns, column_count)); // getnextrecord returning false should mean array is not altered checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE"); + "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); } } // end anonymous namespace From c454dfae8988337bd10bfe0551ee62a267049dfe Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 11:53:45 +0200 Subject: [PATCH 528/974] [1067] Unify the interface --- src/lib/datasrc/database.cc | 12 ++-- src/lib/datasrc/database.h | 11 +-- src/lib/datasrc/sqlite3_accessor.cc | 24 ++++--- src/lib/datasrc/tests/database_unittest.cc | 71 +++++++++++-------- .../tests/sqlite3_accessor_unittest.cc | 30 ++++++-- 5 files changed, 90 insertions(+), 58 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 14e5fa76e7..7affd70d34 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -361,12 +361,12 @@ public: private: // Load next row of data void getData() { - string data[4]; - data_ready_ = context_->getNext(data); - name_ = data[0]; - rtype_ = data[1]; - ttl_ = data[2]; - rdata_ = data[3]; + string data[DatabaseAccessor::COLUMN_COUNT]; + data_ready_ = context_->getNext(data, DatabaseAccessor::COLUMN_COUNT); + name_ = data[DatabaseAccessor::NAME_COLUMN]; + rtype_ = data[DatabaseAccessor::TYPE_COLUMN]; + ttl_ = data[DatabaseAccessor::TTL_COLUMN]; + rdata_ = data[DatabaseAccessor::RDATA_COLUMN]; } // The context diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 8376185344..cccce0dddf 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -108,13 +108,14 @@ public: * RRset must not be interleaved with any other RRs (eg. RRsets must be * "together"). * - * \param data The data are to be returned by this parameter. They are - * (in order) the name, rrtype, TTL and the rdata. - * \todo Unify with the interface in #1062 eventually. + * \param columns The data will be returned through here. The order + * is specified by the RecordColumns enum. + * \param Size of the columns array. Must be equal to COLUMN_COUNT, + * otherwise DataSourceError is thrown. * \todo Do we consider databases where it is stored in binary blob * format? */ - virtual bool getNext(std::string data[4]) = 0; + virtual bool getNext(std::string columns[], size_t column_data) = 0; }; typedef boost::shared_ptr IteratorContextPtr; @@ -222,7 +223,7 @@ public: ///< the RRSIG covers. In the current implementation, ///< this field is ignored. RDATA_COLUMN = 3, ///< Full text representation of the record's RDATA - DOMAIN_NAME = 4 ///< The domain name of this RR + NAME_COLUMN = 4 ///< The domain name of this RR }; /// The number of fields the columns array passed to getNextRecord should have diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 7fd0b86cbf..bab7d2966a 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -141,7 +141,7 @@ const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata, name " "FROM records WHERE zone_id=?1 AND name=?2"; -const char* const q_iterate_str = "SELECT name, rdtype, ttl, rdata FROM records " +const char* const q_iterate_str = "SELECT rdtype, ttl, sigtype, rdata, name FROM records " "WHERE zone_id = ?1 " "ORDER BY name, rdtype"; @@ -362,6 +362,10 @@ convertToPlainChar(const unsigned char* ucp, } } +// TODO: Once we want to have iterator returned from searchForRecords, this +// class can be reused. It should be modified to take the sqlite3 statement +// instead of creating it in constructor, it doesn't have to care which one +// it is, just provide data from it. class SQLite3Database::Context : public DatabaseAccessor::IteratorContext { public: Context(const boost::shared_ptr& database, int id) : @@ -375,18 +379,18 @@ public: " to SQL statement (iterate)"); } } - bool getNext(std::string data[4]) { + bool getNext(std::string data[], size_t size) { + if (size != COLUMN_COUNT) { + isc_throw(DataSourceError, "getNext received size of " << size << + ", not " << COLUMN_COUNT); + } // If there's another row, get it int rc(sqlite3_step(statement)); if (rc == SQLITE_ROW) { - data[0] = convertToPlainChar(sqlite3_column_text(statement, 0), - database_->dbparameters_); - data[1] = convertToPlainChar(sqlite3_column_text(statement, 1), - database_->dbparameters_); - data[2] = boost::lexical_cast( - sqlite3_column_int(statement, 2)); - data[3] = convertToPlainChar(sqlite3_column_text(statement, 3), - database_->dbparameters_); + for (size_t i(0); i < size; ++ i) { + data[i] = convertToPlainChar(sqlite3_column_text(statement, i), + database_->dbparameters_); + } return (true); } else if (rc != SQLITE_DONE) { isc_throw(DataSourceError, diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index c6da13a617..48319ea03b 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -94,38 +94,41 @@ private: MockIteratorContext() : step(0) { } - virtual bool getNext(string data[4]) { + virtual bool getNext(string data[], size_t size) { + if (size != DatabaseAccessor::COLUMN_COUNT) { + isc_throw(DataSourceError, "Wrong column count in getNextRecord"); + } switch (step ++) { case 0: - data[0] = "example.org"; - data[1] = "SOA"; - data[2] = "300"; - data[3] = "ns1.example.org. admin.example.org. " + data[DatabaseAccessor::NAME_COLUMN] = "example.org"; + data[DatabaseAccessor::TYPE_COLUMN] = "SOA"; + data[DatabaseAccessor::TTL_COLUMN] = "300"; + data[DatabaseAccessor::RDATA_COLUMN] = "ns1.example.org. admin.example.org. " "1234 3600 1800 2419200 7200"; return (true); case 1: - data[0] = "x.example.org"; - data[1] = "A"; - data[2] = "300"; - data[3] = "192.0.2.1"; + data[DatabaseAccessor::NAME_COLUMN] = "x.example.org"; + data[DatabaseAccessor::TYPE_COLUMN] = "A"; + data[DatabaseAccessor::TTL_COLUMN] = "300"; + data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1"; return (true); case 2: - data[0] = "x.example.org"; - data[1] = "A"; - data[2] = "300"; - data[3] = "192.0.2.2"; + data[DatabaseAccessor::NAME_COLUMN] = "x.example.org"; + data[DatabaseAccessor::TYPE_COLUMN] = "A"; + data[DatabaseAccessor::TTL_COLUMN] = "300"; + data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2"; return (true); case 3: - data[0] = "x.example.org"; - data[1] = "AAAA"; - data[2] = "300"; - data[3] = "2001:db8::1"; + data[DatabaseAccessor::NAME_COLUMN] = "x.example.org"; + data[DatabaseAccessor::TYPE_COLUMN] = "AAAA"; + data[DatabaseAccessor::TTL_COLUMN] = "300"; + data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::1"; return (true); case 4: - data[0] = "x.example.org"; - data[1] = "AAAA"; - data[2] = "300"; - data[3] = "2001:db8::2"; + data[DatabaseAccessor::NAME_COLUMN] = "x.example.org"; + data[DatabaseAccessor::TYPE_COLUMN] = "AAAA"; + data[DatabaseAccessor::TTL_COLUMN] = "300"; + data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::2"; return (true); default: ADD_FAILURE() << @@ -137,7 +140,10 @@ private: }; class EmptyIteratorContext : public IteratorContext { public: - virtual bool getNext(string[4]) { + virtual bool getNext(string[], size_t size) { + if (size != DatabaseAccessor::COLUMN_COUNT) { + isc_throw(DataSourceError, "Wrong column count in getNextRecord"); + } return (false); } }; @@ -148,19 +154,22 @@ private: BadIteratorContext() : step(0) { } - virtual bool getNext(string data[4]) { + virtual bool getNext(string data[], size_t size) { + if (size != DatabaseAccessor::COLUMN_COUNT) { + isc_throw(DataSourceError, "Wrong column count in getNextRecord"); + } switch (step ++) { case 0: - data[0] = "x.example.org"; - data[1] = "A"; - data[2] = "300"; - data[3] = "192.0.2.1"; + data[DatabaseAccessor::NAME_COLUMN] = "x.example.org"; + data[DatabaseAccessor::TYPE_COLUMN] = "A"; + data[DatabaseAccessor::TTL_COLUMN] = "300"; + data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1"; return (true); case 1: - data[0] = "x.example.org"; - data[1] = "A"; - data[2] = "301"; - data[3] = "192.0.2.2"; + data[DatabaseAccessor::NAME_COLUMN] = "x.example.org"; + data[DatabaseAccessor::TYPE_COLUMN] = "A"; + data[DatabaseAccessor::TTL_COLUMN] = "301"; + data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2"; return (true); default: ADD_FAILURE() << diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index d3dcbfa502..89fc069248 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -114,16 +114,34 @@ TEST_F(SQLite3Access, iterator) { ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); - std::string data[4]; + size_t size(5); + std::string data[size]; // Get and check the first and only record - EXPECT_TRUE(context->getNext(data)); - EXPECT_EQ("example2.com.", data[0]); - EXPECT_EQ("SOA", data[1]); + EXPECT_TRUE(context->getNext(data, size)); + EXPECT_EQ("example2.com.", data[4]); + EXPECT_EQ("SOA", data[0]); EXPECT_EQ("master.example2.com. admin.example2.com. " "1234 3600 1800 2419200 7200", data[3]); - EXPECT_EQ("3600", data[2]); + EXPECT_EQ("3600", data[1]); // Check there's no other - EXPECT_FALSE(context->getNext(data)); + EXPECT_FALSE(context->getNext(data, size)); +} + +TEST_F(SQLite3Access, iteratorColumnCount) { + // Our test zone is conveniently small, but not empty + initAccessor(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); + + // Get the iterator context + DatabaseAccessor::IteratorContextPtr + context(db->getAllRecords(Name("example2.com"), 1)); + ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), + context); + + EXPECT_THROW(context->getNext(NULL, 0), DataSourceError); + std::string data[6]; + EXPECT_THROW(context->getNext(data, 4), DataSourceError); + EXPECT_THROW(context->getNext(data, 6), DataSourceError); + EXPECT_NO_THROW(context->getNext(data, 5)); } TEST(SQLite3Open, getDBNameExample2) { From cc004ec0ff327ca300cde89ffc252a9b1c588bec Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 12:02:46 +0200 Subject: [PATCH 529/974] [1067] Note about iterator and exception --- src/lib/datasrc/database.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index cccce0dddf..fa4125cf0a 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -114,6 +114,9 @@ public: * otherwise DataSourceError is thrown. * \todo Do we consider databases where it is stored in binary blob * format? + * \throw DataSourceError if there's database-related error. If the + * exception (or any other in case of derived class) is thrown, + * the iterator can't be safely used any more. */ virtual bool getNext(std::string columns[], size_t column_data) = 0; }; From 81613a741bcc9cbe909c814fab9ca99c1a1fc2fd Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 12:11:20 +0200 Subject: [PATCH 530/974] [1067] Note why we have friend --- src/lib/datasrc/memory_datasrc.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index b5193b7f51..6cd1753c18 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -182,6 +182,10 @@ private: struct InMemoryZoneFinderImpl; InMemoryZoneFinderImpl* impl_; //@} + // The friend here is for InMemoryClient::getIterator. The iterator + // needs to access the data inside the zone, so the InMemoryClient + // extracts the pointer to data and puts it into the iterator. + // The access is read only. friend class InMemoryClient; }; From 6df7102965c6afdec6f621175f9e91a56ee42a67 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 12:39:37 +0200 Subject: [PATCH 531/974] [1067] Added little bit of logging --- src/lib/datasrc/database.cc | 6 ++++++ src/lib/datasrc/datasrc_messages.mes | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 7affd70d34..d5e8f59be5 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -342,6 +342,8 @@ public: if (!data_ready_) { // At the end of zone ready_ = false; + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_ITERATE_END); return (ConstRRsetPtr()); } string name_str(name_), rtype_str(rtype_), ttl(ttl_); @@ -356,6 +358,8 @@ public: rrset->addRdata(rdata::createRdata(rtype, class_, rdata_)); getData(); } + LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT). + arg(rrset->getName()).arg(rrset->getType()); return (rrset); } private: @@ -404,6 +408,8 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const { // actual zone class from the connection, as the DatabaseClient // doesn't know it and the iterator needs it (so it wouldn't query // it each time) + LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE). + arg(name); return (ZoneIteratorPtr(new DatabaseIterator(context, RRClass::IN()))); } diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 6af4fe6678..ef1b26f8d3 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -103,6 +103,17 @@ The data returned by the database backend contained data for the given domain name, and it either matches the type or has a relevant type. The RRset that is returned is printed. +% DATASRC_DATABASE_ITERATE iterating zone %1 +The program is reading the whole zone, eg. not searching for data, but going +through each of the RRsets there. + +% DATASRC_DATABASE_ITERATE_END iterating zone finished +While iterating through the zone, the program reached end of the data. + +% DATASRC_DATABASE_ITERATE_NEXT next RRset in zone is %1/%2 +While iterating through the zone, the program extracted next RRset from it. +The name and RRtype of the RRset is indicated in the message. + % DATASRC_DO_QUERY handling query for '%1/%2' A debug message indicating that a query for the given name and RR type is being processed. From 3f5a0900a568436b011fc14b628b71bb130ae5f7 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 13:11:46 +0200 Subject: [PATCH 532/974] [1063] Split long test --- src/lib/datasrc/tests/database_unittest.cc | 320 +++++++++++---------- 1 file changed, 167 insertions(+), 153 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f4b5d0948c..5526bd4105 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -336,6 +336,21 @@ public: EXPECT_EQ(42, finder->zone_id()); EXPECT_EQ(current_database_, &finder->database()); } + + shared_ptr getFinder() { + DataSourceClient::FindResult zone( + client_->findZone(Name("example.org"))); + EXPECT_EQ(result::SUCCESS, zone.code); + shared_ptr finder( + dynamic_pointer_cast(zone.zone_finder)); + EXPECT_EQ(42, finder->zone_id()); + EXPECT_FALSE(current_database_->searchRunning()); + + return (finder); + } + + std::vector expected_rdatas_; + std::vector expected_sig_rdatas_; }; TEST_F(DatabaseClientTest, zoneNotFound) { @@ -388,24 +403,24 @@ doFindTest(shared_ptr finder, const isc::dns::RRType& expected_type, const isc::dns::RRTTL expected_ttl, ZoneFinder::Result expected_result, - const std::vector& expected_rdatas, - const std::vector& expected_sig_rdatas, + const std::vector& expected_rdatas_, + const std::vector& expected_sig_rdatas_, const isc::dns::Name& expected_name = isc::dns::Name::ROOT_NAME()) { SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText()); ZoneFinder::FindResult result = finder->find(name, type, NULL, ZoneFinder::FIND_DEFAULT); ASSERT_EQ(expected_result, result.code) << name << " " << type; - if (expected_rdatas.size() > 0) { + if (expected_rdatas_.size() > 0) { checkRRset(result.rrset, expected_name != Name(".") ? expected_name : name, finder->getClass(), expected_type, expected_ttl, - expected_rdatas); + expected_rdatas_); - if (expected_sig_rdatas.size() > 0) { + if (expected_sig_rdatas_.size() > 0) { checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ? expected_name : name, finder->getClass(), isc::dns::RRType::RRSIG(), expected_ttl, - expected_sig_rdatas); + expected_sig_rdatas_); } else { EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig()); } @@ -416,227 +431,220 @@ doFindTest(shared_ptr finder, } // end anonymous namespace TEST_F(DatabaseClientTest, find) { - DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); - ASSERT_EQ(result::SUCCESS, zone.code); - shared_ptr finder( - dynamic_pointer_cast(zone.zone_finder)); - EXPECT_EQ(42, finder->zone_id()); - EXPECT_FALSE(current_database_->searchRunning()); - std::vector expected_rdatas; - std::vector expected_sig_rdatas; + shared_ptr finder(getFinder()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.2"); doFindTest(finder, isc::dns::Name("www2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("2001:db8::1"); - expected_rdatas.push_back("2001:db8::2"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("2001:db8::1"); + expected_rdatas_.push_back("2001:db8::2"); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); doFindTest(finder, isc::dns::Name("www.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("www.example.org."); doFindTest(finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("www.example.org."); doFindTest(finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); doFindTest(finder, isc::dns::Name("doesnotexist.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("2001:db8::1"); - expected_rdatas.push_back("2001:db8::2"); - expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("2001:db8::1"); + expected_rdatas_.push_back("2001:db8::2"); + expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); doFindTest(finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); - expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("www.example.org."); + expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signedcname1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("2001:db8::2"); - expected_rdatas.push_back("2001:db8::1"); - expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("2001:db8::2"); + expected_rdatas_.push_back("2001:db8::1"); + expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); doFindTest(finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); - expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("www.example.org."); + expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("signedcname2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("acnamesig1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("acnamesig2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("acnamesig3.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.2"); doFindTest(finder, isc::dns::Name("ttldiff1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(360), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.2"); doFindTest(finder, isc::dns::Name("ttldiff2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(360), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); @@ -712,112 +720,118 @@ TEST_F(DatabaseClientTest, find) { // This RRSIG has the wrong sigtype field, which should be // an error if we decide to keep using that field // Right now the field is ignored, so it does not error - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("badsigtype.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); + expected_rdatas_, expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); +} + +TEST_F(DatabaseClientTest, findDelegation) { + shared_ptr finder(getFinder()); // The apex should not be considered delegation point and we can access // data - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); doFindTest(finder, isc::dns::Name("example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, - expected_sig_rdatas); + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, + expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); - expected_rdatas.push_back("ns.example.com."); - expected_sig_rdatas.push_back("NS 5 3 3600 20000101000000 20000201000000 " + expected_rdatas_.clear(); + expected_rdatas_.push_back("ns.example.com."); + expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 " "12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, - expected_sig_rdatas); + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, + expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); // Check when we ask for something below delegation point, we get the NS // (Both when the RRset there exists and doesn't) - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("ns.example.com."); - expected_rdatas.push_back("ns.delegation.example.org."); - expected_sig_rdatas.push_back("NS 5 3 3600 20000101000000 20000201000000 " + expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); + expected_rdatas_.push_back("ns.example.com."); + expected_rdatas_.push_back("ns.delegation.example.org."); + expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 " "12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::A(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas, - expected_sig_rdatas, isc::dns::Name("delegation.example.org.")); + isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, + expected_sig_rdatas_, + isc::dns::Name("delegation.example.org.")); EXPECT_FALSE(current_database_->searchRunning()); doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas, - expected_sig_rdatas, isc::dns::Name("delegation.example.org.")); + isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, + expected_sig_rdatas_, + isc::dns::Name("delegation.example.org.")); EXPECT_FALSE(current_database_->searchRunning()); // Even when we check directly at the delegation point, we should get // the NS doFindTest(finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas, - expected_sig_rdatas); + isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, + expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); // And when we ask direcly for the NS, we should still get delegation doFindTest(finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas, - expected_sig_rdatas); + isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, + expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); // Now test delegation. If it is below the delegation point, we should get // the DNAME (the one with data under DNAME is invalid zone, but we test // the behaviour anyway just to make sure) - expected_rdatas.clear(); - expected_rdatas.push_back("dname.example.com."); - expected_sig_rdatas.clear(); - expected_sig_rdatas.push_back("DNAME 5 3 3600 20000101000000 " + expected_rdatas_.clear(); + expected_rdatas_.push_back("dname.example.com."); + expected_sig_rdatas_.clear(); + expected_sig_rdatas_.push_back("DNAME 5 3 3600 20000101000000 " "20000201000000 12345 example.org. " "FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas, - expected_sig_rdatas, isc::dns::Name("dname.example.org.")); + isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, + expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); EXPECT_FALSE(current_database_->searchRunning()); doFindTest(finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas, - expected_sig_rdatas, isc::dns::Name("dname.example.org.")); + isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, + expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); EXPECT_FALSE(current_database_->searchRunning()); // Asking direcly for DNAME should give SUCCESS doFindTest(finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::DNAME(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, - expected_sig_rdatas); + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, + expected_sig_rdatas_); // But we don't delegate at DNAME point - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.clear(); + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_sig_rdatas_.clear(); doFindTest(finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas, - expected_sig_rdatas); + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, + expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); - expected_rdatas.clear(); + expected_rdatas_.clear(); doFindTest(finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas, - expected_sig_rdatas); + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, + expected_sig_rdatas_); EXPECT_FALSE(current_database_->searchRunning()); // This is broken dname, it contains two targets From b3bcd825cfb9c19a62a7db4d12717e85aca0b1e8 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 13:17:29 +0200 Subject: [PATCH 533/974] [1063] Few more tests --- src/lib/datasrc/tests/database_unittest.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 5526bd4105..9efb1dd421 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -272,6 +272,8 @@ private: addCurName("delegation.example.org."); addRecord("A", "3600", "", "192.0.2.1"); addCurName("ns.delegation.example.org."); + addRecord("A", "3600", "", "192.0.2.1"); + addCurName("deep.below.delegation.example.org."); addRecord("A", "3600", "", "192.0.2.1"); addRecord("DNAME", "3600", "", "dname.example.com."); @@ -775,6 +777,11 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("delegation.example.org.")); + doFindTest(finder, isc::dns::Name("deep.below.delegation.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), + isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, + expected_sig_rdatas_, + isc::dns::Name("delegation.example.org.")); EXPECT_FALSE(current_database_->searchRunning()); // Even when we check directly at the delegation point, we should get @@ -811,6 +818,11 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); EXPECT_FALSE(current_database_->searchRunning()); + doFindTest(finder, isc::dns::Name("really.deep.below.dname.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), + isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, + expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); + EXPECT_FALSE(current_database_->searchRunning()); // Asking direcly for DNAME should give SUCCESS doFindTest(finder, isc::dns::Name("dname.example.org."), From 4cbf309be8a302afe3bc041da11c24b593464157 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 13:29:59 +0200 Subject: [PATCH 534/974] [1063] Little bit of logging --- src/lib/datasrc/database.cc | 10 ++++++++++ src/lib/datasrc/datasrc_messages.mes | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 287602ab1f..b9d7330663 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -314,8 +314,14 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // (it can be only NS or DNAME here) result_rrset = found.second; if (result_rrset->getType() == isc::dns::RRType::NS()) { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_DELEGATION). + arg(superdomain); result_status = DELEGATION; } else { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_DNAME). + arg(superdomain); result_status = DNAME; } // Don't search more @@ -331,7 +337,11 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, records_found = found.first; result_rrset = found.second; if (result_rrset && name != origin && + result_rrset->getType() == isc::dns::RRType::NS()) { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_DELEGATION_EXACT). + arg(name); result_status = DELEGATION; } else if (result_rrset && type != isc::dns::RRType::CNAME() && result_rrset->getType() == isc::dns::RRType::CNAME()) { diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 6af4fe6678..a080a6a92c 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -90,6 +90,20 @@ most likely points to a logic error in the code, and can be considered a bug. The current search is aborted. Specific information about the exception is printed in this error message. +% DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %1 +When searching for a domain, the program met a delegation to a different zone +at the given domain name. It will return that one instead. + +% DATASRC_DATABASE_FOUND_DELEGATION_EXACT Found delegation at %1 (exact match) +The program found the domain requested, but it is a delegation point to a +different zone, therefore it is not authoritative for this domain name. +It will return the NS record instead. + +% DATASRC_DATABASE_FOUND_DNAME Found DNAME at %1 +When searching for a domain, the program met a DNAME redirection to a different +place in the domain space at the given domain name. It will return that one +instead. + % DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4 The data returned by the database backend did not contain any data for the given domain name, class and type. From b06a3e2ba1febb9e34458c5106f8d1629a191d5f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 13:45:52 +0200 Subject: [PATCH 535/974] [1064] Documentation update --- src/lib/datasrc/database.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index eaeecc57e8..95782ef3bb 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -268,7 +268,8 @@ public: * \param name The name to find * \param type The RRType to find * \param target Unused at this moment - * \param options Unused at this moment + * \param options Options about how to search. + * See ZoneFinder::FindOptions. */ virtual FindResult find(const isc::dns::Name& name, const isc::dns::RRType& type, From e074df43e95dc002374de30503ba44e203b04788 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 07:15:49 -0500 Subject: [PATCH 536/974] [master] add revision for entry 278 --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 56bf8e97d7..83cce337cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ 278. [doc] jelte Add logging configuration documentation to the guide. - (Trac #1011, git TODO) + (Trac #1011, git 2cc500af0929c1f268aeb6f8480bc428af70f4c4) 277. [func] jerry Implement the SRV rrtype according to RFC2782. From ed6fc7857e3fe7d64f19a0bed27226964009f095 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 16 Aug 2011 08:28:04 -0400 Subject: [PATCH 537/974] [1138] accessor added; source untabified --- src/lib/dns/rdata/in_1/dhcid_49.cc | 18 ++++++++++++------ src/lib/dns/rdata/in_1/dhcid_49.h | 10 +++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/lib/dns/rdata/in_1/dhcid_49.cc b/src/lib/dns/rdata/in_1/dhcid_49.cc index ce3b6193eb..b0a76f184f 100644 --- a/src/lib/dns/rdata/in_1/dhcid_49.cc +++ b/src/lib/dns/rdata/in_1/dhcid_49.cc @@ -43,15 +43,15 @@ DHCID::DHCID(const string& dhcid_str) { // < 2 octets > Identifier type code // < 1 octet > Digest type code if (digest_.size() < 3) { - isc_throw(InvalidRdataLength, "DHCID length " << digest_.size() << - " too short, need at least 3 bytes"); + isc_throw(InvalidRdataLength, "DHCID length " << digest_.size() << + " too short, need at least 3 bytes"); } } DHCID::DHCID(InputBuffer& buffer, size_t rdata_len) { if (rdata_len < 3) { - isc_throw(InvalidRdataLength, "DHCID length " << rdata_len << - " too short, need at least 3 bytes"); + isc_throw(InvalidRdataLength, "DHCID length " << rdata_len << + " too short, need at least 3 bytes"); } digest_.resize(rdata_len); @@ -85,10 +85,16 @@ DHCID::compare(const Rdata& other) const { size_t cmplen = min(this_len, other_len); int cmp = memcmp(&digest_[0], &other_dhcid.digest_[0], cmplen); if (cmp != 0) { - return (cmp); + return (cmp); } else { - return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); } } + +const std::vector& +DHCID::getDigest() const { + return (digest_); +} + // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/dhcid_49.h b/src/lib/dns/rdata/in_1/dhcid_49.h index 9792bb6c48..666f2837ed 100644 --- a/src/lib/dns/rdata/in_1/dhcid_49.h +++ b/src/lib/dns/rdata/in_1/dhcid_49.h @@ -31,10 +31,14 @@ public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS - //We can use the default destructor. - //virtual ~DHCID() {} + // subject to change + // DHCID& operator=(const DHCID& source); + // ~DHCID(); + + const std::vector& getDigest() const; + private: - std::vector digest_; // opaque data at least 3 octets long + std::vector digest_; // opaque data at least 3 octets long }; // END_RDATA_NAMESPACE // END_ISC_NAMESPACE From c74d3b7f393f3934bae22fc9d3a4a49e2211aadb Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 07:33:45 -0500 Subject: [PATCH 538/974] [jreed-docs-2] fix typo --- doc/guide/bind10-guide.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 59914cfd89..ef4a18612b 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -547,7 +547,7 @@ Debian and Ubuntu: --prefix - Define the the installation location (the + Define the installation location (the default is /usr/local/). From 810c79d6d9b8efbc12ec8e1ad727cf002f2dedc6 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 07:35:13 -0500 Subject: [PATCH 539/974] [jreed-docs-2] reformat some long lines (no content change) --- doc/guide/bind10-guide.xml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index ef4a18612b..87c6ac1bde 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1599,14 +1599,15 @@ then change those defaults with config set Resolver/forward_addresses[0]/address error 111 opening TCP socket to 127.0.0.1(53) - A brief description of the cause of the problem. Within this text, - information relating to the condition that caused the message to - be logged will be included. In this example, error number 111 - (an operating system-specific error number) was encountered when - trying to open a TCP connection to port 53 on the local system - (address 127.0.0.1). The next step would be to find out the reason - for the failure by consulting your system's documentation to - identify what error number 111 means. + A brief description of the cause of the problem. + Within this text, information relating to the condition + that caused the message to be logged will be included. + In this example, error number 111 (an operating + system-specific error number) was encountered when trying + to open a TCP connection to port 53 on the local system + (address 127.0.0.1). The next step would be to find + out the reason for the failure by consulting your system's + documentation to identify what error number 111 means. From 5253640054d48f7816aa00c803f5bc593c0c12c1 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Tue, 16 Aug 2011 21:15:26 +0800 Subject: [PATCH 540/974] [master] merge #1114: Implement AFSDB rrtype --- ChangeLog | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 83cce337cf..24134fd76c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,13 @@ +279. [func] jerry + libdns++: Implement the AFSDB rrtype according to RFC1183. + (Trac #1114, git TODO) + 278. [doc] jelte Add logging configuration documentation to the guide. (Trac #1011, git 2cc500af0929c1f268aeb6f8480bc428af70f4c4) 277. [func] jerry - Implement the SRV rrtype according to RFC2782. + libdns++: Implement the SRV rrtype according to RFC2782. (Trac #1128, git 5fd94aa027828c50e63ae1073d9d6708e0a9c223) 276. [func] stephen From 6ad78d124740f1ea18f6f93721ec6f152364e878 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Tue, 16 Aug 2011 21:18:01 +0800 Subject: [PATCH 541/974] [master] update ChangeLog for #1114 --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 24134fd76c..b58cb7ac2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ 279. [func] jerry libdns++: Implement the AFSDB rrtype according to RFC1183. - (Trac #1114, git TODO) + (Trac #1114, git ce052cd92cd128ea3db5a8f154bd151956c2920c) 278. [doc] jelte Add logging configuration documentation to the guide. From 691c232b2655673ac352beafc0bfba4bc966f8f8 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 08:20:02 -0500 Subject: [PATCH 542/974] [master] clarify reset and remove even those these may be removed from stats soon. Also add some TODO comments --- src/bin/stats/b10-stats.xml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index 13e568df63..445ac4393c 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -115,9 +115,11 @@ + remove removes the named statistics name and data. - reset + + reset will reset all statistics data to + default values except for constant names. + This may re-add previously removed statistics names. set + @@ -161,6 +168,8 @@ when starts collecting data An optional item name may be specified to receive individual output. + + shutdown will shutdown the b10-stats process. From 9df1f04f8b1f7091ab32dcd56fb6e47e3e96d5a7 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 08:22:18 -0500 Subject: [PATCH 543/974] [master] moved the STATISTICS section to after the configuration section and renamed it --- src/bin/stats/b10-stats.xml | 87 ++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index 445ac4393c..f2b6c03de0 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -87,50 +87,6 @@ - - DEFAULT STATISTICS - - - The b10-stats daemon contains - built-in statistics: - - - - - - report_time - - The latest report date and time in - ISO 8601 format. - - - - stats.timestamp - The current date and time represented in - seconds since UNIX epoch (1970-01-01T0 0:00:00Z) with - precision (delimited with a period) up to - one hundred thousandth of second. - - - - - - - - - - CONFIGURATION AND COMMANDS @@ -183,6 +139,49 @@ when starts collecting data + + STATISTICS DATA + + + The b10-stats daemon contains + built-in statistics: + + + + + + report_time + + The latest report date and time in + ISO 8601 format. + + + + stats.timestamp + The current date and time represented in + seconds since UNIX epoch (1970-01-01T0 0:00:00Z) with + precision (delimited with a period) up to + one hundred thousandth of second. + + + + + + + + + FILES From 0fe4f0151ae7a994aaf305e7985d4ba9f992e482 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 15:28:52 +0200 Subject: [PATCH 544/974] [1063] Name of DB in log messages --- src/lib/datasrc/database.cc | 6 +++--- src/lib/datasrc/datasrc_messages.mes | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index b9d7330663..166a1d2fb7 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -316,12 +316,12 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, if (result_rrset->getType() == isc::dns::RRType::NS()) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DELEGATION). - arg(superdomain); + arg(database_->getDBName()).arg(superdomain); result_status = DELEGATION; } else { LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DNAME). - arg(superdomain); + arg(database_->getDBName()).arg(superdomain); result_status = DNAME; } // Don't search more @@ -341,7 +341,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_rrset->getType() == isc::dns::RRType::NS()) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DELEGATION_EXACT). - arg(name); + arg(database_->getDBName()).arg(name); result_status = DELEGATION; } else if (result_rrset && type != isc::dns::RRType::CNAME() && result_rrset->getType() == isc::dns::RRType::CNAME()) { diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index a080a6a92c..190adbe3ac 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -90,16 +90,16 @@ most likely points to a logic error in the code, and can be considered a bug. The current search is aborted. Specific information about the exception is printed in this error message. -% DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %1 +% DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %2 in %1 When searching for a domain, the program met a delegation to a different zone at the given domain name. It will return that one instead. -% DATASRC_DATABASE_FOUND_DELEGATION_EXACT Found delegation at %1 (exact match) +% DATASRC_DATABASE_FOUND_DELEGATION_EXACT Found delegation at %2 (exact match) in %1 The program found the domain requested, but it is a delegation point to a different zone, therefore it is not authoritative for this domain name. It will return the NS record instead. -% DATASRC_DATABASE_FOUND_DNAME Found DNAME at %1 +% DATASRC_DATABASE_FOUND_DNAME Found DNAME at %2 in %1 When searching for a domain, the program met a DNAME redirection to a different place in the domain space at the given domain name. It will return that one instead. From 98e74ad62b23ce33f66e3841431511136bc1c2f8 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 08:40:30 -0500 Subject: [PATCH 545/974] [master] document other b10-stats statistics --- src/bin/stats/b10-stats.xml | 55 +++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index f2b6c03de0..1164711a8e 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -143,8 +143,7 @@ STATISTICS DATA - The b10-stats daemon contains - built-in statistics: + The b10-stats daemon contains these statistics: @@ -156,6 +155,38 @@ ISO 8601 format. + + stats.boot_time + The date and time when this daemon was + started in ISO 8601 format. + This is a constant which can't be reset except by restarting + b10-stats. + + + + + stats.last_update_time + The date and time (in ISO 8601 format) + when this daemon last received data from another component. + + + + + stats.lname + This is the name used for the + b10-msgq command-control channel. + (This is a constant which can't be reset except by restarting + b10-stats.) + + + + + stats.start_time + This is the date and time (in ISO 8601 format) + when this daemon started collecting data. + + + stats.timestamp The current date and time represented in @@ -164,23 +195,13 @@ one hundred thousandth of second. - - - - + + See other manual pages for explanations for their statistics + that are kept track by b10-stats. + + From 09e8c50958a1fca313c2be427c2991c39798f90f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 16 Aug 2011 16:07:10 +0200 Subject: [PATCH 546/974] Fix lexical cast (missing boost::) Reviewed on jabber. --- src/lib/dns/rdata/generic/afsdb_18.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc index 0aca23f133..dd7fa5f861 100644 --- a/src/lib/dns/rdata/generic/afsdb_18.cc +++ b/src/lib/dns/rdata/generic/afsdb_18.cc @@ -23,6 +23,8 @@ #include #include +#include + using namespace std; using namespace isc::util::str; @@ -109,7 +111,7 @@ AFSDB::operator=(const AFSDB& source) { /// \return A \c string object that represents the \c AFSDB object. string AFSDB::toText() const { - return (lexical_cast(subtype_) + " " + server_.toText()); + return (boost::lexical_cast(subtype_) + " " + server_.toText()); } /// \brief Render the \c AFSDB in the wire format without name compression. From 1e702fae4c9adbd7134a739dee28c868a15f0b3e Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 11:03:20 -0500 Subject: [PATCH 547/974] [master] fix typo in message file expansion ordering the RRsets and class were reversed. This was the only example in all .mes files that had the numbers out of order. Okayed via jabber. This fixes: 2011-08-15 14:37:22.991 DEBUG [b10-resolver.cache] CACHE_RRSET_INIT initializing RRset cache for IN RRsets of class 10000 to: 2011-08-16 11:01:50.899 DEBUG [b10-resolver.cache] CACHE_RRSET_INIT initializing RRset cache for 10000 RRsets of class IN One thing to note: If this was a production server, we would need to consider deprecating this message ID and creating a new message ID as use of the logged results would be broken, because problem is in the logged brief explanation and not the full description. I will send an email about this. --- src/lib/cache/cache_messages.mes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 2a68cc23bf..7f593ec6e6 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -124,7 +124,7 @@ the message will not be cached. Debug message. The requested data was found in the RRset cache. However, it is expired, so the cache removed it and is going to pretend nothing was found. -% CACHE_RRSET_INIT initializing RRset cache for %2 RRsets of class %1 +% CACHE_RRSET_INIT initializing RRset cache for %1 RRsets of class %2 Debug message. The RRset cache to hold at most this many RRsets for the given class is being created. From 7cdda20613f7ed7b18e7fe210ae0f6a87054dbf3 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 11:50:17 -0500 Subject: [PATCH 548/974] [master] update verbose explanation, document query_acl, add some history The query_acl now has some beginning docs here, but needs more. --- src/bin/resolver/b10-resolver.xml | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/bin/resolver/b10-resolver.xml b/src/bin/resolver/b10-resolver.xml index bdf4f8ad25..efe045a5f3 100644 --- a/src/bin/resolver/b10-resolver.xml +++ b/src/bin/resolver/b10-resolver.xml @@ -20,7 +20,7 @@ - February 17, 2011 + August 16, 2011 @@ -99,11 +99,14 @@ + + - Enabled verbose mode. This enables diagnostic messages to - STDERR. + Enable verbose mode. + This sets logging to the maximum debugging level. @@ -146,6 +149,22 @@ once that is merged you can for instance do 'config add Resolver/forward_address + + + + + + + query_acl is a list of query access control + rules. The list items are the action string + and the from or key strings. + The possible actions are ACCEPT, REJECT and DROP. + The from is a remote (source) IPv4 or IPv6 + address or special keyword. + The key is a TSIG key name. + The default configuration accepts queries from 127.0.0.1 and ::1. + + retries is the number of times to retry (resend query) after a query timeout @@ -234,7 +253,8 @@ once that is merged you can for instance do 'config add Resolver/forward_address The b10-resolver daemon was first coded in September 2010. The initial implementation only provided forwarding. Iteration was introduced in January 2011. - + Caching was implemented in February 2011. + Access control was introduced in June 2011. From 6a55aa002c8f3b701dbb8291cd9a8e21534c6974 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 12:12:48 -0500 Subject: [PATCH 549/974] [master] add the statistics date While here also sort the configurations (no content change for that). Regenerate the nroff file. --- src/bin/auth/b10-auth.8 | 47 +++++++++++++++++++++++++++------------ src/bin/auth/b10-auth.xml | 18 +++++++-------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/bin/auth/b10-auth.8 b/src/bin/auth/b10-auth.8 index 0356683b11..aedadeefb0 100644 --- a/src/bin/auth/b10-auth.8 +++ b/src/bin/auth/b10-auth.8 @@ -2,12 +2,12 @@ .\" Title: b10-auth .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" Date: March 8, 2011 +.\" Date: August 11, 2011 .\" Manual: BIND10 .\" Source: BIND10 .\" Language: English .\" -.TH "B10\-AUTH" "8" "March 8, 2011" "BIND10" "BIND10" +.TH "B10\-AUTH" "8" "August 11, 2011" "BIND10" "BIND10" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -70,18 +70,6 @@ defines the path to the SQLite3 zone file when using the sqlite datasource\&. Th /usr/local/var/bind10\-devel/zone\&.sqlite3\&. .PP -\fIlisten_on\fR -is a list of addresses and ports for -\fBb10\-auth\fR -to listen on\&. The list items are the -\fIaddress\fR -string and -\fIport\fR -number\&. By default, -\fBb10\-auth\fR -listens on port 53 on the IPv6 (::) and IPv4 (0\&.0\&.0\&.0) wildcard addresses\&. -.PP - \fIdatasources\fR configures data sources\&. The list items include: \fItype\fR @@ -114,6 +102,18 @@ In this development version, currently this is only used for the memory data sou .RE .PP +\fIlisten_on\fR +is a list of addresses and ports for +\fBb10\-auth\fR +to listen on\&. The list items are the +\fIaddress\fR +string and +\fIport\fR +number\&. By default, +\fBb10\-auth\fR +listens on port 53 on the IPv6 (::) and IPv4 (0\&.0\&.0\&.0) wildcard addresses\&. +.PP + \fIstatistics\-interval\fR is the timer interval in seconds for \fBb10\-auth\fR @@ -164,6 +164,25 @@ immediately\&. \fBshutdown\fR exits \fBb10\-auth\fR\&. (Note that the BIND 10 boss process will restart this service\&.) +.SH "STATISTICS DATA" +.PP +The statistics data collected by the +\fBb10\-stats\fR +daemon include: +.PP +auth\&.queries\&.tcp +.RS 4 +Total count of queries received by the +\fBb10\-auth\fR +server over TCP since startup\&. +.RE +.PP +auth\&.queries\&.udp +.RS 4 +Total count of queries received by the +\fBb10\-auth\fR +server over UDP since startup\&. +.RE .SH "FILES" .PP diff --git a/src/bin/auth/b10-auth.xml b/src/bin/auth/b10-auth.xml index a05be586b0..636f437993 100644 --- a/src/bin/auth/b10-auth.xml +++ b/src/bin/auth/b10-auth.xml @@ -131,15 +131,6 @@ /usr/local/var/bind10-devel/zone.sqlite3. - - listen_on is a list of addresses and ports for - b10-auth to listen on. - The list items are the address string - and port number. - By default, b10-auth listens on port 53 - on the IPv6 (::) and IPv4 (0.0.0.0) wildcard addresses. - - datasources configures data sources. The list items include: @@ -164,6 +155,15 @@ + + listen_on is a list of addresses and ports for + b10-auth to listen on. + The list items are the address string + and port number. + By default, b10-auth listens on port 53 + on the IPv6 (::) and IPv4 (0.0.0.0) wildcard addresses. + + statistics-interval is the timer interval in seconds for b10-auth to share its From 485e0ba7f7fe11e4d28e3eec2be835157521a6e9 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 16 Aug 2011 12:35:37 -0500 Subject: [PATCH 550/974] [master] mention class and its default --- src/bin/xfrin/b10-xfrin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml index 71fcf931ca..a8fe425d37 100644 --- a/src/bin/xfrin/b10-xfrin.xml +++ b/src/bin/xfrin/b10-xfrin.xml @@ -103,7 +103,7 @@ in separate zonemgr process. b10-xfrin daemon. The list items are: name (the zone name), - + class (defaults to IN), master_addr (the zone master to transfer from), master_port (defaults to 53), and tsig_key (optional TSIG key to use). From a7fe0d5982813f092f8a497d350620c02b995649 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 16 Aug 2011 18:17:13 -0700 Subject: [PATCH 551/974] [1068] made RRSIG::typeCovered() a const member function. not directly related to this branch, but it's the right thing. --- src/lib/dns/rdata/generic/rrsig_46.cc | 2 +- src/lib/dns/rdata/generic/rrsig_46.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc index fc8e3400c9..59ff030541 100644 --- a/src/lib/dns/rdata/generic/rrsig_46.cc +++ b/src/lib/dns/rdata/generic/rrsig_46.cc @@ -244,7 +244,7 @@ RRSIG::compare(const Rdata& other) const { } const RRType& -RRSIG::typeCovered() { +RRSIG::typeCovered() const { return (impl_->covered_); } diff --git a/src/lib/dns/rdata/generic/rrsig_46.h b/src/lib/dns/rdata/generic/rrsig_46.h index b8e630631e..b32c17f86b 100644 --- a/src/lib/dns/rdata/generic/rrsig_46.h +++ b/src/lib/dns/rdata/generic/rrsig_46.h @@ -40,7 +40,7 @@ public: ~RRSIG(); // specialized methods - const RRType& typeCovered(); + const RRType& typeCovered() const; private: RRSIGImpl* impl_; }; From 13e236a3d647d15858b061c7d96288bf7407e090 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 16 Aug 2011 18:31:09 -0700 Subject: [PATCH 552/974] [1068] implemented adding RRsets --- src/lib/datasrc/database.cc | 66 ++++- src/lib/datasrc/database.h | 19 +- src/lib/datasrc/tests/database_unittest.cc | 281 ++++++++++++++++++--- src/lib/datasrc/zone.h | 21 +- 4 files changed, 331 insertions(+), 56 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index e68173eb7c..a7d63c5803 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -31,8 +32,8 @@ using namespace std; using boost::shared_ptr; -using isc::dns::Name; -using isc::dns::RRClass; +using namespace isc::dns; +using namespace isc::dns::rdata; namespace isc { namespace datasrc { @@ -327,30 +328,31 @@ DatabaseClient::startUpdateZone(const isc::dns::Name& name, } return (ZoneUpdaterPtr(new Updater(accessor_, zone.second, - name.toText(), rrclass_.toText()))); + name.toText(), rrclass_))); } DatabaseClient::Updater::Updater(shared_ptr accessor, int zone_id, const string& zone_name, - const string& class_name) : + const RRClass& zone_class) : committed_(false), accessor_(accessor), zone_id_(zone_id), db_name_(accessor->getDBName()), zone_name_(zone_name), - class_name_(class_name), - finder_(new Finder(accessor_, zone_id_)) + zone_class_(zone_class), + finder_(new Finder(accessor_, zone_id_)), + add_columns_(DatabaseAccessor::ADD_COLUMN_COUNT) { logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED) - .arg(zone_name_).arg(class_name_).arg(db_name_); + .arg(zone_name_).arg(zone_class_).arg(db_name_); } DatabaseClient::Updater::~Updater() { if (!committed_) { accessor_->rollbackUpdateZone(); logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK) - .arg(zone_name_).arg(class_name_).arg(db_name_); + .arg(zone_name_).arg(zone_class_).arg(db_name_); } logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED) - .arg(zone_name_).arg(class_name_).arg(db_name_); + .arg(zone_name_).arg(zone_class_).arg(db_name_); } ZoneFinder& @@ -358,11 +360,53 @@ DatabaseClient::Updater::getFinder() { return (*finder_); } +void +DatabaseClient::Updater::addRRset(const RRset& rrset) { + if (committed_) { + isc_throw(DataSourceError, "Add attempt after commit to zone: " + << zone_name_ << "/" << zone_class_); + } + if (rrset.getClass() != zone_class_) { + isc_throw(DataSourceError, "An RRset of a different class is being " + << "added to " << zone_name_ << "/" << zone_class_ << ": " + << rrset.toText()); + } + + RdataIteratorPtr it = rrset.getRdataIterator(); + if (it->isLast()) { + isc_throw(DataSourceError, "An empty RRset is being added for " + << rrset.getName() << "/" << zone_class_ << "/" + << rrset.getType()); + } + + add_columns_.clear(); + add_columns_[DatabaseAccessor::ADD_NAME] = rrset.getName().toText(); + add_columns_[DatabaseAccessor::ADD_REV_NAME] = + rrset.getName().reverse().toText(); + add_columns_[DatabaseAccessor::ADD_TTL] = rrset.getTTL().toText(); + add_columns_[DatabaseAccessor::ADD_TYPE] = rrset.getType().toText(); + for (; !it->isLast(); it->next()) { + if (rrset.getType() == RRType::RRSIG()) { + // XXX: the current interface (based on the current sqlite3 + // data source schema) requires a separate "sigtype" column, + // even though it won't be used in a newer implementation. + // We should eventually clean up the schema design and simplify + // the interface, but until then we have to conform to the schema. + const generic::RRSIG& rrsig_rdata = + dynamic_cast(it->getCurrent()); + add_columns_[DatabaseAccessor::ADD_SIGTYPE] = + rrsig_rdata.typeCovered().toText(); + } + add_columns_[DatabaseAccessor::ADD_RDATA] = it->getCurrent().toText(); + accessor_->addRecordToZone(add_columns_); + } +} + void DatabaseClient::Updater::commit() { if (committed_) { isc_throw(DataSourceError, "Duplicate commit attempt for " - << zone_name_ << "/" << class_name_ << " on " + << zone_name_ << "/" << zone_class_ << " on " << db_name_); } accessor_->commitUpdateZone(); @@ -374,7 +418,7 @@ DatabaseClient::Updater::commit() { committed_ = true; logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_COMMIT) - .arg(zone_name_).arg(class_name_).arg(db_name_); + .arg(zone_name_).arg(zone_class_).arg(db_name_); } } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 8e5d7e9783..3a9292cc50 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -20,6 +20,8 @@ #include #include +#include +#include #include @@ -153,6 +155,16 @@ public: RDATA_COLUMN = 3 ///< Full text representation of the record's RDATA }; + enum AddRecordColumns { + ADD_NAME = 0, ///< The owner name of the record (a domain name) + ADD_REV_NAME = 1, ///< Reversed name of NAME (used for DNSSEC) + ADD_TTL = 2, ///< The TTL of the record (an integer) + ADD_TYPE = 3, ///< The RRType of the record (A/NS/TXT etc.) + ADD_SIGTYPE = 4, ///< For RRSIG records, this contains the RRTYPE + ///< the RRSIG covers. + ADD_RDATA = 5 ///< Full text representation of the record's RDATA + }; + /// TBD virtual std::pair startUpdateZone(const std::string& zone_name, bool replace) = 0; @@ -345,9 +357,11 @@ public: class Updater : public ZoneUpdater { public: Updater(boost::shared_ptr database, int zone_id, - const std::string& zone_name, const std::string& class_name); + const std::string& zone_name, + const isc::dns::RRClass& zone_class); ~Updater(); virtual ZoneFinder& getFinder(); + virtual void addRRset(const isc::dns::RRset& rrset); virtual void commit(); private: @@ -356,8 +370,9 @@ public: const int zone_id_; std::string db_name_; std::string zone_name_; - std::string class_name_; + isc::dns::RRClass zone_class_; boost::scoped_ptr finder_; + std::vector add_columns_; }; /** diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index fad4cba181..570118904e 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -12,9 +12,12 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + #include #include +#include #include #include #include @@ -30,10 +33,7 @@ using namespace isc::datasrc; using namespace std; using namespace boost; -using isc::dns::Name; -using isc::dns::RRType; -using isc::dns::RRClass; -using isc::dns::RRTTL; +using namespace isc::dns; namespace { @@ -119,6 +119,47 @@ public: search_running_ = false; } + virtual pair startUpdateZone(const std::string& zone_name, + bool replace) + { + const pair zone_info = getZone(zone_name); + if (!zone_info.first) { + return (pair(false, 0)); + } + + // Prepare the record set for update. If replacing the existing one, + // we use an empty set; otherwise we use a writable copy of the + // original. + if (replace) { + update_records.clear(); + } else { + update_records = readonly_records; + } + + return (pair(true, WRITABLE_ZONE_ID)); + } + virtual void commitUpdateZone() { + readonly_records = update_records; + } + virtual void rollbackUpdateZone() { + rollbacked_ = true; + } + virtual void addRecordToZone(const vector& columns) { + // Copy the current value to cur_name. If it doesn't exist, + // operator[] will create a new one. + cur_name = update_records[columns[DatabaseAccessor::ADD_NAME]]; + addRecord(columns[DatabaseAccessor::ADD_TYPE], + columns[DatabaseAccessor::ADD_TTL], + columns[DatabaseAccessor::ADD_SIGTYPE], + columns[DatabaseAccessor::ADD_RDATA]); + // copy back the added entry. + update_records[columns[DatabaseAccessor::ADD_NAME]] = cur_name; + + // remember this one so that test cases can check it. + columns_lastadded = columns; + } + virtual void deleteRecordInZone(const vector&) {} + bool searchRunning() const { return (search_running_); } @@ -130,6 +171,10 @@ public: virtual const std::string& getDBName() const { return (database_name_); } + + const vector& getLastAdded() const { + return (columns_lastadded); + } private: RECORDS readonly_records; RECORDS update_records; @@ -142,6 +187,9 @@ private: // fake data std::vector< std::vector > cur_name; + // The columns that were most recently added via addRecordToZone() + vector columns_lastadded; + // This boolean is used to make sure find() calls resetSearch // when it encounters an error bool search_running_; @@ -168,13 +216,13 @@ private: // Adds one record to the current name in the database // The actual data will not be added to 'records' until // addCurName() is called - void addRecord(const std::string& name, - const std::string& type, + void addRecord(const std::string& type, + const std::string& ttl, const std::string& sigtype, const std::string& rdata) { std::vector columns; - columns.push_back(name); columns.push_back(type); + columns.push_back(ttl); columns.push_back(sigtype); columns.push_back(rdata); cur_name.push_back(columns); @@ -292,40 +340,33 @@ private: addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("badsigtype.example.org."); } - - virtual pair startUpdateZone(const std::string& zone_name, - bool replace) - { - const pair zone_info = getZone(zone_name); - if (!zone_info.first) { - return (pair(false, 0)); - } - - // Prepare the record set for update. If replacing the existing one, - // we use an empty set; otherwise we use a writable copy of the - // original. - if (replace) { - update_records.clear(); - } else { - update_records = readonly_records; - } - - return (pair(true, WRITABLE_ZONE_ID)); - } - virtual void commitUpdateZone() { - readonly_records = update_records; - } - virtual void rollbackUpdateZone() { - rollbacked_ = true; - } - virtual void addRecordToZone(const vector&) {} - virtual void deleteRecordInZone(const vector&) {} }; class DatabaseClientTest : public ::testing::Test { protected: - DatabaseClientTest() : qname("www.example.org"), qtype("A") { + DatabaseClientTest() : qname("www.example.org"), qtype("A"), rrttl(3600) { createClient(); + + // set up the commonly used finder. + DataSourceClient::FindResult zone( + client_->findZone(Name("example.org"))); + assert(zone.code == result::SUCCESS); + finder = dynamic_pointer_cast( + zone.zone_finder); + + rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); + // Adding an IN/A RDATA. Intentionally using different data + // than the initial data configured MockAccessor::fillData(). + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "192.0.2.2")); + + rrsigset.reset(new RRset(qname, RRClass::IN(), RRType::RRSIG(), + rrttl)); + rrsigset->addRdata(rdata::createRdata(rrsigset->getType(), + rrsigset->getClass(), + "A 5 3 0 20000101000000 " + "20000201000000 0 example.org. " + "FAKEFAKEFAKE")); } /* * We initialize the client from a function, so we can call it multiple @@ -342,8 +383,14 @@ protected: shared_ptr client_; const std::string database_name_; + // The zone finder of the test zone commonly used in various tests. + shared_ptr finder; + const Name qname; // commonly used name to be found const RRType qtype; // commonly used RR type with qname + const RRTTL rrttl; // commonly used RR TTL + RRsetPtr rrset; // for adding/deleting an RRset + RRsetPtr rrsigset; // for adding/deleting an RRset ZoneUpdaterPtr updater; const std::vector empty_rdatas; // for NXRRSET/NXDOMAIN @@ -450,10 +497,6 @@ doFindTest(ZoneFinder& finder, } // end anonymous namespace TEST_F(DatabaseClientTest, find) { - DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); - ASSERT_EQ(result::SUCCESS, zone.code); - shared_ptr finder( - dynamic_pointer_cast(zone.zone_finder)); EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); EXPECT_FALSE(current_accessor_->searchRunning()); @@ -766,7 +809,7 @@ TEST_F(DatabaseClientTest, updaterFinder) { expected_rdatas.clear(); expected_rdatas.push_back("192.0.2.1"); doFindTest(updater->getFinder(), Name("www.example.org."), - RRType::A(), RRType::A(), RRTTL(3600), ZoneFinder::SUCCESS, + qtype, qtype, rrttl, ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); // When replacing the zone, the updater's finder shouldn't see anything @@ -829,6 +872,160 @@ TEST_F(DatabaseClientTest, duplicateCommit) { EXPECT_THROW(updater->commit(), DataSourceError); } -// add/delete after commit. should error +TEST_F(DatabaseClientTest, addRRsetToNewZone) { + // Add a single RRset to a fresh empty zone + updater = client_->startUpdateZone(Name("example.org"), true); + updater->addRRset(*rrset); + + expected_rdatas.clear(); + expected_rdatas.push_back("192.0.2.2"); + { + SCOPED_TRACE("add RRset"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } + + // Similar to the previous case, but with RRSIG + updater = client_->startUpdateZone(Name("example.org"), true); + updater->addRRset(*rrset); + updater->addRRset(*rrsigset); + + // confirm the expected columns were passed to the mock accessor. + const char* const rrsig_added[] = { + "www.example.org.", "org.example.www.", "3600", "RRSIG", "A", + "A 5 3 0 20000101000000 20000201000000 0 example.org. FAKEFAKEFAKE" + }; + int i = 0; + BOOST_FOREACH(const string& column, current_accessor_->getLastAdded()) { + EXPECT_EQ(rrsig_added[i++], column); + } + + expected_sig_rdatas.clear(); + expected_sig_rdatas.push_back(rrsig_added[DatabaseAccessor::ADD_RDATA]); + { + SCOPED_TRACE("add RRset with RRSIG"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); + } +} + +TEST_F(DatabaseClientTest, addRRsetToCurrentZone) { + // Similar to the previous test, but not replacing the existing data. + updater = client_->startUpdateZone(Name("example.org"), false); + updater->addRRset(*rrset); + + // We should see both old and new data. + expected_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_rdatas.push_back("192.0.2.2"); + { + SCOPED_TRACE("add RRset"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } + updater->commit(); + { + SCOPED_TRACE("add RRset after commit"); + doFindTest(*finder, qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, addRRsetOfLargerTTL) { + // Similar to the previous one, but the TTL of the added RRset is larger + // than that of the existing record. The finder should use the smaller + // one. + updater = client_->startUpdateZone(Name("example.org"), false); + rrset->setTTL(RRTTL(7200)); + updater->addRRset(*rrset); + + expected_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_rdatas.push_back("192.0.2.2"); + { + SCOPED_TRACE("add RRset of larger TTL"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, addRRsetOfSmallerTTL) { + // Similar to the previous one, but the added RRset has a smaller TTL. + // The added TTL should be used by the finder. + updater = client_->startUpdateZone(Name("example.org"), false); + rrset->setTTL(RRTTL(1800)); + updater->addRRset(*rrset); + + expected_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_rdatas.push_back("192.0.2.2"); + { + SCOPED_TRACE("add RRset of smaller TTL"); + doFindTest(updater->getFinder(), qname, qtype, qtype, RRTTL(1800), + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, addSameRR) { + // Add the same RR as that is already in the data source. + // Currently the add interface doesn't try to suppress the duplicate, + // neither does the finder. We may want to revisit it in future versions. + + updater = client_->startUpdateZone(Name("example.org"), false); + rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "192.0.2.1")); + updater->addRRset(*rrset); + expected_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_rdatas.push_back("192.0.2.1"); + { + SCOPED_TRACE("add same RR"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, addDeviantRR) { + updater = client_->startUpdateZone(Name("example.org"), false); + + // RR class mismatch. This should be detected and rejected. + rrset.reset(new RRset(qname, RRClass::CH(), RRType::TXT(), rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "test text")); + EXPECT_THROW(updater->addRRset(*rrset), DataSourceError); + + // Out-of-zone owner name. At a higher level this should be rejected, + // but it doesn't happen in this interface. + rrset.reset(new RRset(Name("example.com"), RRClass::IN(), qtype, rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "192.0.2.100")); + updater->addRRset(*rrset); + + expected_rdatas.clear(); + expected_rdatas.push_back("192.0.2.100"); + { + // Note: with the find() implementation being more strict about + // zone cuts, this test may fail. Then the test should be updated. + SCOPED_TRACE("add out-of-zone RR"); + doFindTest(updater->getFinder(), Name("example.com"), qtype, qtype, + rrttl, ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, addEmptyRRset) { + updater = client_->startUpdateZone(Name("example.org"), false); + rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); + EXPECT_THROW(updater->addRRset(*rrset), DataSourceError); +} + +TEST_F(DatabaseClientTest, addAfterCommit) { + updater = client_->startUpdateZone(Name("example.org"), false); + updater->commit(); + EXPECT_THROW(updater->addRRset(*rrset), DataSourceError); +} + +// delete rrset without rdata; not necessarily harmful, but treat it as an error. +// delete after commit. should error } diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 066865f711..5315942d5f 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -15,9 +15,11 @@ #ifndef __ZONE_H #define __ZONE_H 1 -#include +#include #include +#include + namespace isc { namespace datasrc { @@ -216,8 +218,25 @@ public: virtual ~ZoneUpdater() {} /// TBD + /// + /// The finder is not expected to provide meaningful data once commit() + /// was performed. virtual ZoneFinder& getFinder() = 0; + /// TBD + /// + /// Notes about unexpected input: class mismatch will be rejected. + /// The owner name isn't checked; it's the caller's responsibility. + /// + /// Open issues: we may eventually want to return result values such as + /// there's a duplicate, etc. + /// + /// The added RRset must not be empty (i.e., it must have at least one + /// RDATA). + /// + /// This method must not be called once commit() is performed. + virtual void addRRset(const isc::dns::RRset& rrset) = 0; + /// TBD /// /// This operation can only be performed at most once. A duplicate call From 77030a4789285a3f08fbdd9621a384a9e008f4a8 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Wed, 17 Aug 2011 10:03:35 +0800 Subject: [PATCH 553/974] [trac1130] Update ChangeLog file --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index b58cb7ac2b..8adcf125d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +280. [func ocean + libdns++: Implement the NAPTR rrtype according to RFC2915, + RFC2168 and RFC3403. + (Trac #1130, git a030033e5a53dd18157509c6c101340688d16011) + 279. [func] jerry libdns++: Implement the AFSDB rrtype according to RFC1183. (Trac #1114, git ce052cd92cd128ea3db5a8f154bd151956c2920c) From db9e3c398b854c83a65eb227ab9ff40dfae1145b Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Wed, 17 Aug 2011 10:05:04 +0800 Subject: [PATCH 554/974] [trac1130] Fix small typos in ChangeLog --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 8adcf125d6..d89d82880d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -280. [func ocean +280. [func] ocean libdns++: Implement the NAPTR rrtype according to RFC2915, RFC2168 and RFC3403. (Trac #1130, git a030033e5a53dd18157509c6c101340688d16011) From 83f8d6de769a33f51b83cd81efe178db162e95e1 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 17 Aug 2011 13:09:39 +0800 Subject: [PATCH 555/974] [trac1113] minor comments fix --- src/lib/dns/rdata/generic/minfo_14.h | 11 ++++++----- src/lib/dns/tests/rdata_minfo_unittest.cc | 1 - .../testdata/rdata_minfo_toWireUncompressed2.spec | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/dns/rdata/generic/minfo_14.h b/src/lib/dns/rdata/generic/minfo_14.h index f7afcc5f13..f3ee1d07fb 100644 --- a/src/lib/dns/rdata/generic/minfo_14.h +++ b/src/lib/dns/rdata/generic/minfo_14.h @@ -39,7 +39,8 @@ public: /// \brief Define the assignment operator. /// - /// This method never throws an exception. + /// \exception std::bad_alloc Memory allocation fails in copying + /// internal member variables (this should be very rare). MINFO& operator=(const MINFO& source); /// \brief Return the value of the rmailbox field. @@ -52,11 +53,11 @@ public: /// \c NS::getNSName()), this method constructs a new \c Name object /// and returns it, instead of returning a reference to a \c Name object /// internally maintained in the class (which is a private member). - /// This is based on the observation that this method will be rarely used - /// and even when it's used it will not be in a performance context + /// This is based on the observation that this method will be rarely + /// used and even when it's used it will not be in a performance context /// (for example, a recursive resolver won't need this field in its - /// resolution process). By returning a new object we have flexibility of - /// changing the internal representation without the risk of changing + /// resolution process). By returning a new object we have flexibility + /// of changing the internal representation without the risk of changing /// the interface or method property. /// The same note applies to the \c getEmailbox() method. Name getRmailbox() const { return (rmailbox_); } diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc index 0956161243..30c7c3945f 100644 --- a/src/lib/dns/tests/rdata_minfo_unittest.cc +++ b/src/lib/dns/tests/rdata_minfo_unittest.cc @@ -39,7 +39,6 @@ const char* const too_long_label = "01234567890123456789012345678901234567" namespace { class Rdata_MINFO_Test : public RdataTest { - // there's nothing to specialize public: Rdata_MINFO_Test(): rdata_minfo(string(minfo_txt)), rdata_minfo2(string(minfo_txt2)) {} diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec index 1182c2b118..0f78fcc63b 100644 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec @@ -1,5 +1,5 @@ # -# A simplest form of MINFO: all default parameters +# A simplest form of MINFO: custom rmailbox and default emailbox # [custom] sections: minfo From d72e84456e23ac19c2c12a186ba429cd2e4985cd Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 17 Aug 2011 13:15:58 +0800 Subject: [PATCH 556/974] [trac1113] fix conflict in src/lib/dns/Makefile.am --- src/lib/dns/Makefile.am | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index c85879d4c2..4a2364190b 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -51,13 +51,10 @@ EXTRA_DIST += rdata/generic/soa_6.cc EXTRA_DIST += rdata/generic/soa_6.h EXTRA_DIST += rdata/generic/txt_16.cc EXTRA_DIST += rdata/generic/txt_16.h -<<<<<<< HEAD EXTRA_DIST += rdata/generic/minfo_14.cc EXTRA_DIST += rdata/generic/minfo_14.h -======= EXTRA_DIST += rdata/generic/afsdb_18.cc EXTRA_DIST += rdata/generic/afsdb_18.h ->>>>>>> master EXTRA_DIST += rdata/hs_4/a_1.cc EXTRA_DIST += rdata/hs_4/a_1.h EXTRA_DIST += rdata/in_1/a_1.cc From 7a9a19d6431df02d48a7bc9de44f08d9450d3a37 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 17 Aug 2011 14:07:32 +0800 Subject: [PATCH 557/974] [master] merge #1113: Implement MINFO rrtype --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index b58cb7ac2b..13737f0aed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +280. [func] jerry + libdns++: Implement the MINFO rrtype according to RFC1035. + (Trac #1113, git TBD) + 279. [func] jerry libdns++: Implement the AFSDB rrtype according to RFC1183. (Trac #1114, git ce052cd92cd128ea3db5a8f154bd151956c2920c) From 7dd0238dbd4ed086ca7217ec50d8f0a5be3179f3 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 17 Aug 2011 15:11:54 +0800 Subject: [PATCH 558/974] [master] update ChangeLog entry for #1113 --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 13737f0aed..dc701e3af8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ 280. [func] jerry libdns++: Implement the MINFO rrtype according to RFC1035. - (Trac #1113, git TBD) + (Trac #1113, git 7a9a19d6431df02d48a7bc9de44f08d9450d3a37) 279. [func] jerry libdns++: Implement the AFSDB rrtype according to RFC1183. From 9001f1db99dfff10957dc2a971e7466a496f0f2f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 15 Aug 2011 13:00:28 +0200 Subject: [PATCH 559/974] [1066] Tests for some basic wildcards --- src/lib/datasrc/tests/database_unittest.cc | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index e382a63d7a..21511fd454 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -326,6 +326,12 @@ private: // This is because of empty domain test addRecord("A", "3600", "", "192.0.2.1"); addCurName("a.b.example.org."); + + // Something for wildcards + addRecord("A", "3600", "", "192.0.2.5"); + addCurName("*.wild.example.org."); + addRecord("AAAA", "3600", "", "2001:db8::5"); + addCurName("cancel.here.wild.example.org."); } }; @@ -964,6 +970,70 @@ TEST_F(DatabaseClientTest, glueOK) { EXPECT_FALSE(current_database_->searchRunning()); } +TEST_F(DatabaseClientTest, wildcard) { + shared_ptr finder(getFinder()); + + // First, simple wildcard match + expected_rdatas_.push_back("192.0.2.5"); + doFindTest(finder, isc::dns::Name("a.wild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, + expected_sig_rdatas_); + doFindTest(finder, isc::dns::Name("b.a.wild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, + expected_sig_rdatas_); + expected_rdatas_.clear(); + doFindTest(finder, isc::dns::Name("a.wild.example.org"), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, + expected_sig_rdatas_); + doFindTest(finder, isc::dns::Name("b.a.wild.example.org"), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, + expected_sig_rdatas_); + + // Direct request for thi wildcard + expected_rdatas_.push_back("192.0.2.5"); + doFindTest(finder, isc::dns::Name("*.wild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, + expected_sig_rdatas_); + expected_rdatas_.clear(); + doFindTest(finder, isc::dns::Name("*.wild.example.org"), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, + expected_sig_rdatas_); + // This is nonsense, but check it doesn't match by some stupid accident + doFindTest(finder, isc::dns::Name("a.*.wild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, + expected_rdatas_, expected_sig_rdatas_); + // These should be canceled, since it is below a domain which exitsts + doFindTest(finder, isc::dns::Name("nothing.here.wild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, + expected_rdatas_, expected_sig_rdatas_); + doFindTest(finder, isc::dns::Name("cancel.here.wild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, + expected_rdatas_, expected_sig_rdatas_); + doFindTest(finder, + isc::dns::Name("below.cancel.here.wild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, + expected_rdatas_, expected_sig_rdatas_); + // And this should be just plain empty non-terminal domain, check + // the wildcard doesn't hurt it + doFindTest(finder, isc::dns::Name("here.wild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::A(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, + expected_sig_rdatas_); + + // TODO Check delegation, multiple wildcards and wildcards somewhere + // in the middle. +} + TEST_F(DatabaseClientTest, getOrigin) { DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); ASSERT_EQ(result::SUCCESS, zone.code); From e3c81bd07046903b4b3bff8325024aafcdb35cba Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 15 Aug 2011 15:55:14 +0200 Subject: [PATCH 560/974] [1066] Basic wildcard matching Still, some canceling needs to be done properly. --- src/lib/datasrc/database.cc | 68 ++++++++++++++++++++++++++++--------- src/lib/datasrc/database.h | 7 +++- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 4286f78c99..5dc735ae88 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -170,7 +170,8 @@ std::pair DatabaseClient::Finder::getRRset(const isc::dns::Name& name, const isc::dns::RRType* type, bool want_cname, bool want_dname, - bool want_ns) + bool want_ns, + const isc::dns::Name* construct_name) { RRsigStore sig_store; database_->searchForRecords(zone_id_, name.toText()); @@ -178,6 +179,9 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, isc::dns::RRsetPtr result_rrset; std::string columns[DatabaseAccessor::COLUMN_COUNT]; + if (construct_name == NULL) { + construct_name = &name; + } while (database_->getNextRecord(columns, DatabaseAccessor::COLUMN_COUNT)) { if (!records_found) { records_found = true; @@ -212,7 +216,8 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, isc_throw(DataSourceError, "NS found together with data" " in non-apex domain " + name.toText()); } - addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, + addOrCreate(result_rrset, *construct_name, getClass(), + cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], *database_); } else if (type != NULL && cur_type == *type) { @@ -225,7 +230,8 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, isc_throw(DataSourceError, "NS found together with data" " in non-apex domain " + name.toText()); } - addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, + addOrCreate(result_rrset, *construct_name, getClass(), + cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], *database_); } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) { @@ -235,7 +241,8 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, isc_throw(DataSourceError, "CNAME found but it is not " "the only record for " + name.toText()); } - addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, + addOrCreate(result_rrset, *construct_name, getClass(), + cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], *database_); } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) { @@ -245,7 +252,8 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, isc_throw(DataSourceError, "DNAME with multiple RRs in " + name.toText()); } - addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, + addOrCreate(result_rrset, *construct_name, getClass(), + cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], *database_); } else if (cur_type == isc::dns::RRType::RRSIG()) { @@ -300,6 +308,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // First, do we have any kind of delegation (NS/DNAME) here? Name origin(getOrigin()); size_t origin_label_count(origin.getLabelCount()); + // Number of labels in the last known non-empty domain + size_t last_known(origin_label_count); size_t current_label_count(name.getLabelCount()); // This is how many labels we remove to get origin size_t remove_labels(current_label_count - origin_label_count); @@ -310,6 +320,10 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Look if there's NS or DNAME (but ignore the NS in origin) found = getRRset(superdomain, NULL, false, true, i != remove_labels && !glue_ok); + if (found.first) { + // It contains some RRs, so it exists. + last_known = superdomain.getLabelCount(); + } if (found.second) { // We found something redirecting somewhere else // (it can be only NS or DNAME here) @@ -338,19 +352,10 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, name != origin && !glue_ok); records_found = found.first; result_rrset = found.second; - if (result_rrset && name != origin && !glue_ok && - result_rrset->getType() == isc::dns::RRType::NS()) { - LOG_DEBUG(logger, DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_DELEGATION_EXACT). - arg(database_->getDBName()).arg(name); - result_status = DELEGATION; - } else if (result_rrset && type != isc::dns::RRType::CNAME() && - result_rrset->getType() == isc::dns::RRType::CNAME()) { - result_status = CNAME; - } } if (!result_rrset && !records_found) { - // Nothing lives here. But check if something lives below this + // Nothing lives here. + // But check if something lives below this // domain and if so, pretend something is here as well. database_->searchForRecords(zone_id_, name.toText(), true); std::string columns[DatabaseAccessor::COLUMN_COUNT]; @@ -359,8 +364,39 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, records_found = true; // We don't consume everything, so get rid of the rest database_->resetSearch(); + } else { + // It's not empty non-terminal. So check for wildcards. + // We remove labels one by one and look for the wildcard there. + // Go up to first non-empty domain. + remove_labels = current_label_count - last_known; + Name star("*"); + for (size_t i(1); i < remove_labels; ++ i) { + // Construct the name with * + // TODO: Once the underlying DatabaseAccessor takes string, + // do the concatenation on strings, not Names + Name superdomain(name.split(i)); + Name wildcard(star.concatenate(superdomain)); + // TODO What do we do about DNAME here? + found = getRRset(wildcard, &type, true, false, true, + &name); + result_rrset = found.second; + if (found.first) { + records_found = true; + break; + } + } } } + if (result_rrset && name != origin && !glue_ok && + result_rrset->getType() == isc::dns::RRType::NS()) { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_DELEGATION_EXACT). + arg(database_->getDBName()).arg(name); + result_status = DELEGATION; + } else if (result_rrset && type != isc::dns::RRType::CNAME() && + result_rrset->getType() == isc::dns::RRType::CNAME()) { + result_status = CNAME; + } } catch (const DataSourceError& dse) { logger.error(DATASRC_DATABASE_FIND_ERROR) .arg(database_->getDBName()).arg(dse.what()); diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 49a7ecf135..a257bd5445 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -327,6 +327,9 @@ public: * DataSourceError. * \param want_ns This allows redirection by NS to be returned. If * any other data is met as well, DataSourceError is thrown. + * \param construct_name If set to non-NULL, the resulting RRset will + * be constructed for this name instead of the queried one. This + * is useful for wildcards. * \note It may happen that some of the above error conditions are not * detected in some circumstances. The goal here is not to validate * the domain in DB, but to avoid bad behaviour resulting from @@ -344,7 +347,9 @@ public: type, bool want_cname, bool want_dname, - bool want_ns); + bool want_ns, const + isc::dns::Name* + construct_name = NULL); }; /** * \brief Find a zone in the database From 2bb551be853647c25005d1ab167e17ada7a5bfc5 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 17 Aug 2011 11:24:15 +0200 Subject: [PATCH 561/974] [1067] constify array size var --- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 89fc069248..a6314498f7 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -114,7 +114,7 @@ TEST_F(SQLite3Access, iterator) { ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); - size_t size(5); + const size_t size(5); std::string data[size]; // Get and check the first and only record EXPECT_TRUE(context->getNext(data, size)); From 1d314b2544b8af8a936c90e00a0dbbb605410952 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 17 Aug 2011 11:49:00 +0200 Subject: [PATCH 562/974] [1066] Cancel wildcard match --- src/lib/datasrc/database.cc | 30 ++++++++++++++++++++++-------- src/lib/datasrc/database.h | 9 +++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 5dc735ae88..36ce554311 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -287,6 +287,19 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, return (std::pair(records_found, result_rrset)); } +bool +DatabaseClient::Finder::hasSubdomains(const std::string& name) { + database_->searchForRecords(zone_id_, name, true); + std::string columns[DatabaseAccessor::COLUMN_COUNT]; + if (database_->getNextRecord(columns, + DatabaseAccessor::COLUMN_COUNT)) { + // We don't consume everything, discard the rest + database_->resetSearch(); + return (true); + } else { + return (false); + } +} ZoneFinder::FindResult DatabaseClient::Finder::find(const isc::dns::Name& name, @@ -357,13 +370,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Nothing lives here. // But check if something lives below this // domain and if so, pretend something is here as well. - database_->searchForRecords(zone_id_, name.toText(), true); - std::string columns[DatabaseAccessor::COLUMN_COUNT]; - if (database_->getNextRecord(columns, - DatabaseAccessor::COLUMN_COUNT)) { + if (hasSubdomains(name.toText())) { records_found = true; - // We don't consume everything, so get rid of the rest - database_->resetSearch(); } else { // It's not empty non-terminal. So check for wildcards. // We remove labels one by one and look for the wildcard there. @@ -379,9 +387,15 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // TODO What do we do about DNAME here? found = getRRset(wildcard, &type, true, false, true, &name); - result_rrset = found.second; if (found.first) { - records_found = true; + // Nothing we added as part of the * can exist directly, + // as we go up only to first existing domain, + // but it could be empty non-terminal. In that case, we + // need to cancel the match. + if (!hasSubdomains(name.split(i - 1).toText())) { + records_found = true; + result_rrset = found.second; + } break; } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index a257bd5445..1342fb7f2b 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -350,6 +350,15 @@ public: bool want_ns, const isc::dns::Name* construct_name = NULL); + /** + * \brief Checks if something lives below this domain. + * + * This looks if there's any subdomain of the given name. It can be + * used to test if domain is empty non-terminal. + * + * \param name The domain to check. + */ + bool hasSubdomains(const std::string& name); }; /** * \brief Find a zone in the database From 2384bcf387e93435658ec1ab92addbf28c9ab640 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 17 Aug 2011 13:43:01 +0200 Subject: [PATCH 563/974] [1066] More wildcard cornercases Wildcard match below NS with GLUE_OK mode Empty non-terminal asterisk --- src/lib/datasrc/database.cc | 40 +++++++++++--- src/lib/datasrc/tests/database_unittest.cc | 63 +++++++++++++++++++++- 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 36ce554311..2444257eaa 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -316,6 +316,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, std::pair found; logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS) .arg(database_->getDBName()).arg(name).arg(type); + // In case we are in GLUE_OK mode and start matching wildcards, + // we can't do it under NS, so we store it here to check + isc::dns::RRsetPtr first_ns; try { // First, do we have any kind of delegation (NS/DNAME) here? @@ -336,6 +339,12 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, if (found.first) { // It contains some RRs, so it exists. last_known = superdomain.getLabelCount(); + // In case we are in GLUE_OK, we want to store the highest + // encounderet RRset. + if (glue_ok && !first_ns && i != remove_labels) { + first_ns = getRRset(superdomain, NULL, false, false, + true).second; + } } if (found.second) { // We found something redirecting somewhere else @@ -376,27 +385,42 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // It's not empty non-terminal. So check for wildcards. // We remove labels one by one and look for the wildcard there. // Go up to first non-empty domain. + remove_labels = current_label_count - last_known; Name star("*"); - for (size_t i(1); i < remove_labels; ++ i) { + for (size_t i(1); i <= remove_labels; ++ i) { // Construct the name with * - // TODO: Once the underlying DatabaseAccessor takes string, - // do the concatenation on strings, not Names + // TODO: Once the underlying DatabaseAccessor takes + // string, do the concatenation on strings, not + // Names Name superdomain(name.split(i)); Name wildcard(star.concatenate(superdomain)); // TODO What do we do about DNAME here? found = getRRset(wildcard, &type, true, false, true, &name); if (found.first) { - // Nothing we added as part of the * can exist directly, - // as we go up only to first existing domain, - // but it could be empty non-terminal. In that case, we - // need to cancel the match. - if (!hasSubdomains(name.split(i - 1).toText())) { + if (first_ns) { + // In case we are under NS, we don't + // wildcard-match, but return delegation + result_rrset = first_ns; + result_status = DELEGATION; + records_found = true; + // We pretend to switch to non-glue_ok mode + glue_ok = false; + } else if (!hasSubdomains(name.split(i - 1).toText())) + { + // Nothing we added as part of the * can exist + // directly, as we go up only to first existing + // domain, but it could be empty non-terminal. In + // that case, we need to cancel the match. records_found = true; result_rrset = found.second; } break; + } else if (hasSubdomains(wildcard.toText())) { + // Empty non-terminal asterisk + records_found = true; + break; } } } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 21511fd454..a0efd50ff2 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -332,6 +332,14 @@ private: addCurName("*.wild.example.org."); addRecord("AAAA", "3600", "", "2001:db8::5"); addCurName("cancel.here.wild.example.org."); + addRecord("NS", "3600", "", "ns.example.com."); + addCurName("delegatedwild.example.org."); + addRecord("A", "3600", "", "192.0.2.5"); + addCurName("*.delegatedwild.example.org."); + addRecord("A", "3600", "", "192.0.2.5"); + addCurName("wild.*.foo.example.org."); + addRecord("A", "3600", "", "192.0.2.5"); + addCurName("wild.*.foo.*.bar.example.org."); } }; @@ -1030,8 +1038,59 @@ TEST_F(DatabaseClientTest, wildcard) { isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); - // TODO Check delegation, multiple wildcards and wildcards somewhere - // in the middle. + // How wildcard go together with delegation + expected_rdatas_.push_back("ns.example.com."); + doFindTest(finder, isc::dns::Name("below.delegatedwild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::NS(), + isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, + expected_sig_rdatas_, + isc::dns::Name("delegatedwild.example.org")); + // FIXME: This doesn't look logically OK, GLUE_OK should make it transparent, + // so the match should either work or be canceled, but return NXDOMAIN + doFindTest(finder, isc::dns::Name("below.delegatedwild.example.org"), + isc::dns::RRType::A(), isc::dns::RRType::NS(), + isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, + expected_sig_rdatas_, + isc::dns::Name("delegatedwild.example.org"), + ZoneFinder::FIND_GLUE_OK); + + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.5"); + // These are direct matches + const char* positive_names[] = { + "wild.*.foo.example.org.", + "wild.*.foo.*.bar.example.org.", + NULL + }; + for (const char** name(positive_names); *name != NULL; ++ name) { + doFindTest(finder, isc::dns::Name(*name), isc::dns::RRType::A(), + isc::dns::RRType::A(), isc::dns::RRTTL(3600), + ZoneFinder::SUCCESS, expected_rdatas_, + expected_sig_rdatas_); + } + + // These are wildcard matches against empty nonterminal asterisk + expected_rdatas_.clear(); + const char* negative_names[] = { + "a.foo.example.org.", + "*.foo.example.org.", + "foo.example.org.", + "wild.bar.foo.example.org.", + "baz.foo.*.bar.example.org", + "baz.foo.baz.bar.example.org", + "*.foo.baz.bar.example.org", + "*.foo.*.bar.example.org", + "foo.*.bar.example.org", + "*.bar.example.org", + "bar.example.org", + NULL + }; + for (const char** name(negative_names); *name != NULL; ++ name) { + doFindTest(finder, isc::dns::Name(*name), isc::dns::RRType::A(), + isc::dns::RRType::A(), isc::dns::RRTTL(3600), + ZoneFinder::NXRRSET, expected_rdatas_, + expected_sig_rdatas_); + } } TEST_F(DatabaseClientTest, getOrigin) { From 20871297d2aaae57acb79e987ff80a9020d608d1 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Wed, 17 Aug 2011 08:27:13 -0400 Subject: [PATCH 564/974] [1140] coding style fixes in src/lib/dns/rdata/generic/detail/txt_like.h --- src/lib/dns/rdata/generic/detail/txt_like.h | 180 ++++++++++---------- 1 file changed, 94 insertions(+), 86 deletions(-) diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h index f740cbc7fa..392a8ce593 100644 --- a/src/lib/dns/rdata/generic/detail/txt_like.h +++ b/src/lib/dns/rdata/generic/detail/txt_like.h @@ -26,126 +26,134 @@ using namespace isc::util; templateclass TXTLikeImpl { public: TXTLikeImpl(InputBuffer& buffer, size_t rdata_len) { - if (rdata_len > MAX_RDLENGTH) { - isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); - } + if (rdata_len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); + } - if (rdata_len == 0) { // note that this couldn't happen in the loop. - isc_throw(DNSMessageFORMERR, "Error in parsing " << RRType(typeCode) << - " RDATA: 0-length character string"); - } + if (rdata_len == 0) { // note that this couldn't happen in the loop. + isc_throw(DNSMessageFORMERR, "Error in parsing " << + RRType(typeCode) << " RDATA: 0-length character string"); + } - do { - const uint8_t len = buffer.readUint8(); - if (rdata_len < len + 1) { - isc_throw(DNSMessageFORMERR, "Error in parsing " << RRType(typeCode) << - " RDATA: character string length is too large: " << static_cast(len)); - } - vector data(len + 1); - data[0] = len; - buffer.readData(&data[0] + 1, len); - string_list_.push_back(data); + do { + const uint8_t len = buffer.readUint8(); + if (rdata_len < len + 1) { + isc_throw(DNSMessageFORMERR, "Error in parsing " << + RRType(typeCode) << + " RDATA: character string length is too large: " << + static_cast(len)); + } + vector data(len + 1); + data[0] = len; + buffer.readData(&data[0] + 1, len); + string_list_.push_back(data); - rdata_len -= (len + 1); - } while (rdata_len > 0); + rdata_len -= (len + 1); + } while (rdata_len > 0); } explicit TXTLikeImpl(const std::string& txtstr) { - // TBD: this is a simple, incomplete implementation that only supports - // a single character-string. + // TBD: this is a simple, incomplete implementation that only supports + // a single character-string. - size_t length = txtstr.size(); - size_t pos_begin = 0; + size_t length = txtstr.size(); + size_t pos_begin = 0; - if (length > 1 && txtstr[0] == '"' && txtstr[length - 1] == '"') { - pos_begin = 1; - length -= 2; - } + if (length > 1 && txtstr[0] == '"' && txtstr[length - 1] == '"') { + pos_begin = 1; + length -= 2; + } - if (length > MAX_CHARSTRING_LEN) { - isc_throw(CharStringTooLong, RRType(typeCode) << - " RDATA construction from text: string length is too long: " << length); - } + if (length > MAX_CHARSTRING_LEN) { + isc_throw(CharStringTooLong, RRType(typeCode) << + " RDATA construction from text:" + " string length is too long: " << length); + } - // TBD: right now, we don't support escaped characters - if (txtstr.find('\\') != string::npos) { - isc_throw(InvalidRdataText, RRType(typeCode) << - " RDATA from text: escaped character is currently not supported: " << txtstr); - } + // TBD: right now, we don't support escaped characters + if (txtstr.find('\\') != string::npos) { + isc_throw(InvalidRdataText, RRType(typeCode) << + " RDATA from text:" + " escaped character is currently not supported: " << + txtstr); + } - vector data; - data.reserve(length + 1); - data.push_back(length); - data.insert(data.end(), txtstr.begin() + pos_begin, - txtstr.begin() + pos_begin + length); - string_list_.push_back(data); + vector data; + data.reserve(length + 1); + data.push_back(length); + data.insert(data.end(), txtstr.begin() + pos_begin, + txtstr.begin() + pos_begin + length); + string_list_.push_back(data); } TXTLikeImpl(const TXTLikeImpl& other) : - string_list_(other.string_list_) + string_list_(other.string_list_) {} void toWire(OutputBuffer& buffer) const { - for (vector >::const_iterator it = string_list_.begin(); - it != string_list_.end(); - ++it) - { - buffer.writeData(&(*it)[0], (*it).size()); - } + for (vector >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + buffer.writeData(&(*it)[0], (*it).size()); + } } void toWire(AbstractMessageRenderer& renderer) const { - for (vector >::const_iterator it = string_list_.begin(); - it != string_list_.end(); - ++it) - { - renderer.writeData(&(*it)[0], (*it).size()); - } + for (vector >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + renderer.writeData(&(*it)[0], (*it).size()); + } } string toText() const { - string s; + string s; - // XXX: this implementation is not entirely correct. for example, it - // should escape double-quotes if they appear in the character string. - for (vector >::const_iterator it = string_list_.begin(); - it != string_list_.end(); - ++it) - { - if (!s.empty()) { - s.push_back(' '); - } - s.push_back('"'); - s.insert(s.end(), (*it).begin() + 1, (*it).end()); - s.push_back('"'); - } + // XXX: this implementation is not entirely correct. for example, it + // should escape double-quotes if they appear in the character string. + for (vector >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + if (!s.empty()) { + s.push_back(' '); + } + s.push_back('"'); + s.insert(s.end(), (*it).begin() + 1, (*it).end()); + s.push_back('"'); + } - return (s); + return (s); } int compare(const TXTLikeImpl& other) const { - // This implementation is not efficient. Revisit this (TBD). - OutputBuffer this_buffer(0); - toWire(this_buffer); - size_t this_len = this_buffer.getLength(); + // This implementation is not efficient. Revisit this (TBD). + OutputBuffer this_buffer(0); + toWire(this_buffer); + size_t this_len = this_buffer.getLength(); - OutputBuffer other_buffer(0); - other.toWire(other_buffer); - const size_t other_len = other_buffer.getLength(); + OutputBuffer other_buffer(0); + other.toWire(other_buffer); + const size_t other_len = other_buffer.getLength(); - const size_t cmplen = min(this_len, other_len); - const int cmp = memcmp(this_buffer.getData(), other_buffer.getData(), - cmplen); - if (cmp != 0) { - return (cmp); - } else { - return ((this_len == other_len) ? 0 : - (this_len < other_len) ? -1 : 1); - } + const size_t cmplen = min(this_len, other_len); + const int cmp = memcmp(this_buffer.getData(), other_buffer.getData(), + cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : + (this_len < other_len) ? -1 : 1); + } } private: From b4ae924f504e9749989059a14e6a5dc830c99e81 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 17 Aug 2011 15:23:21 +0200 Subject: [PATCH 565/974] [1066] Logging about wildcards --- src/lib/datasrc/database.cc | 20 ++++++++++++++++++++ src/lib/datasrc/datasrc_messages.mes | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 2444257eaa..bafd6a9b8c 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -380,6 +380,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // But check if something lives below this // domain and if so, pretend something is here as well. if (hasSubdomains(name.toText())) { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL). + arg(database_->getDBName()).arg(name); records_found = true; } else { // It's not empty non-terminal. So check for wildcards. @@ -407,6 +410,10 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, records_found = true; // We pretend to switch to non-glue_ok mode glue_ok = false; + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_WILDCARD_CANCEL_NS). + arg(database_->getDBName()).arg(wildcard). + arg(first_ns->getName()); } else if (!hasSubdomains(name.split(i - 1).toText())) { // Nothing we added as part of the * can exist @@ -415,11 +422,24 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // that case, we need to cancel the match. records_found = true; result_rrset = found.second; + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_WILDCARD). + arg(database_->getDBName()).arg(wildcard). + arg(name); + } else { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_WILDCARD_CANCEL_SUB). + arg(database_->getDBName()).arg(wildcard). + arg(name).arg(superdomain); } break; } else if (hasSubdomains(wildcard.toText())) { // Empty non-terminal asterisk records_found = true; + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_WILDCARD_EMPTY). + arg(database_->getDBName()).arg(wildcard). + arg(name); break; } } diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 190adbe3ac..aa9dc439d6 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -104,6 +104,11 @@ When searching for a domain, the program met a DNAME redirection to a different place in the domain space at the given domain name. It will return that one instead. +% DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL empty non-terminal %2 in %1 +The domain name doesn't have any RRs, so it doesn't exist in the database. +However, it has a subdomain, so it exists in the DNS address space. So we +return NXRRSET instead of NXDOMAIN. + % DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4 The data returned by the database backend did not contain any data for the given domain name, class and type. @@ -117,6 +122,28 @@ The data returned by the database backend contained data for the given domain name, and it either matches the type or has a relevant type. The RRset that is returned is printed. +% DATASRC_DATABASE_WILDCARD constructing RRset %3 from wildcard %2 in %1 +The database doesn't contain directly matching domain, but it does contain a +wildcard one which is being used to synthesize the answer. + +% DATASRC_DATABASE_WILDCARD_CANCEL_NS canceled wildcard match on %2 because %3 contains NS in %1 +The database was queried to provide glue data and it didn't find direct match. +It could create it from given wildcard, but matching wildcards is forbidden +under a zone cut, which was found. Therefore the delegation will be returned +instead. + +% DATASRC_DATABASE_WILDCARD_CANCEL_SUB wildcard %2 can't be used to construct %3 because %4 exists in %1 +The answer could be constructed using the wildcard, but the given subdomain +exists, therefore this name is something like empty non-terminal (actually, +from the protocol point of view, it is empty non-terminal, but the code +discovers it differently). + +% DATASRC_DATABASE_WILDCARD_EMPTY implicit wildcard %2 used to construct %3 in %1 +The given wildcard exists implicitly in the domainspace, as empty nonterminal +(eg. there's something like subdomain.*.example.org, so *.example.org exists +implicitly, but is empty). This will produce NXRRSET, because the constructed +domain is empty as well as the wildcard. + % DATASRC_DO_QUERY handling query for '%1/%2' A debug message indicating that a query for the given name and RR type is being processed. From fe8f3314300936f71cc89535ecd3f0f3cad3804c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 17 Aug 2011 15:45:40 +0200 Subject: [PATCH 566/974] [1067] "FIX" different TTL instead of throwing When we iterate and find out that there are RRs in an RRset with different TTL, we go for the lowest of them instead of throwing and give a warning. --- src/lib/datasrc/database.cc | 8 ++++++-- src/lib/datasrc/datasrc_messages.mes | 7 +++++++ src/lib/datasrc/tests/database_unittest.cc | 3 ++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index d5e8f59be5..196cf6fb4e 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -352,8 +352,12 @@ public: RRsetPtr rrset(new RRset(name, class_, rtype, RRTTL(ttl))); while (data_ready_ && name_ == name_str && rtype_str == rtype_) { if (ttl_ != ttl) { - isc_throw(DataSourceError, "TTLs in rrset " + name_str + "/" + - rtype_str + " differ"); + LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_DIFF). + arg(name_).arg(ttl).arg(ttl_); + if (ttl < ttl_) { + ttl_ = ttl; + rrset->setTTL(RRTTL(ttl)); + } } rrset->addRdata(rdata::createRdata(rtype, class_, rdata_)); getData(); diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index ef1b26f8d3..c2d99f3d7f 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -114,6 +114,13 @@ While iterating through the zone, the program reached end of the data. While iterating through the zone, the program extracted next RRset from it. The name and RRtype of the RRset is indicated in the message. +% DATASRC_DATABASE_ITERATE_TTL_DIFF TTL for %1/%2 differs (%3 and %4) +While iterating through the zone, the time to live for RRs of the given RRset +was discovered not to be the same the same This isn't allowed on the wire and +is considered generally broken, so we set it to the lowest of them internaly +(but we don't modify the database). But the data in database should better be +checked and fixed by human. + % DATASRC_DO_QUERY handling query for '%1/%2' A debug message indicating that a query for the given name and RR type is being processed. diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 48319ea03b..f5e785ca6b 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -554,8 +554,9 @@ TEST_F(DatabaseClientTest, iterator) { // This has inconsistent TTL in the set (the rest, like nonsense in // the data is handled in rdata itself). TEST_F(DatabaseClientTest, badIterator) { + // It should not throw, but get the lowest one of them ZoneIteratorPtr it(client_->getIterator(Name("bad.example.org"))); - EXPECT_THROW(it->getNextRRset(), DataSourceError); + EXPECT_EQ(it->getNextRRset()->getTTL(), isc::dns::RRTTL(300)); } // checks if the given rrset matches the From d63056e7cff35f58898a9bdc8d5cad589689590c Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Wed, 17 Aug 2011 09:55:35 -0400 Subject: [PATCH 567/974] [1144] style fixes in src/lib/dns/rdata/generic/detail/ds_like.h --- src/lib/dns/rdata/generic/detail/ds_like.h | 141 +++++++++++---------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h index 5249abf1c8..7e26d275b1 100644 --- a/src/lib/dns/rdata/generic/detail/ds_like.h +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -24,107 +24,112 @@ templateclass DSLikeImpl { public: DSLikeImpl(const string& ds_str) { - istringstream iss(ds_str); - stringbuf digestbuf; - uint32_t tag, algorithm, digest_type; + istringstream iss(ds_str); + stringbuf digestbuf; + uint32_t tag, algorithm, digest_type; - iss >> tag >> algorithm >> digest_type >> &digestbuf; - if (iss.bad() || iss.fail()) { - isc_throw(InvalidRdataText, "Invalid " << RRType(typeCode) << " text"); - } - if (tag > 0xffff) { - isc_throw(InvalidRdataText, RRType(typeCode) << " tag out of range"); - } - if (algorithm > 0xff) { - isc_throw(InvalidRdataText, RRType(typeCode) << " algorithm out of range"); - } - if (digest_type > 0xff) { - isc_throw(InvalidRdataText, RRType(typeCode) << " digest type out of range"); - } + iss >> tag >> algorithm >> digest_type >> &digestbuf; + if (iss.bad() || iss.fail()) { + isc_throw(InvalidRdataText, + "Invalid " << RRType(typeCode) << " text"); + } + if (tag > 0xffff) { + isc_throw(InvalidRdataText, + RRType(typeCode) << " tag out of range"); + } + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, + RRType(typeCode) << " algorithm out of range"); + } + if (digest_type > 0xff) { + isc_throw(InvalidRdataText, + RRType(typeCode) << " digest type out of range"); + } - tag_ = tag; - algorithm_ = algorithm; - digest_type_ = digest_type; - decodeHex(digestbuf.str(), digest_); + tag_ = tag; + algorithm_ = algorithm; + digest_type_ = digest_type; + decodeHex(digestbuf.str(), digest_); } DSLikeImpl(InputBuffer& buffer, size_t rdata_len) { - if (rdata_len < 4) { - isc_throw(InvalidRdataLength, RRType(typeCode) << " too short"); - } + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, RRType(typeCode) << " too short"); + } - uint16_t tag = buffer.readUint16(); - uint16_t algorithm = buffer.readUint8(); - uint16_t digest_type = buffer.readUint8(); + uint16_t tag = buffer.readUint16(); + uint16_t algorithm = buffer.readUint8(); + uint16_t digest_type = buffer.readUint8(); - rdata_len -= 4; - digest_.resize(rdata_len); - buffer.readData(&digest_[0], rdata_len); + rdata_len -= 4; + digest_.resize(rdata_len); + buffer.readData(&digest_[0], rdata_len); - tag_ = tag; - algorithm_ = algorithm; - digest_type_ = digest_type; + tag_ = tag; + algorithm_ = algorithm; + digest_type_ = digest_type; } DSLikeImpl(const DSLikeImpl& source) { - digest_ = source.digest_; - tag_ = source.tag_; - algorithm_ = source.algorithm_; - digest_type_ = source.digest_type_; + digest_ = source.digest_; + tag_ = source.tag_; + algorithm_ = source.algorithm_; + digest_type_ = source.digest_type_; } string toText() const { - using namespace boost; - return (lexical_cast(static_cast(tag_)) + - " " + lexical_cast(static_cast(algorithm_)) + - " " + lexical_cast(static_cast(digest_type_)) + - " " + encodeHex(digest_)); + using namespace boost; + return (lexical_cast(static_cast(tag_)) + + " " + lexical_cast(static_cast(algorithm_)) + + " " + lexical_cast(static_cast(digest_type_)) + + " " + encodeHex(digest_)); } void toWire(OutputBuffer& buffer) const { - buffer.writeUint16(tag_); - buffer.writeUint8(algorithm_); - buffer.writeUint8(digest_type_); - buffer.writeData(&digest_[0], digest_.size()); + buffer.writeUint16(tag_); + buffer.writeUint8(algorithm_); + buffer.writeUint8(digest_type_); + buffer.writeData(&digest_[0], digest_.size()); } void toWire(AbstractMessageRenderer& renderer) const { - renderer.writeUint16(tag_); - renderer.writeUint8(algorithm_); - renderer.writeUint8(digest_type_); - renderer.writeData(&digest_[0], digest_.size()); + renderer.writeUint16(tag_); + renderer.writeUint8(algorithm_); + renderer.writeUint8(digest_type_); + renderer.writeData(&digest_[0], digest_.size()); } int compare(const DSLikeImpl& other_ds) const { - if (tag_ != other_ds.tag_) { - return (tag_ < other_ds.tag_ ? -1 : 1); - } - if (algorithm_ != other_ds.algorithm_) { - return (algorithm_ < other_ds.algorithm_ ? -1 : 1); - } - if (digest_type_ != other_ds.digest_type_) { - return (digest_type_ < other_ds.digest_type_ ? -1 : 1); - } + if (tag_ != other_ds.tag_) { + return (tag_ < other_ds.tag_ ? -1 : 1); + } + if (algorithm_ != other_ds.algorithm_) { + return (algorithm_ < other_ds.algorithm_ ? -1 : 1); + } + if (digest_type_ != other_ds.digest_type_) { + return (digest_type_ < other_ds.digest_type_ ? -1 : 1); + } - size_t this_len = digest_.size(); - size_t other_len = other_ds.digest_.size(); - size_t cmplen = min(this_len, other_len); - int cmp = memcmp(&digest_[0], &other_ds.digest_[0], cmplen); - if (cmp != 0) { - return (cmp); - } else { - return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); - } + size_t this_len = digest_.size(); + size_t other_len = other_ds.digest_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&digest_[0], &other_ds.digest_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) + ? 0 : (this_len < other_len) ? -1 : 1); + } } uint16_t getTag() const { - return (tag_); + return (tag_); } private: From f9b1950752ff1d3041d776a5d50ec2d0ddb8065a Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 17 Aug 2011 08:56:31 -0500 Subject: [PATCH 568/974] [master] fix the default root address --- src/bin/resolver/b10-resolver.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bin/resolver/b10-resolver.xml b/src/bin/resolver/b10-resolver.xml index efe045a5f3..75cced71d1 100644 --- a/src/bin/resolver/b10-resolver.xml +++ b/src/bin/resolver/b10-resolver.xml @@ -20,7 +20,7 @@ - August 16, 2011 + August 17, 2011 @@ -178,8 +178,10 @@ once that is merged you can for instance do 'config add Resolver/forward_address root servers to start resolving. The list items are the address string and port number. - If empty, a hardcoded address for F-root (192.5.5.241) is used. + By default, a hardcoded address for l.root-servers.net + (199.7.83.42 or 2001:500:3::42) is used. + timeout_client is the number of milliseconds From bbc661e3c38f02b4a1fb50bd4e058a22150b0087 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Wed, 17 Aug 2011 10:01:04 -0400 Subject: [PATCH 569/974] [1138] style fixes --- src/lib/dns/rdata/in_1/dhcid_49.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/rdata/in_1/dhcid_49.cc b/src/lib/dns/rdata/in_1/dhcid_49.cc index b0a76f184f..937b068bde 100644 --- a/src/lib/dns/rdata/in_1/dhcid_49.cc +++ b/src/lib/dns/rdata/in_1/dhcid_49.cc @@ -38,8 +38,8 @@ DHCID::DHCID(const string& dhcid_str) { iss >> &digestbuf; decodeHex(digestbuf.str(), digest_); - // RFC4701 states DNS software should consider the RDATA section to be opaque, - // but there must be at least three bytes in the data: + // RFC4701 states DNS software should consider the RDATA section to + // be opaque, but there must be at least three bytes in the data: // < 2 octets > Identifier type code // < 1 octet > Digest type code if (digest_.size() < 3) { From 6ea996c67dff319e332b465ed450ee50b97de4f7 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 17 Aug 2011 16:27:19 +0200 Subject: [PATCH 570/974] [1067] small update to ttl mismatch log message --- src/lib/datasrc/database.cc | 4 ++-- src/lib/datasrc/datasrc_messages.mes | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 196cf6fb4e..764a83a7e0 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -352,12 +352,12 @@ public: RRsetPtr rrset(new RRset(name, class_, rtype, RRTTL(ttl))); while (data_ready_ && name_ == name_str && rtype_str == rtype_) { if (ttl_ != ttl) { - LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_DIFF). - arg(name_).arg(ttl).arg(ttl_); if (ttl < ttl_) { ttl_ = ttl; rrset->setTTL(RRTTL(ttl)); } + LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_MISMATCH). + arg(name_).arg(class_).arg(rtype_).arg(rrset->getTTL()); } rrset->addRdata(rdata::createRdata(rtype, class_, rdata_)); getData(); diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index c2d99f3d7f..c2347703d0 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -75,8 +75,9 @@ name and type in the database. % DATASRC_DATABASE_FIND_TTL_MISMATCH TTL values differ in %1 for elements of %2/%3/%4, setting to %5 The datasource backend provided resource records for the given RRset with -different TTL values. The TTL of the RRSET is set to the lowest value, which -is printed in the log message. +different TTL values. This isn't allowed on the wire and is considered +an error, so we set it to the lowest value we found (but we don't modify the +database). The data in database should be checked and fixed. % DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from datasource %1: %2 There was an uncaught general exception while reading data from a datasource. @@ -114,12 +115,11 @@ While iterating through the zone, the program reached end of the data. While iterating through the zone, the program extracted next RRset from it. The name and RRtype of the RRset is indicated in the message. -% DATASRC_DATABASE_ITERATE_TTL_DIFF TTL for %1/%2 differs (%3 and %4) +% DATASRC_DATABASE_ITERATE_TTL_MISMATCH TTL values differ for RRs of %1/%2/%3, setting to %4 While iterating through the zone, the time to live for RRs of the given RRset -was discovered not to be the same the same This isn't allowed on the wire and -is considered generally broken, so we set it to the lowest of them internaly -(but we don't modify the database). But the data in database should better be -checked and fixed by human. +were found to be different. This isn't allowed on the wire and is considered +an error, so we set it to the lowest value we found (but we don't modify the +database). The data in database should be checked and fixed. % DATASRC_DO_QUERY handling query for '%1/%2' A debug message indicating that a query for the given name and RR type is being From ba9f03b99b6e1dd46d9b11eb1bac629789c8f94a Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 17 Aug 2011 09:42:59 -0500 Subject: [PATCH 571/974] [master] grammar --- src/bin/xfrin/b10-xfrin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml index a8fe425d37..17840fe455 100644 --- a/src/bin/xfrin/b10-xfrin.xml +++ b/src/bin/xfrin/b10-xfrin.xml @@ -169,7 +169,7 @@ in separate zonemgr process. and port to define the port number on the authoritative server (defaults to 53). If the address or port is not specified, it will use the - values previously defined in the zones + value previously defined in the zones configuration. From 4a7d63179ae732ede6bdc77c393a1cfd9b0b58ca Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 17 Aug 2011 09:43:43 -0500 Subject: [PATCH 572/974] [master] improve the resolver documentation --- doc/guide/bind10-guide.xml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 021c593332..d34746b519 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1370,7 +1370,7 @@ what is XfroutClient xfr_client?? The main bind10 process can be configured - to select to run either the authoritative or resolver. + to select to run either the authoritative or resolver or both. By default, it starts the authoritative service. @@ -1390,22 +1390,28 @@ what is XfroutClient xfr_client?? - The resolver also needs to be configured to listen on an address - and port: + By default, the resolver listens on port 53 for 127.0.0.1 and ::1. + The following example shows how it can be configured to + listen on an additional address (and port): -> config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }] +> config add Resolver/listen_on +> config set Resolver/listen_on[2]/address "192.168.1.1" +> config set Resolver/listen_on[2]/port 53 > config commit - + (Replace the 2 + as needed; run config show + Resolver/listen_on if needed.) +
Access Control - The b10-resolver daemon only accepts + By default, the b10-resolver daemon only accepts DNS queries from the localhost (127.0.0.1 and ::1). The configuration may be used to reject, drop, or allow specific IPs or networks. @@ -1437,6 +1443,8 @@ url="bind10-messages.html#RESOLVER_QUERY_DROPPED">RESOLVER_QUERY_DROPPED From ea8bdd6cb6894855f109b8d19ce104ae9a4b9cb5 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 17 Aug 2011 09:59:27 -0500 Subject: [PATCH 573/974] [master] regenerate HTML, nroff and messages XML. The content changes were done in the source files previously. --- doc/guide/bind10-guide.html | 549 ++++++-- doc/guide/bind10-messages.html | 1135 ++++++++++++++--- doc/guide/bind10-messages.xml | 2089 ++++++++++++++++++++++++++++--- src/bin/bind10/bind10.8 | 16 +- src/bin/resolver/b10-resolver.8 | 30 +- src/bin/stats/b10-stats.8 | 97 +- src/bin/xfrin/b10-xfrin.8 | 5 +- 7 files changed, 3418 insertions(+), 503 deletions(-) diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html index 94adf4aa92..a9a4cc6223 100644 --- a/doc/guide/bind10-guide.html +++ b/doc/guide/bind10-guide.html @@ -1,24 +1,24 @@ -BIND 10 Guide

BIND 10 Guide

Administrator Reference for BIND 10

This is the reference guide for BIND 10 version - 20110705.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by +BIND 10 Guide

BIND 10 Guide

Administrator Reference for BIND 10

This is the reference guide for BIND 10 version + 20110809.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers.

- This is the reference guide for BIND 10 version 20110705. + This is the reference guide for BIND 10 version 20110809. The most up-to-date version of this document, along with - other documents for BIND 10, can be found at http://bind10.isc.org/docs.


Chapter 1. Introduction

+ other documents for BIND 10, can be found at http://bind10.isc.org/docs.


Chapter 1. Introduction

BIND is the popular implementation of a DNS server, developer interfaces, and DNS tools. BIND 10 is a rewrite of BIND 9. BIND 10 is written in C++ and Python and provides a modular environment for serving and maintaining DNS.

Note

This guide covers the experimental prototype of - BIND 10 version 20110705. + BIND 10 version 20110809.

Note

BIND 10 provides a EDNS0- and DNSSEC-capable authoritative DNS server and a caching recursive name server which also provides forwarding. -

Supported Platforms

+

Supported Platforms

BIND 10 builds have been tested on Debian GNU/Linux 5, Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, and CentOS Linux 5.3. @@ -28,7 +28,7 @@ It is planned for BIND 10 to build, install and run on Windows and standard Unix-type platforms. -

Required Software

+

Required Software

BIND 10 requires Python 3.1. Later versions may work, but Python 3.1 is the minimum version which will work.

@@ -138,7 +138,7 @@ and, of course, DNS. These include detailed developer documentation and code examples. -

Chapter 2. Installation

Building Requirements

+

Chapter 2. Installation

Building Requirements

In addition to the run-time requirements, building BIND 10 from source code requires various development include headers.

Note

@@ -202,14 +202,14 @@ the Git code revision control system or as a downloadable tar file. It may also be available in pre-compiled ready-to-use packages from operating system vendors. -

Download Tar File

+

Download Tar File

Downloading a release tar file is the recommended method to obtain the source code.

The BIND 10 releases are available as tar file downloads from ftp://ftp.isc.org/isc/bind10/. Periodic development snapshots may also be available. -

Retrieve from Git

+

Retrieve from Git

Downloading this "bleeding edge" code is recommended only for developers or advanced users. Using development code in a production environment is not recommended. @@ -243,7 +243,7 @@ autoheader, automake, and related commands. -

Configure before the build

+

Configure before the build

BIND 10 uses the GNU Build System to discover build environment details. To generate the makefiles using the defaults, simply run: @@ -252,7 +252,7 @@ Run ./configure with the --help switch to view the different options. The commonly-used options are: -

--prefix
Define the the installation location (the +

--prefix
Define the installation location (the default is /usr/local/).
--with-boost-include
Define the path to find the Boost headers.
--with-pythonpath
Define the path to Python 3.1 if it is not in the @@ -274,16 +274,16 @@

If the configure fails, it may be due to missing or old dependencies. -

Build

+

Build

After the configure step is complete, to build the executables from the C++ code and prepare the Python scripts, run:

$ make

-

Install

+

Install

To install the BIND 10 executables, support files, and documentation, run:

$ make install

-

Note

The install step may require superuser privileges.

Install Hierarchy

+

Note

The install step may require superuser privileges.

Install Hierarchy

The following is the layout of the complete BIND 10 installation:

  • bin/ — @@ -314,14 +314,14 @@ data source and configuration databases.

Chapter 3. Starting BIND10 with bind10

Table of Contents

Starting BIND 10

- BIND 10 provides the bind10 command which + BIND 10 provides the bind10 command which starts up the required processes. bind10 will also restart processes that exit unexpectedly. This is the only command needed to start the BIND 10 system.

After starting the b10-msgq communications channel, - bind10 connects to it, + bind10 connects to it, runs the configuration manager, and reads its own configuration. Then it starts the other modules.

@@ -344,7 +344,12 @@ To start the BIND 10 service, simply run bind10. Run it with the --verbose switch to get additional debugging or diagnostic output. -

Chapter 4. Command channel

+

Note

+ If the setproctitle Python module is detected at start up, + the process names for the Python-based daemons will be renamed + to better identify them instead of just python. + This is not needed on some operating systems. +

Chapter 4. Command channel

The BIND 10 components use the b10-msgq message routing daemon to communicate with other BIND 10 components. The b10-msgq implements what is called the @@ -500,12 +505,12 @@ shutdown the details and relays (over a b10-msgq command channel) the configuration on to the specified module.

-

Chapter 8. Authoritative Server

+

Chapter 8. Authoritative Server

The b10-auth is the authoritative DNS server. It supports EDNS0 and DNSSEC. It supports IPv6. Normally it is started by the bind10 master process. -

Server Configurations

+

Server Configurations

b10-auth is configured via the b10-cfgmgr configuration manager. The module name is Auth. @@ -525,7 +530,7 @@ This may be a temporary setting until then.

shutdown
Stop the authoritative DNS server.

-

Data Source Backends

Note

+

Data Source Backends

Note

For the development prototype release, b10-auth supports a SQLite3 data source backend and in-memory data source backend. @@ -539,7 +544,7 @@ This may be a temporary setting until then. The default is /usr/local/var/.) This data file location may be changed by defining the database_file configuration. -

Loading Master Zones Files

+

Loading Master Zones Files

RFC 1035 style DNS master zone files may imported into a BIND 10 data source by using the b10-loadzone utility. @@ -579,7 +584,7 @@ This may be a temporary setting until then. provide secondary service.

Note

The current development release of BIND 10 only supports - AXFR. (IXFR is not supported.) + AXFR. (IXFR is not supported.) @@ -601,7 +606,7 @@ This may be a temporary setting until then. NOTIFY messages to slaves.

Note

The current development release of BIND 10 only supports - AXFR. (IXFR is not supported.) + AXFR. (IXFR is not supported.) Access control is not yet provided.

Chapter 11. Secondary Manager

The b10-zonemgr process is started by @@ -617,13 +622,13 @@ This may be a temporary setting until then.

Note

Access control (such as allowing notifies) is not yet provided. The primary/secondary service is not yet complete. -

Chapter 12. Recursive Name Server

Table of Contents

Forwarding

+

Chapter 12. Recursive Name Server

Table of Contents

Access Control
Forwarding

The b10-resolver process is started by bind10.

The main bind10 process can be configured - to select to run either the authoritative or resolver. + to select to run either the authoritative or resolver or both. By default, it starts the authoritative service. @@ -639,14 +644,52 @@ This may be a temporary setting until then. The master bind10 will stop and start the desired services.

- The resolver also needs to be configured to listen on an address - and port: + By default, the resolver listens on port 53 for 127.0.0.1 and ::1. + The following example shows how it can be configured to + listen on an additional address (and port):

-> config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]
+> config add Resolver/listen_on
+> config set Resolver/listen_on[2]/address "192.168.1.1"
+> config set Resolver/listen_on[2]/port 53
 > config commit
 

-

Forwarding

+

(Replace the 2 + as needed; run config show + Resolver/listen_on if needed.)

Access Control

+ By default, the b10-resolver daemon only accepts + DNS queries from the localhost (127.0.0.1 and ::1). + The Resolver/query_acl configuration may + be used to reject, drop, or allow specific IPs or networks. + This configuration list is first match. +

+ The configuration's action item may be + set to ACCEPT to allow the incoming query, + REJECT to respond with a DNS REFUSED return + code, or DROP to ignore the query without + any response (such as a blackhole). For more information, + see the respective debugging messages: RESOLVER_QUERY_ACCEPTED, + RESOLVER_QUERY_REJECTED, + and RESOLVER_QUERY_DROPPED. +

+ The required configuration's from item is set + to an IPv4 or IPv6 address, addresses with an network mask, or to + the special lowercase keywords any6 (for + any IPv6 address) or any4 (for any IPv4 + address). +

+ For example to allow the 192.168.1.0/24 + network to use your recursive name server, at the + bindctl prompt run: +

+> config add Resolver/query_acl
+> config set Resolver/query_acl[2]/action "ACCEPT"
+> config set Resolver/query_acl[2]/from "192.168.1.0/24"
+> config commit
+

(Replace the 2 + as needed; run config show + Resolver/query_acl if needed.)

Note

This prototype access control configuration + syntax may be changed.

Forwarding

To enable forwarding, the upstream address and port must be configured to forward queries to, such as: @@ -694,48 +737,414 @@ This may be a temporary setting until then. "stats.timestamp": 1295543046.823504 }

-

Chapter 14. Logging

- Each message written by BIND 10 to the configured logging destinations - comprises a number of components that identify the origin of the - message and, if the message indicates a problem, information about the - problem that may be useful in fixing it. -

- Consider the message below logged to a file: -

2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink]
-    ASIODNS_OPENSOCK error 111 opening TCP socket to 127.0.0.1(53)

-

- Note: the layout of messages written to the system logging - file (syslog) may be slightly different. This message has - been split across two lines here for display reasons; in the - logging file, it will appear on one line.) -

- The log message comprises a number of components: +

Chapter 14. Logging

Logging configuration

-

2011-06-15 13:48:22.034

- The date and time at which the message was generated. -

ERROR

- The severity of the message. -

[b10-resolver.asiolink]

- The source of the message. This comprises two components: - the BIND 10 process generating the message (in this - case, b10-resolver) and the module - within the program from which the message originated - (which in the example is the asynchronous I/O link - module, asiolink). -

ASIODNS_OPENSOCK

+ The logging system in BIND 10 is configured through the + Logging module. All BIND 10 modules will look at the + configuration in Logging to see what should be logged and + to where. + + + +

Loggers

+ + Within BIND 10, a message is logged through a component + called a "logger". Different parts of BIND 10 log messages + through different loggers, and each logger can be configured + independently of one another. + +

+ + In the Logging module, you can specify the configuration + for zero or more loggers; any that are not specified will + take appropriate default values.. + +

+ + The three most important elements of a logger configuration + are the name (the component that is + generating the messages), the severity + (what to log), and the output_options + (where to log). + +

name (string)

+ Each logger in the system has a name, the name being that + of the component using it to log messages. For instance, + if you want to configure logging for the resolver module, + you add an entry for a logger named Resolver. This + configuration will then be used by the loggers in the + Resolver module, and all the libraries used by it. +

+ + If you want to specify logging for one specific library + within the module, you set the name to + module.library. For example, the + logger used by the nameserver address store component + has the full name of Resolver.nsas. If + there is no entry in Logging for a particular library, + it will use the configuration given for the module. + + + +

+ + + + To illustrate this, suppose you want the cache library + to log messages of severity DEBUG, and the rest of the + resolver code to log messages of severity INFO. To achieve + this you specify two loggers, one with the name + Resolver and severity INFO, and one with + the name Resolver.cache with severity + DEBUG. As there are no entries for other libraries (e.g. + the nsas), they will use the configuration for the module + (Resolver), so giving the desired behavior. + +

+ + One special case is that of a module name of * + (asterisks), which is interpreted as any + module. You can set global logging options by using this, + including setting the logging configuration for a library + that is used by multiple modules (e.g. *.config + specifies the configuration library code in whatever + module is using it). + +

+ + If there are multiple logger specifications in the + configuration that might match a particular logger, the + specification with the more specific logger name takes + precedence. For example, if there are entries for for + both * and Resolver, the + resolver module — and all libraries it uses — + will log messages according to the configuration in the + second entry (Resolver). All other modules + will use the configuration of the first entry + (*). If there was also a configuration + entry for Resolver.cache, the cache library + within the resolver would use that in preference to the + entry for Resolver. + +

+ + One final note about the naming. When specifying the + module name within a logger, use the name of the module + as specified in bindctl, e.g. + Resolver for the resolver module, + Xfrout for the xfrout module, etc. When + the message is logged, the message will include the name + of the logger generating the message, but with the module + name replaced by the name of the process implementing + the module (so for example, a message generated by the + Auth.cache logger will appear in the output + with a logger name of b10-auth.cache). + +

severity (string)

+ + This specifies the category of messages logged. + Each message is logged with an associated severity which + may be one of the following (in descending order of + severity): +

  • FATAL
  • ERROR
  • WARN
  • INFO
  • DEBUG

+ + When the severity of a logger is set to one of these + values, it will only log messages of that severity, and + the severities above it. The severity may also be set to + NONE, in which case all messages from that logger are + inhibited. + + + +

output_options (list)

+ + Each logger can have zero or more + output_options. These specify where log + messages are sent to. These are explained in detail below. + +

+ + The other options for a logger are: + +

debuglevel (integer)

+ + When a logger's severity is set to DEBUG, this value + specifies what debug messages should be printed. It ranges + from 0 (least verbose) to 99 (most verbose). +

+ + If severity for the logger is not DEBUG, this value is ignored. + +

additive (true or false)

+ + If this is true, the output_options from + the parent will be used. For example, if there are two + loggers configured; Resolver and + Resolver.cache, and additive + is true in the second, it will write the log messages + not only to the destinations specified for + Resolver.cache, but also to the destinations + as specified in the output_options in + the logger named Resolver. + + + +

Output Options

+ + The main settings for an output option are the + destination and a value called + output, the meaning of which depends on + the destination that is set. + +

destination (string)

+ + The destination is the type of output. It can be one of: + +

  • console
  • file
  • syslog

output (string)

+ + Depending on what is set as the output destination, this + value is interpreted as follows: + +

destination is console
+ The value of output must be one of stdout + (messages printed to standard output) or + stderr (messages printed to standard + error). +
destination is file
+ The value of output is interpreted as a file name; + log messages will be appended to this file. +
destination is syslog
+ The value of output is interpreted as the + syslog facility (e.g. + local0) that should be used + for log messages. +

+ + The other options for output_options are: + +

flush (true of false)

+ Flush buffers after each log message. Doing this will + reduce performance but will ensure that if the program + terminates abnormally, all messages up to the point of + termination are output. +

maxsize (integer)

+ Only relevant when destination is file, this is maximum + file size of output files in bytes. When the maximum + size is reached, the file is renamed and a new file opened. + (For example, a ".1" is appended to the name — + if a ".1" file exists, it is renamed ".2", + etc.) +

+ If this is 0, no maximum file size is used. +

maxver (integer)

+ Maximum number of old log files to keep around when + rolling the output file. Only relevant when + destination is file. +

Example session

+ + In this example we want to set the global logging to + write to the file /var/log/my_bind10.log, + at severity WARN. We want the authoritative server to + log at DEBUG with debuglevel 40, to a different file + (/tmp/debug_messages). + +

+ + Start bindctl. + +

+ +

["login success "]
+> config show Logging
+Logging/loggers	[]	list
+

+ +

+ + By default, no specific loggers are configured, in which + case the severity defaults to INFO and the output is + written to stderr. + +

+ + Let's first add a default logger: + +

+ +

> config add Logging/loggers
+> config show Logging
+Logging/loggers/	list	(modified)
+

+ +

+ + The loggers value line changed to indicate that it is no + longer an empty list: + +

+ +

> config show Logging/loggers
+Logging/loggers[0]/name	""	string	(default)
+Logging/loggers[0]/severity	"INFO"	string	(default)
+Logging/loggers[0]/debuglevel	0	integer	(default)
+Logging/loggers[0]/additive	false	boolean	(default)
+Logging/loggers[0]/output_options	[]	list	(default)
+

+ +

+ + The name is mandatory, so we must set it. We will also + change the severity as well. Let's start with the global + logger. + +

+ +

> config set Logging/loggers[0]/name *
+> config set Logging/loggers[0]/severity WARN
+> config show Logging/loggers
+Logging/loggers[0]/name	"*"	string	(modified)
+Logging/loggers[0]/severity	"WARN"	string	(modified)
+Logging/loggers[0]/debuglevel	0	integer	(default)
+Logging/loggers[0]/additive	false	boolean	(default)
+Logging/loggers[0]/output_options	[]	list	(default)
+

+ +

+ + Of course, we need to specify where we want the log + messages to go, so we add an entry for an output option. + +

+ +

>  config add Logging/loggers[0]/output_options
+>  config show Logging/loggers[0]/output_options
+Logging/loggers[0]/output_options[0]/destination	"console"	string	(default)
+Logging/loggers[0]/output_options[0]/output	"stdout"	string	(default)
+Logging/loggers[0]/output_options[0]/flush	false	boolean	(default)
+Logging/loggers[0]/output_options[0]/maxsize	0	integer	(default)
+Logging/loggers[0]/output_options[0]/maxver	0	integer	(default)
+

+ + +

+ + These aren't the values we are looking for. + +

+ +

>  config set Logging/loggers[0]/output_options[0]/destination file
+>  config set Logging/loggers[0]/output_options[0]/output /var/log/bind10.log
+>  config set Logging/loggers[0]/output_options[0]/maxsize 30000
+>  config set Logging/loggers[0]/output_options[0]/maxver 8
+

+ +

+ + Which would make the entire configuration for this logger + look like: + +

+ +

>  config show all Logging/loggers
+Logging/loggers[0]/name	"*"	string	(modified)
+Logging/loggers[0]/severity	"WARN"	string	(modified)
+Logging/loggers[0]/debuglevel	0	integer	(default)
+Logging/loggers[0]/additive	false	boolean	(default)
+Logging/loggers[0]/output_options[0]/destination	"file"	string	(modified)
+Logging/loggers[0]/output_options[0]/output	"/var/log/bind10.log"	string	(modified)
+Logging/loggers[0]/output_options[0]/flush	false	boolean	(default)
+Logging/loggers[0]/output_options[0]/maxsize	30000	integer	(modified)
+Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
+

+ +

+ + That looks OK, so let's commit it before we add the + configuration for the authoritative server's logger. + +

+ +

>  config commit

+ +

+ + Now that we have set it, and checked each value along + the way, adding a second entry is quite similar. + +

+ +

>  config add Logging/loggers
+>  config set Logging/loggers[1]/name Auth
+>  config set Logging/loggers[1]/severity DEBUG
+>  config set Logging/loggers[1]/debuglevel 40
+>  config add Logging/loggers[1]/output_options
+>  config set Logging/loggers[1]/output_options[0]/destination file
+>  config set Logging/loggers[1]/output_options[0]/output /tmp/auth_debug.log
+>  config commit
+

+ +

+ + And that's it. Once we have found whatever it was we + needed the debug messages for, we can simply remove the + second logger to let the authoritative server use the + same settings as the rest. + +

+ +

>  config remove Logging/loggers[1]
+>  config commit
+

+ +

+ + And every module will now be using the values from the + logger named *. + +

Logging Message Format

+ Each message written by BIND 10 to the configured logging + destinations comprises a number of components that identify + the origin of the message and, if the message indicates + a problem, information about the problem that may be + useful in fixing it. +

+ Consider the message below logged to a file: +

2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink]
+    ASIODNS_OPENSOCK error 111 opening TCP socket to 127.0.0.1(53)

+

+ Note: the layout of messages written to the system logging + file (syslog) may be slightly different. This message has + been split across two lines here for display reasons; in the + logging file, it will appear on one line.) +

+ The log message comprises a number of components: + +

2011-06-15 13:48:22.034

+ The date and time at which the message was generated. +

ERROR

+ The severity of the message. +

[b10-resolver.asiolink]

+ The source of the message. This comprises two components: + the BIND 10 process generating the message (in this + case, b10-resolver) and the module + within the program from which the message originated + (which in the example is the asynchronous I/O link + module, asiolink). +

ASIODNS_OPENSOCK

The message identification. Every message in BIND 10 has a unique identification, which can be used as an index into the BIND 10 Messages Manual (http://bind10.isc.org/docs/bind10-messages.html) from which more information can be obtained. -

error 111 opening TCP socket to 127.0.0.1(53)

- A brief description of the cause of the problem. Within this text, - information relating to the condition that caused the message to - be logged will be included. In this example, error number 111 - (an operating system-specific error number) was encountered when - trying to open a TCP connection to port 53 on the local system - (address 127.0.0.1). The next step would be to find out the reason - for the failure by consulting your system's documentation to - identify what error number 111 means. -

- -

+

error 111 opening TCP socket to 127.0.0.1(53)

+ A brief description of the cause of the problem. + Within this text, information relating to the condition + that caused the message to be logged will be included. + In this example, error number 111 (an operating + system-specific error number) was encountered when + trying to open a TCP connection to port 53 on the + local system (address 127.0.0.1). The next step + would be to find out the reason for the failure by + consulting your system's documentation to identify + what error number 111 means. +

+

diff --git a/doc/guide/bind10-messages.html b/doc/guide/bind10-messages.html index ecebcd825c..237b7adf80 100644 --- a/doc/guide/bind10-messages.html +++ b/doc/guide/bind10-messages.html @@ -1,10 +1,10 @@ -BIND 10 Messages Manual

BIND 10 Messages Manual

This is the messages manual for BIND 10 version - 20110705.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by +BIND 10 Messages Manual

BIND 10 Messages Manual

This is the messages manual for BIND 10 version + 20110809.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers.

- This is the messages manual for BIND 10 version 20110705. + This is the messages manual for BIND 10 version 20110809. The most up-to-date version of this document, along with other documents for BIND 10, can be found at http://bind10.isc.org/docs. @@ -36,21 +36,21 @@ enabled.

ASIODNS_OPEN_SOCKET error %1 opening %2 socket to %3(%4)

The asynchronous I/O code encountered an error when trying to open a socket of the specified protocol in order to send a message to the target address. -The number of the system error that cause the problem is given in the +The number of the system error that caused the problem is given in the message.

ASIODNS_READ_DATA error %1 reading %2 data from %3(%4)

The asynchronous I/O code encountered an error when trying to read data from the specified address on the given protocol. The number of the system -error that cause the problem is given in the message. +error that caused the problem is given in the message.

ASIODNS_READ_TIMEOUT receive timeout while waiting for data from %1(%2)

An upstream fetch from the specified address timed out. This may happen for any number of reasons and is most probably a problem at the remote server or a problem on the network. The message will only appear if debug is enabled.

ASIODNS_SEND_DATA error %1 sending data using %2 to %3(%4)

-The asynchronous I/O code encountered an error when trying send data to -the specified address on the given protocol. The the number of the system -error that cause the problem is given in the message. +The asynchronous I/O code encountered an error when trying to send data to +the specified address on the given protocol. The number of the system +error that caused the problem is given in the message.

ASIODNS_UNKNOWN_ORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)

An internal consistency check on the origin of a message from the asynchronous I/O module failed. This may indicate an internal error; @@ -99,7 +99,7 @@ This is a debug message produced by the authoritative server when it accesses a datebase data source, listing the file that is being accessed.

AUTH_DNS_SERVICES_CREATED DNS services created

This is a debug message indicating that the component that will handling -incoming queries for the authoritiative server (DNSServices) has been +incoming queries for the authoritative server (DNSServices) has been successfully created. It is issued during server startup is an indication that the initialization is proceeding normally.

AUTH_HEADER_PARSE_FAIL unable to parse header in received DNS packet: %1

@@ -108,7 +108,7 @@ attempt to parse the header of a received DNS packet has failed. (The reason for the failure is given in the message.) The server will drop the packet.

AUTH_LOAD_TSIG loading TSIG keys

-This is a debug message indicating that the authoritiative server +This is a debug message indicating that the authoritative server has requested the keyring holding TSIG keys from the configuration database. It is issued during server startup is an indication that the initialization is proceeding normally. @@ -164,8 +164,8 @@ encountered an internal error whilst processing a received packet: the cause of the error is included in the message.

The server will return a SERVFAIL error code to the sender of the packet. -However, this message indicates a potential error in the server. -Please open a bug ticket for this issue. +This message indicates a potential error in the server. Please open a +bug ticket for this issue.

AUTH_RECEIVED_COMMAND command '%1' received

This is a debug message issued when the authoritative server has received a command on the command channel. @@ -220,7 +220,7 @@ has established communication over the previously created statistics channel. It is issued during server startup is an indication that the initialization is proceeding normally.

AUTH_STATS_COMMS communication error in sending statistics data: %1

-An error was encountered when the authoritiative server tried to send data +An error was encountered when the authoritative server tried to send data to the statistics daemon. The message includes additional information describing the reason for the failure.

AUTH_STATS_TIMEOUT timeout while sending statistics data: %1

@@ -259,6 +259,246 @@ This is a debug message output during the processing of a NOTIFY request. The zone manager component has been informed of the request, but has returned an error response (which is included in the message). The NOTIFY request will not be honored. +

BIND10_CHECK_MSGQ_ALREADY_RUNNING checking if msgq is already running

+The boss process is starting up and will now check if the message bus +daemon is already running. If so, it will not be able to start, as it +needs a dedicated message bus. +

BIND10_CONFIGURATION_START_AUTH start authoritative server: %1

+This message shows whether or not the authoritative server should be +started according to the configuration. +

BIND10_CONFIGURATION_START_RESOLVER start resolver: %1

+This message shows whether or not the resolver should be +started according to the configuration. +

BIND10_INVALID_USER invalid user: %1

+The boss process was started with the -u option, to drop root privileges +and continue running as the specified user, but the user is unknown. +

BIND10_KILLING_ALL_PROCESSES killing all started processes

+The boss module was not able to start every process it needed to start +during startup, and will now kill the processes that did get started. +

BIND10_KILL_PROCESS killing process %1

+The boss module is sending a kill signal to process with the given name, +as part of the process of killing all started processes during a failed +startup, as described for BIND10_KILLING_ALL_PROCESSES +

BIND10_MSGQ_ALREADY_RUNNING msgq daemon already running, cannot start

+There already appears to be a message bus daemon running. Either an +old process was not shut down correctly, and needs to be killed, or +another instance of BIND10, with the same msgq domain socket, is +running, which needs to be stopped. +

BIND10_MSGQ_DAEMON_ENDED b10-msgq process died, shutting down

+The message bus daemon has died. This is a fatal error, since it may +leave the system in an inconsistent state. BIND10 will now shut down. +

BIND10_MSGQ_DISAPPEARED msgq channel disappeared

+While listening on the message bus channel for messages, it suddenly +disappeared. The msgq daemon may have died. This might lead to an +inconsistent state of the system, and BIND 10 will now shut down. +

BIND10_PROCESS_ENDED_NO_EXIT_STATUS process %1 (PID %2) died: exit status not available

+The given process ended unexpectedly, but no exit status is +available. See BIND10_PROCESS_ENDED_WITH_EXIT_STATUS for a longer +description. +

BIND10_PROCESS_ENDED_WITH_EXIT_STATUS process %1 (PID %2) terminated, exit status = %3

+The given process ended unexpectedly with the given exit status. +Depending on which module it was, it may simply be restarted, or it +may be a problem that will cause the boss module to shut down too. +The latter happens if it was the message bus daemon, which, if it has +died suddenly, may leave the system in an inconsistent state. BIND10 +will also shut down now if it has been run with --brittle. +

BIND10_READING_BOSS_CONFIGURATION reading boss configuration

+The boss process is starting up, and will now process the initial +configuration, as received from the configuration manager. +

BIND10_RECEIVED_COMMAND received command: %1

+The boss module received a command and shall now process it. The command +is printed. +

BIND10_RECEIVED_NEW_CONFIGURATION received new configuration: %1

+The boss module received a configuration update and is going to apply +it now. The new configuration is printed. +

BIND10_RECEIVED_SIGNAL received signal %1

+The boss module received the given signal. +

BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2)

+The given process has been restarted successfully, and is now running +with the given process id. +

BIND10_RESURRECTING_PROCESS resurrecting dead %1 process...

+The given process has ended unexpectedly, and is now restarted. +

BIND10_SELECT_ERROR error in select() call: %1

+There was a fatal error in the call to select(), used to see if a child +process has ended or if there is a message on the message bus. This +should not happen under normal circumstances and is considered fatal, +so BIND 10 will now shut down. The specific error is printed. +

BIND10_SEND_SIGKILL sending SIGKILL to %1 (PID %2)

+The boss module is sending a SIGKILL signal to the given process. +

BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)

+The boss module is sending a SIGTERM signal to the given process. +

BIND10_SHUTDOWN stopping the server

+The boss process received a command or signal telling it to shut down. +It will send a shutdown command to each process. The processes that do +not shut down will then receive a SIGTERM signal. If that doesn't work, +it shall send SIGKILL signals to the processes still alive. +

BIND10_SHUTDOWN_COMPLETE all processes ended, shutdown complete

+All child processes have been stopped, and the boss process will now +stop itself. +

BIND10_SOCKCREATOR_BAD_CAUSE unknown error cause from socket creator: %1

+The socket creator reported an error when creating a socket. But the function +which failed is unknown (not one of 'S' for socket or 'B' for bind). +

BIND10_SOCKCREATOR_BAD_RESPONSE unknown response for socket request: %1

+The boss requested a socket from the creator, but the answer is unknown. This +looks like a programmer error. +

BIND10_SOCKCREATOR_CRASHED the socket creator crashed

+The socket creator terminated unexpectedly. It is not possible to restart it +(because the boss already gave up root privileges), so the system is going +to terminate. +

BIND10_SOCKCREATOR_EOF eof while expecting data from socket creator

+There should be more data from the socket creator, but it closed the socket. +It probably crashed. +

BIND10_SOCKCREATOR_INIT initializing socket creator parser

+The boss module initializes routines for parsing the socket creator +protocol. +

BIND10_SOCKCREATOR_KILL killing the socket creator

+The socket creator is being terminated the aggressive way, by sending it +sigkill. This should not happen usually. +

BIND10_SOCKCREATOR_TERMINATE terminating socket creator

+The boss module sends a request to terminate to the socket creator. +

BIND10_SOCKCREATOR_TRANSPORT_ERROR transport error when talking to the socket creator: %1

+Either sending or receiving data from the socket creator failed with the given +error. The creator probably crashed or some serious OS-level problem happened, +as the communication happens only on local host. +

BIND10_SOCKET_CREATED successfully created socket %1

+The socket creator successfully created and sent a requested socket, it has +the given file number. +

BIND10_SOCKET_ERROR error on %1 call in the creator: %2/%3

+The socket creator failed to create the requested socket. It failed on the +indicated OS API function with given error. +

BIND10_SOCKET_GET requesting socket [%1]:%2 of type %3 from the creator

+The boss forwards a request for a socket to the socket creator. +

BIND10_STARTED_PROCESS started %1

+The given process has successfully been started. +

BIND10_STARTED_PROCESS_PID started %1 (PID %2)

+The given process has successfully been started, and has the given PID. +

BIND10_STARTING starting BIND10: %1

+Informational message on startup that shows the full version. +

BIND10_STARTING_PROCESS starting process %1

+The boss module is starting the given process. +

BIND10_STARTING_PROCESS_PORT starting process %1 (to listen on port %2)

+The boss module is starting the given process, which will listen on the +given port number. +

BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2#%3)

+The boss module is starting the given process, which will listen on the +given address and port number (written as <address>#<port>). +

BIND10_STARTUP_COMPLETE BIND 10 started

+All modules have been successfully started, and BIND 10 is now running. +

BIND10_STARTUP_ERROR error during startup: %1

+There was a fatal error when BIND10 was trying to start. The error is +shown, and BIND10 will now shut down. +

BIND10_START_AS_NON_ROOT starting %1 as a user, not root. This might fail.

+The given module is being started or restarted without root privileges. +If the module needs these privileges, it may have problems starting. +Note that this issue should be resolved by the pending 'socket-creator' +process; once that has been implemented, modules should not need root +privileges anymore. See tickets #800 and #801 for more information. +

BIND10_STOP_PROCESS asking %1 to shut down

+The boss module is sending a shutdown command to the given module over +the message channel. +

BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited

+An unknown child process has exited. The PID is printed, but no further +action will be taken by the boss process. +

CACHE_ENTRY_MISSING_RRSET missing RRset to generate message for %1

+The cache tried to generate the complete answer message. It knows the structure +of the message, but some of the RRsets to be put there are not in cache (they +probably expired already). Therefore it pretends the message was not found. +

CACHE_LOCALZONE_FOUND found entry with key %1 in local zone data

+Debug message, noting that the requested data was successfully found in the +local zone data of the cache. +

CACHE_LOCALZONE_UNKNOWN entry with key %1 not found in local zone data

+Debug message. The requested data was not found in the local zone data. +

CACHE_LOCALZONE_UPDATE updating local zone element at key %1

+Debug message issued when there's update to the local zone section of cache. +

CACHE_MESSAGES_DEINIT deinitialized message cache

+Debug message. It is issued when the server deinitializes the message cache. +

CACHE_MESSAGES_EXPIRED found an expired message entry for %1 in the message cache

+Debug message. The requested data was found in the message cache, but it +already expired. Therefore the cache removes the entry and pretends it found +nothing. +

CACHE_MESSAGES_FOUND found a message entry for %1 in the message cache

+Debug message. We found the whole message in the cache, so it can be returned +to user without any other lookups. +

CACHE_MESSAGES_INIT initialized message cache for %1 messages of class %2

+Debug message issued when a new message cache is issued. It lists the class +of messages it can hold and the maximum size of the cache. +

CACHE_MESSAGES_REMOVE removing old instance of %1/%2/%3 first

+Debug message. This may follow CACHE_MESSAGES_UPDATE and indicates that, while +updating, the old instance is being removed prior of inserting a new one. +

CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3

+Debug message, noting that the given message can not be cached. This is because +there's no SOA record in the message. See RFC 2308 section 5 for more +information. +

CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache

+Debug message. The message cache didn't find any entry for the given key. +

CACHE_MESSAGES_UPDATE updating message entry %1/%2/%3

+Debug message issued when the message cache is being updated with a new +message. Either the old instance is removed or, if none is found, new one +is created. +

CACHE_RESOLVER_DEEPEST looking up deepest NS for %1/%2

+Debug message. The resolver cache is looking up the deepest known nameserver, +so the resolution doesn't have to start from the root. +

CACHE_RESOLVER_INIT initializing resolver cache for class %1

+Debug message. The resolver cache is being created for this given class. +

CACHE_RESOLVER_INIT_INFO initializing resolver cache for class %1

+Debug message, the resolver cache is being created for this given class. The +difference from CACHE_RESOLVER_INIT is only in different format of passed +information, otherwise it does the same. +

CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data

+Debug message. The resolver cache found a complete message for the user query +in the zone data. +

CACHE_RESOLVER_LOCAL_RRSET RRset for %1/%2 found in local zone data

+Debug message. The resolver cache found a requested RRset in the local zone +data. +

CACHE_RESOLVER_LOOKUP_MSG looking up message in resolver cache for %1/%2

+Debug message. The resolver cache is trying to find a message to answer the +user query. +

CACHE_RESOLVER_LOOKUP_RRSET looking up RRset in resolver cache for %1/%2

+Debug message. The resolver cache is trying to find an RRset (which usually +originates as internally from resolver). +

CACHE_RESOLVER_NO_QUESTION answer message for %1/%2 has empty question section

+The cache tried to fill in found data into the response message. But it +discovered the message contains no question section, which is invalid. +This is likely a programmer error, please submit a bug report. +

CACHE_RESOLVER_UNKNOWN_CLASS_MSG no cache for class %1

+Debug message. While trying to lookup a message in the resolver cache, it was +discovered there's no cache for this class at all. Therefore no message is +found. +

CACHE_RESOLVER_UNKNOWN_CLASS_RRSET no cache for class %1

+Debug message. While trying to lookup an RRset in the resolver cache, it was +discovered there's no cache for this class at all. Therefore no data is found. +

CACHE_RESOLVER_UPDATE_MSG updating message for %1/%2/%3

+Debug message. The resolver is updating a message in the cache. +

CACHE_RESOLVER_UPDATE_RRSET updating RRset for %1/%2/%3

+Debug message. The resolver is updating an RRset in the cache. +

CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG no cache for class %1

+Debug message. While trying to insert a message into the cache, it was +discovered that there's no cache for the class of message. Therefore +the message will not be cached. +

CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET no cache for class %1

+Debug message. While trying to insert an RRset into the cache, it was +discovered that there's no cache for the class of the RRset. Therefore +the message will not be cached. +

CACHE_RRSET_EXPIRED found expired RRset %1/%2/%3

+Debug message. The requested data was found in the RRset cache. However, it is +expired, so the cache removed it and is going to pretend nothing was found. +

CACHE_RRSET_INIT initializing RRset cache for %1 RRsets of class %2

+Debug message. The RRset cache to hold at most this many RRsets for the given +class is being created. +

CACHE_RRSET_LOOKUP looking up %1/%2/%3 in RRset cache

+Debug message. The resolver is trying to look up data in the RRset cache. +

CACHE_RRSET_NOT_FOUND no RRset found for %1/%2/%3

+Debug message which can follow CACHE_RRSET_LOOKUP. This means the data is not +in the cache. +

CACHE_RRSET_REMOVE_OLD removing old RRset for %1/%2/%3 to make space for new one

+Debug message which can follow CACHE_RRSET_UPDATE. During the update, the cache +removed an old instance of the RRset to replace it with the new one. +

CACHE_RRSET_UNTRUSTED not replacing old RRset for %1/%2/%3, it has higher trust level

+Debug message which can follow CACHE_RRSET_UPDATE. The cache already holds the +same RRset, but from more trusted source, so the old one is kept and new one +ignored. +

CACHE_RRSET_UPDATE updating RRset %1/%2/%3 in the cache

+Debug message. The RRset is updating its data with this given RRset.

CC_ASYNC_READ_FAILED asynchronous read failed

This marks a low level error, we tried to read data from the message queue daemon asynchronously, but the ASIO library returned an error. @@ -290,10 +530,10 @@ Debug message, we're about to send a message over the command channel. This happens when garbage comes over the command channel or some kind of confusion happens in the program. The data received from the socket make no sense if we interpret it as lengths of message. The first one is total length -of message, the second length of the header. The header and it's length -(2 bytes) is counted in the total length. +of the message; the second is the length of the header. The header +and its length (2 bytes) is counted in the total length.

CC_LENGTH_NOT_READY length not ready

-There should be data representing length of message on the socket, but it +There should be data representing the length of message on the socket, but it is not there.

CC_NO_MESSAGE no message ready to be received yet

The program polled for incoming messages, but there was no message waiting. @@ -334,6 +574,12 @@ all messages must contain at least the envelope. An older version of the configuration database has been found, from which there was an automatic upgrade path to the current version. These changes are now applied, and no action from the administrator is necessary. +

CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2

+The configuration manager sent a configuration update to a module, but +the module responded with an answer that could not be parsed. The answer +message appears to be invalid JSON data, or not decodable to a string. +This is likely to be a problem in the module in question. The update is +assumed to have failed, and will not be stored.

CFGMGR_CC_SESSION_ERROR Error connecting to command channel: %1

The configuration manager daemon was unable to connect to the messaging system. The most likely cause is that msgq is not running. @@ -357,6 +603,58 @@ configuration is not stored.

CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down

There was a keyboard interrupt signal to stop the cfgmgr daemon. The daemon will now shut down. +

CMDCTL_BAD_CONFIG_DATA error in config data: %1

+There was an error reading the updated configuration data. The specific +error is printed. +

CMDCTL_BAD_PASSWORD bad password for user: %1

+A login attempt was made to b10-cmdctl, but the password was wrong. +Users can be managed with the tool b10-cmdctl-usermgr. +

CMDCTL_CC_SESSION_ERROR error reading from cc channel: %1

+There was a problem reading from the command and control channel. The +most likely cause is that the message bus daemon is not running. +

CMDCTL_CC_SESSION_TIMEOUT timeout on cc channel

+A timeout occurred when waiting for essential data from the cc session. +This usually occurs when b10-cfgmgr is not running or not responding. +Since we are waiting for essential information, this is a fatal error, +and the cmdctl daemon will now shut down. +

CMDCTL_COMMAND_ERROR error in command %1 to module %2: %3

+An error was encountered sending the given command to the given module. +Either there was a communication problem with the module, or the module +was not able to process the command, and sent back an error. The +specific error is printed in the message. +

CMDCTL_COMMAND_SENT command '%1' to module '%2' was sent

+This debug message indicates that the given command has been sent to +the given module. +

CMDCTL_NO_SUCH_USER username not found in user database: %1

+A login attempt was made to b10-cmdctl, but the username was not known. +Users can be added with the tool b10-cmdctl-usermgr. +

CMDCTL_NO_USER_ENTRIES_READ failed to read user information, all users will be denied

+The b10-cmdctl daemon was unable to find any user data in the user +database file. Either it was unable to read the file (in which case +this message follows a message CMDCTL_USER_DATABASE_READ_ERROR +containing a specific error), or the file was empty. Users can be added +with the tool b10-cmdctl-usermgr. +

CMDCTL_SEND_COMMAND sending command %1 to module %2

+This debug message indicates that the given command is being sent to +the given module. +

CMDCTL_SSL_SETUP_FAILURE_USER_DENIED failed to create an SSL connection (user denied): %1

+The user was denied because the SSL connection could not successfully +be set up. The specific error is given in the log message. Possible +causes may be that the ssl request itself was bad, or the local key or +certificate file could not be read. +

CMDCTL_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down

+There was a keyboard interrupt signal to stop the cmdctl daemon. The +daemon will now shut down. +

CMDCTL_UNCAUGHT_EXCEPTION uncaught exception: %1

+The b10-cmdctl daemon encountered an uncaught exception and +will now shut down. This is indicative of a programming error and +should not happen under normal circumstances. The exception message +is printed. +

CMDCTL_USER_DATABASE_READ_ERROR failed to read user database file %1: %2

+The b10-cmdctl daemon was unable to read the user database file. The +file may be unreadable for the daemon, or it may be corrupted. In the +latter case, it can be recreated with b10-cmdctl-usermgr. The specific +error is printed in the log message.

CONFIG_CCSESSION_MSG error in CC session message: %1

There was a problem with an incoming message on the command and control channel. The message does not appear to be a valid command, and is @@ -377,10 +675,39 @@ the configuration. The full error message answer from the configuration 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_GET_FAILED error getting configuration from cfgmgr: %1

+The configuration manager returned an error response when the module +requested its configuration. The full error message answer from the +configuration manager is appended to the log error.

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_CONFIG_ERRORS error(s) in logging configuration: %1

+There was a logging configuration update, but the internal validator +for logging configuration found that it contained errors. The errors +are shown, and the update is ignored. +

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 +logger that matches the logger specification for the program. The logging +configuration for the program will be updated with the information. +

CONFIG_LOG_IGNORE_EXPLICIT ignoring 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 logger. As this does not match the logger specification for the +program, it has been ignored. +

CONFIG_LOG_IGNORE_WILD ignoring logging configuration for wildcard logger %1

+This is a debug message. When processing the "loggers" part of the +configuration file, the configuration library found the named wildcard +entry (one containing the "*" character) that matched a logger already +matched by an explicitly named entry. The configuration is ignored. +

CONFIG_LOG_WILD_MATCH will use logging configuration for wildcard logger %1

+This is a debug message. When processing the "loggers" part of +the configuration file, the configuration library found the named +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_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 @@ -395,49 +722,92 @@ manager. There was an error opening the given file. The reason for the failure is included in the message.

DATASRC_CACHE_CREATE creating the hotspot cache

-Debug information that the hotspot cache was created at startup. +This is a debug message issued during startup when the hotspot cache +is created.

DATASRC_CACHE_DESTROY destroying the hotspot cache

Debug information. The hotspot cache is being destroyed. -

DATASRC_CACHE_DISABLE disabling the cache

-The hotspot cache is disabled from now on. It is not going to store -information or return anything. -

DATASRC_CACHE_ENABLE enabling the cache

-The hotspot cache is enabled from now on. -

DATASRC_CACHE_EXPIRED the item '%1' is expired

-Debug information. There was an attempt to look up an item in the hotspot -cache. And the item was actually there, but it was too old, so it was removed -instead and nothing is reported (the external behaviour is the same as with -CACHE_NOT_FOUND). +

DATASRC_CACHE_DISABLE disabling the hotspot cache

+A debug message issued when the hotspot cache is disabled. +

DATASRC_CACHE_ENABLE enabling the hotspot cache

+A debug message issued when the hotspot cache is enabled. +

DATASRC_CACHE_EXPIRED item '%1' in the hotspot cache has expired

+A debug message issued when a hotspot cache lookup located the item but it +had expired. The item was removed and the program proceeded as if the item +had not been found.

DATASRC_CACHE_FOUND the item '%1' was found

-Debug information. An item was successfully looked up in the hotspot cache. -

DATASRC_CACHE_FULL cache is full, dropping oldest

+Debug information. An item was successfully located in the hotspot cache. +

DATASRC_CACHE_FULL hotspot cache is full, dropping oldest

Debug information. After inserting an item into the hotspot cache, the maximum number of items was exceeded, so the least recently used item will be dropped. This should be directly followed by CACHE_REMOVE. -

DATASRC_CACHE_INSERT inserting item '%1' into the cache

-Debug information. It means a new item is being inserted into the hotspot +

DATASRC_CACHE_INSERT inserting item '%1' into the hotspot cache

+A debug message indicating that a new item is being inserted into the hotspot cache. -

DATASRC_CACHE_NOT_FOUND the item '%1' was not found

-Debug information. It was attempted to look up an item in the hotspot cache, -but it is not there. -

DATASRC_CACHE_OLD_FOUND older instance of cache item found, replacing

+

DATASRC_CACHE_NOT_FOUND the item '%1' was not found in the hotspot cache

+A debug message issued when hotspot cache was searched for the specified +item but it was not found. +

DATASRC_CACHE_OLD_FOUND older instance of hotspot cache item '%1' found, replacing

Debug information. While inserting an item into the hotspot cache, an older -instance of an item with the same name was found. The old instance will be -removed. This should be directly followed by CACHE_REMOVE. -

DATASRC_CACHE_REMOVE removing '%1' from the cache

+instance of an item with the same name was found; the old instance will be +removed. This will be directly followed by CACHE_REMOVE. +

DATASRC_CACHE_REMOVE removing '%1' from the hotspot cache

Debug information. An item is being removed from the hotspot cache. -

DATASRC_CACHE_SLOTS setting the cache size to '%1', dropping '%2' items

+

DATASRC_CACHE_SLOTS setting the hotspot cache size to '%1', dropping '%2' items

The maximum allowed number of items of the hotspot cache is set to the given number. If there are too many, some of them will be dropped. The size of 0 means no limit. +

DATASRC_DATABASE_FIND_ERROR error retrieving data from datasource %1: %2

+This was an internal error while reading data from a datasource. This can either +mean the specific data source implementation is not behaving correctly, or the +data it provides is invalid. The current search is aborted. +The error message contains specific information about the error. +

DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3

+Debug information. The database data source is looking up records with the given +name and type in the database. +

DATASRC_DATABASE_FIND_TTL_MISMATCH TTL values differ in %1 for elements of %2/%3/%4, setting to %5

+The datasource backend provided resource records for the given RRset with +different TTL values. The TTL of the RRSET is set to the lowest value, which +is printed in the log message. +

DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from datasource %1: %2

+There was an uncaught general exception while reading data from a datasource. +This most likely points to a logic error in the code, and can be considered a +bug. The current search is aborted. Specific information about the exception is +printed in this error message. +

DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR uncaught error retrieving data from datasource %1: %2

+There was an uncaught ISC exception while reading data from a datasource. This +most likely points to a logic error in the code, and can be considered a bug. +The current search is aborted. Specific information about the exception is +printed in this error message. +

DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %2 in %1

+When searching for a domain, the program met a delegation to a different zone +at the given domain name. It will return that one instead. +

DATASRC_DATABASE_FOUND_DELEGATION_EXACT Found delegation at %2 (exact match) in %1

+The program found the domain requested, but it is a delegation point to a +different zone, therefore it is not authoritative for this domain name. +It will return the NS record instead. +

DATASRC_DATABASE_FOUND_DNAME Found DNAME at %2 in %1

+When searching for a domain, the program met a DNAME redirection to a different +place in the domain space at the given domain name. It will return that one +instead. +

DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4

+The data returned by the database backend did not contain any data for the given +domain name, class and type. +

DATASRC_DATABASE_FOUND_NXRRSET search in datasource %1 resulted in NXRRSET for %2/%3/%4

+The data returned by the database backend contained data for the given domain +name and class, but not for the given type. +

DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %2

+The data returned by the database backend contained data for the given domain +name, and it either matches the type or has a relevant type. The RRset that is +returned is printed.

DATASRC_DO_QUERY handling query for '%1/%2'

-Debug information. We're processing some internal query for given name and -type. +A debug message indicating that a query for the given name and RR type is being +processed.

DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'

Debug information. An RRset is being added to the in-memory data source.

DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1'

-Debug information. Some special marks above each * in wildcard name are needed. -They are being added now for this name. +This is a debug message issued during the processing of a wildcard +name. The internal domain name tree is scanned and some nodes are +specially marked to allow the wildcard lookup to succeed.

DATASRC_MEM_ADD_ZONE adding zone '%1/%2'

Debug information. A zone is being added into the in-memory data source.

DATASRC_MEM_ANY_SUCCESS ANY query for '%1' successful

@@ -467,9 +837,9 @@ stop the search.

DATASRC_MEM_DNAME_FOUND DNAME found at '%1'

Debug information. A DNAME was found instead of the requested information.

DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1'

-It was requested for DNAME and NS records to be put into the same domain -which is not the apex (the top of the zone). This is forbidden by RFC -2672, section 3. This indicates a problem with provided data. +A request was made for DNAME and NS records to be put into the same +domain which is not the apex (the top of the zone). This is forbidden +by RFC 2672 (section 3) and indicates a problem with provided data.

DATASRC_MEM_DOMAIN_EMPTY requested domain '%1' is empty

Debug information. The requested domain exists in the tree of domains, but it is empty. Therefore it doesn't contain the requested resource type. @@ -488,7 +858,7 @@ Debug information. A zone object for this zone is being searched for in the in-memory data source.

DATASRC_MEM_LOAD loading zone '%1' from file '%2'

Debug information. The content of master file is being loaded into the memory. -

DATASRC_MEM_NOTFOUND requested domain '%1' not found

+

DATASRC_MEM_NOT_FOUND requested domain '%1' not found

Debug information. The requested domain does not exist.

DATASRC_MEM_NS_ENCOUNTERED encountered a NS

Debug information. While searching for the requested domain, a NS was @@ -535,10 +905,10 @@ explicitly forbidden, but the protocol is ambiguous about how this should behave and BIND 9 refuses that as well. Please describe your intention using different tools.

DATASRC_META_ADD adding a data source into meta data source

-Debug information. Yet another data source is being added into the meta data -source. (probably at startup or reconfiguration) +This is a debug message issued during startup or reconfiguration. +Another data source is being added into the meta data source.

DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2'

-It was attempted to add a data source into a meta data source. But their +It was attempted to add a data source into a meta data source, but their classes do not match.

DATASRC_META_REMOVE removing data source from meta data source

Debug information. A data source is being removed from meta data source. @@ -559,10 +929,10 @@ specific error already.

DATASRC_QUERY_BAD_REFERRAL bad referral to '%1'

The domain lives in another zone. But it is not possible to generate referral information for it. -

DATASRC_QUERY_CACHED data for %1/%2 found in cache

+

DATASRC_QUERY_CACHED data for %1/%2 found in hotspot cache

Debug information. The requested data were found in the hotspot cache, so no query is sent to the real data source. -

DATASRC_QUERY_CHECK_CACHE checking cache for '%1/%2'

+

DATASRC_QUERY_CHECK_CACHE checking hotspot cache for '%1/%2'

Debug information. While processing a query, lookup to the hotspot cache is being made.

DATASRC_QUERY_COPY_AUTH copying authoritative section into message

@@ -572,19 +942,18 @@ response message. Debug information. The software is trying to identify delegation points on the way down to the given domain.

DATASRC_QUERY_EMPTY_CNAME CNAME at '%1' is empty

-There was an CNAME and it was being followed. But it contains no records, -so there's nowhere to go. There will be no answer. This indicates a problem -with supplied data. -We tried to follow +A CNAME chain was being followed and an entry was found that pointed +to a domain name that had no RRsets associated with it. As a result, +the query cannot be answered. This indicates a problem with supplied data.

DATASRC_QUERY_EMPTY_DNAME the DNAME on '%1' is empty

During an attempt to synthesize CNAME from this DNAME it was discovered the DNAME is empty (it has no records). This indicates problem with supplied data.

DATASRC_QUERY_FAIL query failed

Some subtask of query processing failed. The reason should have been reported -already. We are returning SERVFAIL. +already and a SERVFAIL will be returned to the querying system.

DATASRC_QUERY_FOLLOW_CNAME following CNAME at '%1'

-Debug information. The domain is a CNAME (or a DNAME and we created a CNAME -for it already), so it's being followed. +Debug information. The domain is a CNAME (or a DNAME and a CNAME for it +has already been created) and the search is following this chain.

DATASRC_QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2'

Debug information. While processing a query, a MX record was met. It references the mentioned address, so A/AAAA records for it are looked up @@ -603,12 +972,12 @@ operation code.

DATASRC_QUERY_IS_AUTH auth query (%1/%2)

Debug information. The last DO_QUERY is an auth query.

DATASRC_QUERY_IS_GLUE glue query (%1/%2)

-Debug information. The last DO_QUERY is query for glue addresses. +Debug information. The last DO_QUERY is a query for glue addresses.

DATASRC_QUERY_IS_NOGLUE query for non-glue addresses (%1/%2)

-Debug information. The last DO_QUERY is query for addresses that are not +Debug information. The last DO_QUERY is a query for addresses that are not glue.

DATASRC_QUERY_IS_REF query for referral (%1/%2)

-Debug information. The last DO_QUERY is query for referral information. +Debug information. The last DO_QUERY is a query for referral information.

DATASRC_QUERY_IS_SIMPLE simple query (%1/%2)

Debug information. The last DO_QUERY is a simple query.

DATASRC_QUERY_MISPLACED_TASK task of this type should not be here

@@ -626,10 +995,10 @@ does not have one. This indicates problem with provided data. The underlying data source failed to answer the no-glue query. 1 means some error, 2 is not implemented. The data source should have logged the specific error already. -

DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class)

+

DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring hotspot cache for ANY query (%1/%2 in %3 class)

Debug information. The hotspot cache is ignored for authoritative ANY queries for consistency reasons. -

DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class)

+

DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring hotspot cache for ANY query (%1/%2 in %3 class)

Debug information. The hotspot cache is ignored for ANY queries for consistency reasons.

DATASRC_QUERY_NO_DS_NSEC there's no DS record in the '%1' zone

@@ -643,7 +1012,7 @@ Lookup of domain failed because the data have no zone that contain the domain. Maybe someone sent a query to the wrong server for some reason.

DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class

Debug information. A sure query is being processed now. -

DATASRC_QUERY_PROVENX_FAIL unable to prove nonexistence of '%1'

+

DATASRC_QUERY_PROVE_NX_FAIL unable to prove nonexistence of '%1'

The user wants DNSSEC and we discovered the entity doesn't exist (either domain or the record). But there was an error getting NSEC/NSEC3 record to prove the nonexistence. @@ -659,9 +1028,9 @@ The underlying data source failed to answer the simple query. 1 means some error, 2 is not implemented. The data source should have logged the specific error already.

DATASRC_QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1'

-Debug information. While answering a query, a DNAME was met. The DNAME itself -will be returned, but along with it a CNAME for clients which don't understand -DNAMEs will be synthesized. +This is a debug message. While answering a query, a DNAME was encountered. The +DNAME itself will be returned, along with a synthesized CNAME for clients that +do not understand the DNAME RR.

DATASRC_QUERY_TASK_FAIL task failed with %1

The query subtask failed. The reason should have been reported by the subtask already. The code is 1 for error, 2 for not implemented. @@ -679,7 +1048,7 @@ domain is being looked for now.

DATASRC_QUERY_WILDCARD_FAIL error processing wildcard for '%1'

During an attempt to cover the domain by a wildcard an error happened. The exact kind was hopefully already reported. -

DATASRC_QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2)

+

DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL unable to prove nonexistence of '%1' (%2)

While processing a wildcard, it wasn't possible to prove nonexistence of the given domain or record. The code is 1 for error and 2 for not implemented.

DATASRC_QUERY_WILDCARD_REFERRAL unable to find referral info for '%1' (%2)

@@ -687,14 +1056,20 @@ While processing a wildcard, a referral was met. But it wasn't possible to get enough information for it. The code is 1 for error, 2 for not implemented.

DATASRC_SQLITE_CLOSE closing SQLite database

Debug information. The SQLite data source is closing the database file. +

DATASRC_SQLITE_CONNCLOSE Closing sqlite database

+The database file is no longer needed and is being closed. +

DATASRC_SQLITE_CONNOPEN Opening sqlite database file '%1'

+The database file is being opened so it can start providing data.

DATASRC_SQLITE_CREATE SQLite data source created

Debug information. An instance of SQLite data source is being created.

DATASRC_SQLITE_DESTROY SQLite data source destroyed

Debug information. An instance of SQLite data source is being destroyed. +

DATASRC_SQLITE_DROPCONN SQLite3Database is being deinitialized

+The object around a database connection is being destroyed.

DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1'

Debug information. The SQLite data source is trying to identify which zone should hold this domain. -

DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it

+

DATASRC_SQLITE_ENCLOSURE_NOT_FOUND no zone contains '%1'

Debug information. The last SQLITE_ENCLOSURE query was unsuccessful; there's no such zone in our data.

DATASRC_SQLITE_FIND looking for RRset '%1/%2'

@@ -730,21 +1105,30 @@ source.

DATASRC_SQLITE_FIND_NSEC3_NO_ZONE no such zone '%1'

The SQLite data source was asked to provide a NSEC3 record for given zone. But it doesn't contain that zone. +

DATASRC_SQLITE_NEWCONN SQLite3Database is being initialized

+A wrapper object to hold database connection is being initialized.

DATASRC_SQLITE_OPEN opening SQLite database '%1'

Debug information. The SQLite data source is loading an SQLite database in the provided file.

DATASRC_SQLITE_PREVIOUS looking for name previous to '%1'

-Debug information. We're trying to look up name preceding the supplied one. +This is a debug message. The name given was not found, so the program +is searching for the next name higher up the hierarchy (e.g. if +www.example.com were queried for and not found, the software searches +for the "previous" name, example.com).

DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1'

-The SQLite data source tried to identify name preceding this one. But this -one is not contained in any zone in the data source. +The name given was not found, so the program is searching for the next +name higher up the hierarchy (e.g. if www.example.com were queried +for and not found, the software searches for the "previous" name, +example.com). However, this name is not contained in any zone in the +data source. This is an error since it indicates a problem in the earlier +processing of the query.

DATASRC_SQLITE_SETUP setting up SQLite database

The database for SQLite data source was found empty. It is assumed this is the first run and it is being initialized with current schema. It'll still contain no data, but it will be ready for use. -

DATASRC_STATIC_BAD_CLASS static data source can handle CH only

-For some reason, someone asked the static data source a query that is not in -the CH class. +

DATASRC_STATIC_CLASS_NOT_CH static data source can handle CH class only

+An error message indicating that a query requesting a RR for a class other +that CH was sent to the static data source (which only handles CH queries).

DATASRC_STATIC_CREATE creating the static datasource

Debug information. The static data source (the one holding stuff like version.bind) is being created. @@ -777,20 +1161,20 @@ A logger destination value was given that was not recognized. The destination should be one of "console", "file", or "syslog".

LOG_BAD_SEVERITY unrecognized log severity: %1

A logger severity value was given that was not recognized. The severity -should be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL". +should be one of "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE".

LOG_BAD_STREAM bad log console output stream: %1

-A log console output stream was given that was not recognized. The output -stream should be one of "stdout", or "stderr" +Logging has been configured so that output is written to the terminal +(console) but the stream on which it is to be written is not recognised. +Allowed values are "stdout" and "stderr".

LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code

-During start-up, BIND10 detected that the given message identification had -been defined multiple times in the BIND10 code. -

-This has no ill-effects other than the possibility that an erronous -message may be logged. However, as it is indicative of a programming -error, please log a bug report. +During start-up, BIND 10 detected that the given message identification +had been defined multiple times in the BIND 10 code. This indicates a +programming error; please submit a bug report.

LOG_DUPLICATE_NAMESPACE line %1: duplicate $NAMESPACE directive found

When reading a message file, more than one $NAMESPACE directive was found. -Such a condition is regarded as an error and the read will be abandoned. +(This directive is used to set a C++ namespace when generating header +files during software development.) Such a condition is regarded as an +error and the read will be abandoned.

LOG_INPUT_OPEN_FAIL unable to open message file %1 for input: %2

The program was not able to open the specified input message file for the reason given. @@ -837,10 +1221,10 @@ There may be several reasons why this message may appear: - The program outputting the message may not use that particular message (e.g. it originates in a module not used by the program.)

-- The local file was written for an earlier version of the BIND10 software +- The local file was written for an earlier version of the BIND 10 software and the later version no longer generates that message.

-Whatever the reason, there is no impact on the operation of BIND10. +Whatever the reason, there is no impact on the operation of BIND 10.

LOG_OPEN_OUTPUT_FAIL unable to open %1 for output: %2

Originating within the logging code, the program was not able to open the specified output file for the reason given. @@ -851,19 +1235,19 @@ This error is generated when the compiler finds a $PREFIX directive with more than one argument.

Note: the $PREFIX directive is deprecated and will be removed in a future -version of BIND10. +version of BIND 10.

LOG_PREFIX_INVALID_ARG line %1: $PREFIX directive has an invalid argument ('%2')

Within a message file, the $PREFIX directive takes a single argument, a prefix to be added to the symbol names when a C++ file is created. As such, it must adhere to restrictions on C++ symbol names (e.g. may only contain alphanumeric characters or underscores, and may nor start with a digit). A $PREFIX directive was found with an argument (given -in the message) that violates those restictions. +in the message) that violates those restrictions.

Note: the $PREFIX directive is deprecated and will be removed in a future -version of BIND10. +version of BIND 10.

LOG_READING_LOCAL_FILE reading local message file %1

-This is an informational message output by BIND10 when it starts to read +This is an informational message output by BIND 10 when it starts to read a local message file. (A local message file may replace the text of one of more messages; the ID of the message will not be changed though.)

LOG_READ_ERROR error reading from message file %1: %2

@@ -875,6 +1259,62 @@ Within a message file, a line starting with a dollar symbol was found

LOG_WRITE_ERROR error writing to %1: %2

The specified error was encountered by the message compiler when writing to the named output file. +

NOTIFY_OUT_INVALID_ADDRESS invalid address %1#%2: %3

+The notify_out library tried to send a notify message to the given +address, but it appears to be an invalid address. The configuration +for secondary nameservers might contain a typographic error, or a +different BIND 10 module has forgotten to validate its data before +sending this module a notify command. As such, this should normally +not happen, and points to an oversight in a different module. +

NOTIFY_OUT_REPLY_BAD_OPCODE bad opcode in notify reply from %1#%2: %3

+The notify_out library sent a notify message to the nameserver at +the given address, but the response did not have the opcode set to +NOTIFY. The opcode in the response is printed. Since there was a +response, no more notifies will be sent to this server for this +notification event. +

NOTIFY_OUT_REPLY_BAD_QID bad QID in notify reply from %1#%2: got %3, should be %4

+The notify_out library sent a notify message to the nameserver at +the given address, but the query id in the response does not match +the one we sent. Since there was a response, no more notifies will +be sent to this server for this notification event. +

NOTIFY_OUT_REPLY_BAD_QUERY_NAME bad query name in notify reply from %1#%2: got %3, should be %4

+The notify_out library sent a notify message to the nameserver at +the given address, but the query name in the response does not match +the one we sent. Since there was a response, no more notifies will +be sent to this server for this notification event. +

NOTIFY_OUT_REPLY_QR_NOT_SET QR flags set to 0 in reply to notify from %1#%2

+The notify_out library sent a notify message to the namesever at the +given address, but the reply did not have the QR bit set to one. +Since there was a response, no more notifies will be sent to this +server for this notification event. +

NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION uncaught exception: %1

+There was an uncaught exception in the handling of a notify reply +message, either in the message parser, or while trying to extract data +from the parsed message. The error is printed, and notify_out will +treat the response as a bad message, but this does point to a +programming error, since all exceptions should have been caught +explicitly. Please file a bug report. Since there was a response, +no more notifies will be sent to this server for this notification +event. +

NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries (%3) exceeded

+The maximum number of retries for the notify target has been exceeded. +Either the address of the secondary nameserver is wrong, or it is not +responding. +

NOTIFY_OUT_SENDING_NOTIFY sending notify to %1#%2

+A notify message is sent to the secondary nameserver at the given +address. +

NOTIFY_OUT_SOCKET_ERROR socket error sending notify to %1#%2: %3

+There was a network error while trying to send a notify message to +the given address. The address might be unreachable. The socket +error is printed and should provide more information. +

NOTIFY_OUT_SOCKET_RECV_ERROR socket error reading notify reply from %1#%2: %3

+There was a network error while trying to read a notify reply +message from the given address. The socket error is printed and should +provide more information. +

NOTIFY_OUT_TIMEOUT retry notify to %1#%2

+The notify message to the given address (noted as address#port) has +timed out, and the message will be resent until the max retry limit +is reached.

NSAS_FIND_NS_ADDRESS asking resolver to obtain A and AAAA records for %1

A debug message issued when the NSAS (nameserver address store - part of the resolver) is making a callback into the resolver to retrieve the @@ -1008,18 +1448,18 @@ A debug message indicating that a RunningQuery's success callback has been called because a nameserver has been found, and that a query is being sent to the specified nameserver.

RESLIB_TEST_SERVER setting test server to %1(%2)

-This is an internal debugging message and is only generated in unit tests. -It indicates that all upstream queries from the resolver are being routed to -the specified server, regardless of the address of the nameserver to which -the query would normally be routed. As it should never be seen in normal -operation, it is a warning message instead of a debug message. +This is a warning message only generated in unit tests. It indicates +that all upstream queries from the resolver are being routed to the +specified server, regardless of the address of the nameserver to which +the query would normally be routed. If seen during normal operation, +please submit a bug report.

RESLIB_TEST_UPSTREAM sending upstream query for <%1> to test server at %2

This is a debug message and should only be seen in unit tests. A query for the specified <name, class, type> tuple is being sent to a test nameserver whose address is given in the message.

RESLIB_TIMEOUT query <%1> to %2 timed out

-A debug message indicating that the specified query has timed out and as -there are no retries left, an error will be reported. +A debug message indicating that the specified upstream query has timed out and +there are no retries left.

RESLIB_TIMEOUT_RETRY query <%1> to %2 timed out, re-trying (retries left: %3)

A debug message indicating that the specified query has timed out and that the resolver is repeating the query to the same nameserver. After this @@ -1033,137 +1473,160 @@ gives no cause for concern. A debug message indicating that a query for the specified <name, class, type> tuple is being sent to a nameserver whose address is given in the message.

RESOLVER_AXFR_TCP AXFR request received over TCP

-A debug message, the resolver received a NOTIFY message over TCP. The server -cannot process it and will return an error message to the sender with the -RCODE set to NOTIMP. +This is a debug message output when the resolver received a request for +an AXFR (full transfer of a zone) over TCP. Only authoritative servers +are able to handle AXFR requests, so the resolver will return an error +message to the sender with the RCODE set to NOTIMP.

RESOLVER_AXFR_UDP AXFR request received over UDP

-A debug message, the resolver received a NOTIFY message over UDP. The server -cannot process it (and in any case, an AXFR request should be sent over TCP) -and will return an error message to the sender with the RCODE set to FORMERR. +This is a debug message output when the resolver received a request for +an AXFR (full transfer of a zone) over UDP. Only authoritative servers +are able to handle AXFR requests (and in any case, an AXFR request should +be sent over TCP), so the resolver will return an error message to the +sender with the RCODE set to NOTIMP.

RESOLVER_CLIENT_TIME_SMALL client timeout of %1 is too small

-An error indicating that the configuration value specified for the query -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the client timeout was found to be too small. The configuration +update was abandoned and the parameters were not changed.

RESOLVER_CONFIG_CHANNEL configuration channel created

-A debug message, output when the resolver has successfully established a -connection to the configuration channel. +This is a debug message output when the resolver has successfully +established a connection to the configuration channel.

RESOLVER_CONFIG_ERROR error in configuration: %1

-An error was detected in a configuration update received by the resolver. This -may be in the format of the configuration message (in which case this is a -programming error) or it may be in the data supplied (in which case it is -a user error). The reason for the error, given as a parameter in the message, -will give more details. +An error was detected in a configuration update received by the +resolver. This may be in the format of the configuration message (in +which case this is a programming error) or it may be in the data supplied +(in which case it is a user error). The reason for the error, included +in the message, will give more details. The configuration update is +not applied and the resolver parameters were not changed.

RESOLVER_CONFIG_LOADED configuration loaded

-A debug message, output when the resolver configuration has been successfully -loaded. +This is a debug message output when the resolver configuration has been +successfully loaded.

RESOLVER_CONFIG_UPDATED configuration updated: %1

-A debug message, the configuration has been updated with the specified -information. +This is a debug message output when the resolver configuration is being +updated with the specified information.

RESOLVER_CREATED main resolver object created

-A debug message, output when the Resolver() object has been created. +This is a debug message indicating that the main resolver object has +been created.

RESOLVER_DNS_MESSAGE_RECEIVED DNS message received: %1

-A debug message, this always precedes some other logging message and is the -formatted contents of the DNS packet that the other message refers to. +This is a debug message from the resolver listing the contents of a +received DNS message.

RESOLVER_DNS_MESSAGE_SENT DNS message of %1 bytes sent: %2

-A debug message, this contains details of the response sent back to the querying -system. +This is a debug message containing details of the response returned by +the resolver to the querying system.

RESOLVER_FAILED resolver failed, reason: %1

-This is an error message output when an unhandled exception is caught by the -resolver. All it can do is to shut down. +This is an error message output when an unhandled exception is caught +by the resolver. After this, the resolver will shut itself down. +Please submit a bug report.

RESOLVER_FORWARD_ADDRESS setting forward address %1(%2)

-This message may appear multiple times during startup, and it lists the -forward addresses used by the resolver when running in forwarding mode. +If the resolver is running in forward mode, this message will appear +during startup to list the forward address. If multiple addresses are +specified, it will appear once for each address.

RESOLVER_FORWARD_QUERY processing forward query

-The received query has passed all checks and is being forwarded to upstream +This is a debug message indicating that a query received by the resolver +has passed a set of checks (message is well-formed, it is allowed by the +ACL, it is a supported opcode, etc.) and is being forwarded to upstream servers.

RESOLVER_HEADER_ERROR message received, exception when processing header: %1

-A debug message noting that an exception occurred during the processing of -a received packet. The packet has been dropped. +This is a debug message from the resolver noting that an exception +occurred during the processing of a received packet. The packet has +been dropped.

RESOLVER_IXFR IXFR request received

-The resolver received a NOTIFY message over TCP. The server cannot process it -and will return an error message to the sender with the RCODE set to NOTIMP. +This is a debug message indicating that the resolver received a request +for an IXFR (incremental transfer of a zone). Only authoritative servers +are able to handle IXFR requests, so the resolver will return an error +message to the sender with the RCODE set to NOTIMP.

RESOLVER_LOOKUP_TIME_SMALL lookup timeout of %1 is too small

-An error indicating that the configuration value specified for the lookup -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the lookup timeout was found to be too small. The configuration +update will not be applied.

RESOLVER_MESSAGE_ERROR error parsing received message: %1 - returning %2

-A debug message noting that the resolver received a message and the -parsing of the body of the message failed due to some error (although -the parsing of the header succeeded). The message parameters give a -textual description of the problem and the RCODE returned. +This is a debug message noting that parsing of the body of a received +message by the resolver failed due to some error (although the parsing of +the header succeeded). The message parameters give a textual description +of the problem and the RCODE returned.

RESOLVER_NEGATIVE_RETRIES negative number of retries (%1) specified in the configuration

-An error message indicating that the resolver configuration has specified a -negative retry count. Only zero or positive values are valid. +This error is issued when a resolver configuration update has specified +a negative retry count: only zero or positive values are valid. The +configuration update was abandoned and the parameters were not changed.

RESOLVER_NON_IN_PACKET non-IN class request received, returning REFUSED message

-A debug message, the resolver has received a DNS packet that was not IN class. -The resolver cannot handle such packets, so is returning a REFUSED response to -the sender. +This debug message is issued when resolver has received a DNS packet that +was not IN (Internet) class. The resolver cannot handle such packets, +so is returning a REFUSED response to the sender.

RESOLVER_NORMAL_QUERY processing normal query

-The received query has passed all checks and is being processed by the resolver. +This is a debug message indicating that the query received by the resolver +has passed a set of checks (message is well-formed, it is allowed by the +ACL, it is a supported opcode, etc.) and is being processed by the resolver.

RESOLVER_NOTIFY_RECEIVED NOTIFY arrived but server is not authoritative

-The resolver received a NOTIFY message. As the server is not authoritative it -cannot process it, so it returns an error message to the sender with the RCODE -set to NOTAUTH. +The resolver has received a NOTIFY message. As the server is not +authoritative it cannot process it, so it returns an error message to +the sender with the RCODE set to NOTAUTH.

RESOLVER_NOT_ONE_QUESTION query contained %1 questions, exactly one question was expected

-A debug message, the resolver received a query that contained the number of -entires in the question section detailed in the message. This is a malformed -message, as a DNS query must contain only one question. The resolver will -return a message to the sender with the RCODE set to FORMERR. +This debug message indicates that the resolver received a query that +contained the number of entries in the question section detailed in +the message. This is a malformed message, as a DNS query must contain +only one question. The resolver will return a message to the sender +with the RCODE set to FORMERR.

RESOLVER_NO_ROOT_ADDRESS no root addresses available

-A warning message during startup, indicates that no root addresses have been -set. This may be because the resolver will get them from a priming query. +A warning message issued during resolver startup, this indicates that +no root addresses have been set. This may be because the resolver will +get them from a priming query.

RESOLVER_PARSE_ERROR error parsing received message: %1 - returning %2

-A debug message noting that the resolver received a message and the parsing -of the body of the message failed due to some non-protocol related reason -(although the parsing of the header succeeded). The message parameters give -a textual description of the problem and the RCODE returned. +This is a debug message noting that the resolver received a message and +the parsing of the body of the message failed due to some non-protocol +related reason (although the parsing of the header succeeded). +The message parameters give a textual description of the problem and +the RCODE returned.

RESOLVER_PRINT_COMMAND print message command, arguments are: %1

-This message is logged when a "print_message" command is received over the -command channel. +This debug message is logged when a "print_message" command is received +by the resolver over the command channel.

RESOLVER_PROTOCOL_ERROR protocol error parsing received message: %1 - returning %2

-A debug message noting that the resolver received a message and the parsing -of the body of the message failed due to some protocol error (although the -parsing of the header succeeded). The message parameters give a textual -description of the problem and the RCODE returned. +This is a debug message noting that the resolver received a message and +the parsing of the body of the message failed due to some protocol error +(although the parsing of the header succeeded). The message parameters +give a textual description of the problem and the RCODE returned.

RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4

-A debug message that indicates an incoming query is accepted in terms of -the query ACL. The log message shows the query in the form of -<query name>/<query type>/<query class>, and the client that sends the -query in the form of <Source IP address>#<source port>. +This debug message is produced by the resolver when an incoming query +is accepted in terms of the query ACL. The log message shows the query +in the form of <query name>/<query type>/<query class>, and the client +that sends the query in the form of <Source IP address>#<source port>.

RESOLVER_QUERY_DROPPED query dropped: '%1/%2/%3' from %4

-An informational message that indicates an incoming query is dropped -in terms of the query ACL. Unlike the RESOLVER_QUERY_REJECTED -case, the server does not return any response. The log message -shows the query in the form of <query name>/<query type>/<query -class>, and the client that sends the query in the form of <Source -IP address>#<source port>. +This is an informational message that indicates an incoming query has +been dropped by the resolver because of the query ACL. Unlike the +RESOLVER_QUERY_REJECTED case, the server does not return any response. +The log message shows the query in the form of <query name>/<query +type>/<query class>, and the client that sends the query in the form of +<Source IP address>#<source port>.

RESOLVER_QUERY_REJECTED query rejected: '%1/%2/%3' from %4

-An informational message that indicates an incoming query is rejected -in terms of the query ACL. This results in a response with an RCODE of -REFUSED. The log message shows the query in the form of <query -name>/<query type>/<query class>, and the client that sends the -query in the form of <Source IP address>#<source port>. +This is an informational message that indicates an incoming query has +been rejected by the resolver because of the query ACL. This results +in a response with an RCODE of REFUSED. The log message shows the query +in the form of <query name>/<query type>/<query class>, and the client +that sends the query in the form of <Source IP address>#<source port>.

RESOLVER_QUERY_SETUP query setup

-A debug message noting that the resolver is creating a RecursiveQuery object. +This is a debug message noting that the resolver is creating a +RecursiveQuery object.

RESOLVER_QUERY_SHUTDOWN query shutdown

-A debug message noting that the resolver is destroying a RecursiveQuery object. +This is a debug message noting that the resolver is destroying a +RecursiveQuery object.

RESOLVER_QUERY_TIME_SMALL query timeout of %1 is too small

-An error indicating that the configuration value specified for the query -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the query timeout was found to be too small. The configuration +parameters were not changed.

RESOLVER_RECEIVED_MESSAGE resolver has received a DNS message

-A debug message indicating that the resolver has received a message. Depending -on the debug settings, subsequent log output will indicate the nature of the -message. +This is a debug message indicating that the resolver has received a +DNS message. Depending on the debug settings, subsequent log output +will indicate the nature of the message.

RESOLVER_RECURSIVE running in recursive mode

-This is an informational message that appears at startup noting that the -resolver is running in recursive mode. +This is an informational message that appears at startup noting that +the resolver is running in recursive mode.

RESOLVER_SERVICE_CREATED service object created

-A debug message, output when the main service object (which handles the -received queries) is created. +This debug message is output when resolver creates the main service object +(which handles the received queries).

RESOLVER_SET_PARAMS query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4

-A debug message, lists the parameters being set for the resolver. These are: +This debug message lists the parameters being set for the resolver. These are: query timeout: the timeout (in ms) used for queries originated by the resolver -to upstream servers. Client timeout: the interval to resolver a query by +to upstream servers. Client timeout: the interval to resolve a query by a client: after this time, the resolver sends back a SERVFAIL to the client -whilst continuing to resolver the query. Lookup timeout: the time at which the +whilst continuing to resolve the query. Lookup timeout: the time at which the resolver gives up trying to resolve a query. Retry count: the number of times the resolver will retry a query to an upstream server if it gets a timeout.

@@ -1172,30 +1635,181 @@ resolution of the client query might require a large number of queries to upstream nameservers. Even if none of these queries timeout, the total time taken to perform all the queries may exceed the client timeout. When this happens, a SERVFAIL is returned to the client, but the resolver continues -with the resolution process. Data received is added to the cache. However, +with the resolution process; data received is added to the cache. However, there comes a time - the lookup timeout - when even the resolver gives up. At this point it will wait for pending upstream queries to complete or timeout and drop the query.

RESOLVER_SET_QUERY_ACL query ACL is configured

-A debug message that appears when a new query ACL is configured for the -resolver. +This debug message is generated when a new query ACL is configured for +the resolver.

RESOLVER_SET_ROOT_ADDRESS setting root address %1(%2)

-This message may appear multiple times during startup; it lists the root -addresses used by the resolver. +This message gives the address of one of the root servers used by the +resolver. It is output during startup and may appear multiple times, +once for each root server address.

RESOLVER_SHUTDOWN resolver shutdown complete

-This information message is output when the resolver has shut down. +This informational message is output when the resolver has shut down.

RESOLVER_STARTED resolver started

This informational message is output by the resolver when all initialization has been completed and it is entering its main loop.

RESOLVER_STARTING starting resolver with command line '%1'

An informational message, this is output when the resolver starts up.

RESOLVER_UNEXPECTED_RESPONSE received unexpected response, ignoring

-A debug message noting that the server has received a response instead of a -query and is ignoring it. +This is a debug message noting that the resolver received a DNS response +packet on the port on which is it listening for queries. The packet +has been ignored.

RESOLVER_UNSUPPORTED_OPCODE opcode %1 not supported by the resolver

-A debug message, the resolver received a message with an unsupported opcode -(it can only process QUERY opcodes). It will return a message to the sender -with the RCODE set to NOTIMP. +This is debug message output when the resolver received a message with an +unsupported opcode (it can only process QUERY opcodes). It will return +a message to the sender with the RCODE set to NOTIMP. +

SRVCOMM_ADDRESSES_NOT_LIST the address and port specification is not a list in %1

+This points to an error in configuration. What was supposed to be a list of +IP address - port pairs isn't a list at all but something else. +

SRVCOMM_ADDRESS_FAIL failed to listen on addresses (%1)

+The server failed to bind to one of the address/port pair it should according +to configuration, for reason listed in the message (usually because that pair +is already used by other service or missing privileges). The server will try +to recover and bind the address/port pairs it was listening to before (if any). +

SRVCOMM_ADDRESS_MISSING address specification is missing "address" or "port" element in %1

+This points to an error in configuration. An address specification in the +configuration is missing either an address or port and so cannot be used. The +specification causing the error is given in the message. +

SRVCOMM_ADDRESS_TYPE address specification type is invalid in %1

+This points to an error in configuration. An address specification in the +configuration malformed. The specification causing the error is given in the +message. A valid specification contains an address part (which must be a string +and must represent a valid IPv4 or IPv6 address) and port (which must be an +integer in the range valid for TCP/UDP ports on your system). +

SRVCOMM_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2)

+The recovery of old addresses after SRVCOMM_ADDRESS_FAIL also failed for +the reason listed. +

+The condition indicates problems with the server and/or the system on +which it is running. The server will continue running to allow +reconfiguration, but will not be listening on any address or port until +an administrator does so. +

SRVCOMM_ADDRESS_VALUE address to set: %1#%2

+Debug message. This lists one address and port value of the set of +addresses we are going to listen on (eg. there will be one log message +per pair). This appears only after SRVCOMM_SET_LISTEN, but might +be hidden, as it has higher debug level. +

SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring

+Debug message indicating that the server is deinitializing the TSIG keyring. +

SRVCOMM_KEYS_INIT initializing TSIG keyring

+Debug message indicating that the server is initializing the global TSIG +keyring. This should be seen only at server start. +

SRVCOMM_KEYS_UPDATE updating TSIG keyring

+Debug message indicating new keyring is being loaded from configuration (either +on startup or as a result of configuration update). +

SRVCOMM_PORT_RANGE port out of valid range (%1 in %2)

+This points to an error in configuration. The port in an address +specification is outside the valid range of 0 to 65535. +

SRVCOMM_SET_LISTEN setting addresses to listen to

+Debug message, noting that the server is about to start listening on a +different set of IP addresses and ports than before. +

STATHTTPD_BAD_OPTION_VALUE bad command line argument: %1

+The stats-httpd module was called with a bad command-line argument +and will not start. +

STATHTTPD_CC_SESSION_ERROR error connecting to message bus: %1

+The stats-httpd module was unable to connect to the BIND 10 command +and control bus. A likely problem is that the message bus daemon +(b10-msgq) is not running. The stats-httpd module will now shut down. +

STATHTTPD_CLOSING closing %1#%2

+The stats-httpd daemon will stop listening for requests on the given +address and port number. +

STATHTTPD_CLOSING_CC_SESSION stopping cc session

+Debug message indicating that the stats-httpd module is disconnecting +from the command and control bus. +

STATHTTPD_HANDLE_CONFIG reading configuration: %1

+The stats-httpd daemon has received new configuration data and will now +process it. The (changed) data is printed. +

STATHTTPD_RECEIVED_SHUTDOWN_COMMAND shutdown command received

+A shutdown command was sent to the stats-httpd module, and it will +now shut down. +

STATHTTPD_RECEIVED_STATUS_COMMAND received command to return status

+A status command was sent to the stats-httpd module, and it will +respond with 'Stats Httpd is up.' and its PID. +

STATHTTPD_RECEIVED_UNKNOWN_COMMAND received unknown command: %1

+An unknown command has been sent to the stats-httpd module. The +stats-httpd module will respond with an error, and the command will +be ignored. +

STATHTTPD_SERVER_ERROR HTTP server error: %1

+An internal error occurred while handling an HTTP request. An HTTP 500 +response will be sent back, and the specific error is printed. This +is an error condition that likely points to a module that is not +responding correctly to statistic requests. +

STATHTTPD_SERVER_INIT_ERROR HTTP server initialization error: %1

+There was a problem initializing the HTTP server in the stats-httpd +module upon receiving its configuration data. The most likely cause +is a port binding problem or a bad configuration value. The specific +error is printed in the message. The new configuration is ignored, +and an error is sent back. +

STATHTTPD_SHUTDOWN shutting down

+The stats-httpd daemon is shutting down. +

STATHTTPD_STARTED listening on %1#%2

+The stats-httpd daemon will now start listening for requests on the +given address and port number. +

STATHTTPD_STARTING_CC_SESSION starting cc session

+Debug message indicating that the stats-httpd module is connecting to +the command and control bus. +

STATHTTPD_START_SERVER_INIT_ERROR HTTP server initialization error: %1

+There was a problem initializing the HTTP server in the stats-httpd +module upon startup. The most likely cause is that it was not able +to bind to the listening port. The specific error is printed, and the +module will shut down. +

STATHTTPD_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down

+There was a keyboard interrupt signal to stop the stats-httpd +daemon. The daemon will now shut down. +

STATHTTPD_UNKNOWN_CONFIG_ITEM unknown configuration item: %1

+The stats-httpd daemon received a configuration update from the +configuration manager. However, one of the items in the +configuration is unknown. The new configuration is ignored, and an +error is sent back. As possible cause is that there was an upgrade +problem, and the stats-httpd version is out of sync with the rest of +the system. +

STATS_BAD_OPTION_VALUE bad command line argument: %1

+The stats module was called with a bad command-line argument and will +not start. +

STATS_CC_SESSION_ERROR error connecting to message bus: %1

+The stats module was unable to connect to the BIND 10 command and +control bus. A likely problem is that the message bus daemon +(b10-msgq) is not running. The stats module will now shut down. +

STATS_RECEIVED_NEW_CONFIG received new configuration: %1

+This debug message is printed when the stats module has received a +configuration update from the configuration manager. +

STATS_RECEIVED_REMOVE_COMMAND received command to remove %1

+A remove command for the given name was sent to the stats module, and +the given statistics value will now be removed. It will not appear in +statistics reports until it appears in a statistics update from a +module again. +

STATS_RECEIVED_RESET_COMMAND received command to reset all statistics

+The stats module received a command to clear all collected statistics. +The data is cleared until it receives an update from the modules again. +

STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics

+The stats module received a command to show all statistics that it has +collected. +

STATS_RECEIVED_SHOW_NAME_COMMAND received command to show statistics for %1

+The stats module received a command to show the statistics that it has +collected for the given item. +

STATS_RECEIVED_SHUTDOWN_COMMAND shutdown command received

+A shutdown command was sent to the stats module and it will now shut down. +

STATS_RECEIVED_STATUS_COMMAND received command to return status

+A status command was sent to the stats module. It will return a +response indicating that it is running normally. +

STATS_RECEIVED_UNKNOWN_COMMAND received unknown command: %1

+An unknown command has been sent to the stats module. The stats module +will respond with an error and the command will be ignored. +

STATS_SEND_REQUEST_BOSS requesting boss to send statistics

+This debug message is printed when a request is sent to the boss module +to send its data to the stats module. +

STATS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down

+There was a keyboard interrupt signal to stop the stats module. The +daemon will now shut down. +

STATS_UNKNOWN_COMMAND_IN_SPEC unknown command in specification file: %1

+The specification file for the stats module contains a command that +is unknown in the implementation. The most likely cause is an +installation problem, where the specification file stats.spec is +from a different version of BIND 10 than the stats module itself. +Please check your installation.

XFRIN_AXFR_DATABASE_FAILURE AXFR transfer of zone %1 failed: %2

The AXFR transfer for the given zone has failed due to a database problem. The error is shown in the log message. @@ -1277,7 +1891,7 @@ a valid TSIG key. There was a problem reading from the command and control channel. The most likely cause is that the msgq daemon is not running.

XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response

-There was a problem reading a response from antoher module over the +There was a problem reading a response from another module over the command and control channel. The most likely cause is that the configuration manager b10-cfgmgr is not running.

XFROUT_FETCH_REQUEST_ERROR socket error while fetching a request from the auth daemon

@@ -1315,6 +1929,15 @@ There was an error processing a transfer request. The error is included in the log message, but at this point no specific information other than that could be given. This points to incomplete exception handling in the code. +

XFROUT_QUERY_DROPPED request to transfer %1/%2 to [%3]:%4 dropped

+The xfrout process silently dropped a request to transfer zone to given host. +This is required by the ACLs. The %1 and %2 represent the zone name and class, +the %3 and %4 the IP address and port of the peer requesting the transfer. +

XFROUT_QUERY_REJECTED request to transfer %1/%2 to [%3]:%4 rejected

+The xfrout process rejected (by REFUSED rcode) a request to transfer zone to +given host. This is because of ACLs. The %1 and %2 represent the zone name and +class, the %3 and %4 the IP address and port of the peer requesting the +transfer.

XFROUT_RECEIVED_SHUTDOWN_COMMAND shutdown command received

The xfrout daemon received a shutdown command from the command channel and will now shut down. @@ -1350,5 +1973,109 @@ socket needed for contacting the b10-auth daemon to pass requests on, but the file is in use. The most likely cause is that another xfrout daemon process is still running. This xfrout daemon (the one printing this message) will not start. +

ZONEMGR_CCSESSION_ERROR command channel session error: %1

+An error was encountered on the command channel. The message indicates +the nature of the error. +

ZONEMGR_JITTER_TOO_BIG refresh_jitter is too big, setting to 0.5

+The value specified in the configuration for the refresh jitter is too large +so its value has been set to the maximum of 0.5. +

ZONEMGR_KEYBOARD_INTERRUPT exiting zonemgr process as result of keyboard interrupt

+An informational message output when the zone manager was being run at a +terminal and it was terminated via a keyboard interrupt signal. +

ZONEMGR_LOAD_ZONE loading zone %1 (class %2)

+This is a debug message indicating that the zone of the specified class +is being loaded. +

ZONEMGR_NO_MASTER_ADDRESS internal BIND 10 command did not contain address of master

+A command received by the zone manager from the Auth module did not +contain the address of the master server from which a NOTIFY message +was received. This may be due to an internal programming error; please +submit a bug report. +

ZONEMGR_NO_SOA zone %1 (class %2) does not have an SOA record

+When loading the named zone of the specified class the zone manager +discovered that the data did not contain an SOA record. The load has +been abandoned. +

ZONEMGR_NO_TIMER_THREAD trying to stop zone timer thread but it is not running

+An attempt was made to stop the timer thread (used to track when zones +should be refreshed) but it was not running. This may indicate an +internal program error. Please submit a bug report. +

ZONEMGR_NO_ZONE_CLASS internal BIND 10 command did not contain class of zone

+A command received by the zone manager from another BIND 10 module did +not contain the class of the zone on which the zone manager should act. +This may be due to an internal programming error; please submit a +bug report. +

ZONEMGR_NO_ZONE_NAME internal BIND 10 command did not contain name of zone

+A command received by the zone manager from another BIND 10 module did +not contain the name of the zone on which the zone manager should act. +This may be due to an internal programming error; please submit a +bug report. +

ZONEMGR_RECEIVE_NOTIFY received NOTIFY command for zone %1 (class %2)

+This is a debug message indicating that the zone manager has received a +NOTIFY command over the command channel. The command is sent by the Auth +process when it is acting as a slave server for the zone and causes the +zone manager to record the master server for the zone and start a timer; +when the timer expires, the master will be polled to see if it contains +new data. +

ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command

+This is a debug message indicating that the zone manager has received +a SHUTDOWN command over the command channel from the Boss process. +It will act on this command and shut down. +

ZONEMGR_RECEIVE_UNKNOWN received unknown command '%1'

+This is a warning message indicating that the zone manager has received +the stated command over the command channel. The command is not known +to the zone manager and although the command is ignored, its receipt +may indicate an internal error. Please submit a bug report. +

ZONEMGR_RECEIVE_XFRIN_FAILED received XFRIN FAILED command for zone %1 (class %2)

+This is a debug message indicating that the zone manager has received +an XFRIN FAILED command over the command channel. The command is sent +by the Xfrin process when a transfer of zone data into the system has +failed, and causes the zone manager to schedule another transfer attempt. +

ZONEMGR_RECEIVE_XFRIN_SUCCESS received XFRIN SUCCESS command for zone %1 (class %2)

+This is a debug message indicating that the zone manager has received +an XFRIN SUCCESS command over the command channel. The command is sent +by the Xfrin process when the transfer of zone data into the system has +succeeded, and causes the data to be loaded and served by BIND 10. +

ZONEMGR_REFRESH_ZONE refreshing zone %1 (class %2)

+The zone manager is refreshing the named zone of the specified class +with updated information. +

ZONEMGR_SELECT_ERROR error with select(): %1

+An attempt to wait for input from a socket failed. The failing operation +is a call to the operating system's select() function, which failed for +the given reason. +

ZONEMGR_SEND_FAIL failed to send command to %1, session has been closed

+The zone manager attempted to send a command to the named BIND 10 module, +but the send failed. The session between the modules has been closed. +

ZONEMGR_SESSION_ERROR unable to establish session to command channel daemon

+The zonemgr process was not able to be started because it could not +connect to the command channel daemon. The most usual cause of this +problem is that the daemon is not running. +

ZONEMGR_SESSION_TIMEOUT timeout on session to command channel daemon

+The zonemgr process was not able to be started because it timed out when +connecting to the command channel daemon. The most usual cause of this +problem is that the daemon is not running. +

ZONEMGR_SHUTDOWN zone manager has shut down

+A debug message, output when the zone manager has shut down completely. +

ZONEMGR_STARTING zone manager starting

+A debug message output when the zone manager starts up. +

ZONEMGR_TIMER_THREAD_RUNNING trying to start timer thread but one is already running

+This message is issued when an attempt is made to start the timer +thread (which keeps track of when zones need a refresh) but one is +already running. It indicates either an error in the program logic or +a problem with stopping a previous instance of the timer. Please submit +a bug report. +

ZONEMGR_UNKNOWN_ZONE_FAIL zone %1 (class %2) is not known to the zone manager

+An XFRIN operation has failed but the zone that was the subject of the +operation is not being managed by the zone manager. This may indicate +an error in the program (as the operation should not have been initiated +if this were the case). Please submit a bug report. +

ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1 (class %2) is not known to the zone manager

+A NOTIFY was received but the zone that was the subject of the operation +is not being managed by the zone manager. This may indicate an error +in the program (as the operation should not have been initiated if this +were the case). Please submit a bug report. +

ZONEMGR_UNKNOWN_ZONE_SUCCESS zone %1 (class %2) is not known to the zone manager

+An XFRIN operation has succeeded but the zone received is not being +managed by the zone manager. This may indicate an error in the program +(as the operation should not have been initiated if this were the case). +Please submit a bug report.

diff --git a/doc/guide/bind10-messages.xml b/doc/guide/bind10-messages.xml index d146a9ca56..f5c44b33d8 100644 --- a/doc/guide/bind10-messages.xml +++ b/doc/guide/bind10-messages.xml @@ -90,7 +90,7 @@ enabled. The asynchronous I/O code encountered an error when trying to open a socket of the specified protocol in order to send a message to the target address. -The number of the system error that cause the problem is given in the +The number of the system error that caused the problem is given in the message. @@ -100,7 +100,7 @@ message. The asynchronous I/O code encountered an error when trying to read data from the specified address on the given protocol. The number of the system -error that cause the problem is given in the message. +error that caused the problem is given in the message. @@ -117,9 +117,9 @@ enabled. ASIODNS_SEND_DATA error %1 sending data using %2 to %3(%4) -The asynchronous I/O code encountered an error when trying send data to -the specified address on the given protocol. The the number of the system -error that cause the problem is given in the message. +The asynchronous I/O code encountered an error when trying to send data to +the specified address on the given protocol. The number of the system +error that caused the problem is given in the message. @@ -228,7 +228,7 @@ datebase data source, listing the file that is being accessed. AUTH_DNS_SERVICES_CREATED DNS services created This is a debug message indicating that the component that will handling -incoming queries for the authoritiative server (DNSServices) has been +incoming queries for the authoritative server (DNSServices) has been successfully created. It is issued during server startup is an indication that the initialization is proceeding normally. @@ -247,7 +247,7 @@ packet. AUTH_LOAD_TSIG loading TSIG keys -This is a debug message indicating that the authoritiative server +This is a debug message indicating that the authoritative server has requested the keyring holding TSIG keys from the configuration database. It is issued during server startup is an indication that the initialization is proceeding normally. @@ -358,8 +358,8 @@ encountered an internal error whilst processing a received packet: the cause of the error is included in the message. The server will return a SERVFAIL error code to the sender of the packet. -However, this message indicates a potential error in the server. -Please open a bug ticket for this issue. +This message indicates a potential error in the server. Please open a +bug ticket for this issue. @@ -474,7 +474,7 @@ initialization is proceeding normally. AUTH_STATS_COMMS communication error in sending statistics data: %1 -An error was encountered when the authoritiative server tried to send data +An error was encountered when the authoritative server tried to send data to the statistics daemon. The message includes additional information describing the reason for the failure. @@ -556,6 +556,631 @@ NOTIFY request will not be honored. + +BIND10_CHECK_MSGQ_ALREADY_RUNNING checking if msgq is already running + +The boss process is starting up and will now check if the message bus +daemon is already running. If so, it will not be able to start, as it +needs a dedicated message bus. + + + + +BIND10_CONFIGURATION_START_AUTH start authoritative server: %1 + +This message shows whether or not the authoritative server should be +started according to the configuration. + + + + +BIND10_CONFIGURATION_START_RESOLVER start resolver: %1 + +This message shows whether or not the resolver should be +started according to the configuration. + + + + +BIND10_INVALID_USER invalid user: %1 + +The boss process was started with the -u option, to drop root privileges +and continue running as the specified user, but the user is unknown. + + + + +BIND10_KILLING_ALL_PROCESSES killing all started processes + +The boss module was not able to start every process it needed to start +during startup, and will now kill the processes that did get started. + + + + +BIND10_KILL_PROCESS killing process %1 + +The boss module is sending a kill signal to process with the given name, +as part of the process of killing all started processes during a failed +startup, as described for BIND10_KILLING_ALL_PROCESSES + + + + +BIND10_MSGQ_ALREADY_RUNNING msgq daemon already running, cannot start + +There already appears to be a message bus daemon running. Either an +old process was not shut down correctly, and needs to be killed, or +another instance of BIND10, with the same msgq domain socket, is +running, which needs to be stopped. + + + + +BIND10_MSGQ_DAEMON_ENDED b10-msgq process died, shutting down + +The message bus daemon has died. This is a fatal error, since it may +leave the system in an inconsistent state. BIND10 will now shut down. + + + + +BIND10_MSGQ_DISAPPEARED msgq channel disappeared + +While listening on the message bus channel for messages, it suddenly +disappeared. The msgq daemon may have died. This might lead to an +inconsistent state of the system, and BIND 10 will now shut down. + + + + +BIND10_PROCESS_ENDED_NO_EXIT_STATUS process %1 (PID %2) died: exit status not available + +The given process ended unexpectedly, but no exit status is +available. See BIND10_PROCESS_ENDED_WITH_EXIT_STATUS for a longer +description. + + + + +BIND10_PROCESS_ENDED_WITH_EXIT_STATUS process %1 (PID %2) terminated, exit status = %3 + +The given process ended unexpectedly with the given exit status. +Depending on which module it was, it may simply be restarted, or it +may be a problem that will cause the boss module to shut down too. +The latter happens if it was the message bus daemon, which, if it has +died suddenly, may leave the system in an inconsistent state. BIND10 +will also shut down now if it has been run with --brittle. + + + + +BIND10_READING_BOSS_CONFIGURATION reading boss configuration + +The boss process is starting up, and will now process the initial +configuration, as received from the configuration manager. + + + + +BIND10_RECEIVED_COMMAND received command: %1 + +The boss module received a command and shall now process it. The command +is printed. + + + + +BIND10_RECEIVED_NEW_CONFIGURATION received new configuration: %1 + +The boss module received a configuration update and is going to apply +it now. The new configuration is printed. + + + + +BIND10_RECEIVED_SIGNAL received signal %1 + +The boss module received the given signal. + + + + +BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2) + +The given process has been restarted successfully, and is now running +with the given process id. + + + + +BIND10_RESURRECTING_PROCESS resurrecting dead %1 process... + +The given process has ended unexpectedly, and is now restarted. + + + + +BIND10_SELECT_ERROR error in select() call: %1 + +There was a fatal error in the call to select(), used to see if a child +process has ended or if there is a message on the message bus. This +should not happen under normal circumstances and is considered fatal, +so BIND 10 will now shut down. The specific error is printed. + + + + +BIND10_SEND_SIGKILL sending SIGKILL to %1 (PID %2) + +The boss module is sending a SIGKILL signal to the given process. + + + + +BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2) + +The boss module is sending a SIGTERM signal to the given process. + + + + +BIND10_SHUTDOWN stopping the server + +The boss process received a command or signal telling it to shut down. +It will send a shutdown command to each process. The processes that do +not shut down will then receive a SIGTERM signal. If that doesn't work, +it shall send SIGKILL signals to the processes still alive. + + + + +BIND10_SHUTDOWN_COMPLETE all processes ended, shutdown complete + +All child processes have been stopped, and the boss process will now +stop itself. + + + + +BIND10_SOCKCREATOR_BAD_CAUSE unknown error cause from socket creator: %1 + +The socket creator reported an error when creating a socket. But the function +which failed is unknown (not one of 'S' for socket or 'B' for bind). + + + + +BIND10_SOCKCREATOR_BAD_RESPONSE unknown response for socket request: %1 + +The boss requested a socket from the creator, but the answer is unknown. This +looks like a programmer error. + + + + +BIND10_SOCKCREATOR_CRASHED the socket creator crashed + +The socket creator terminated unexpectedly. It is not possible to restart it +(because the boss already gave up root privileges), so the system is going +to terminate. + + + + +BIND10_SOCKCREATOR_EOF eof while expecting data from socket creator + +There should be more data from the socket creator, but it closed the socket. +It probably crashed. + + + + +BIND10_SOCKCREATOR_INIT initializing socket creator parser + +The boss module initializes routines for parsing the socket creator +protocol. + + + + +BIND10_SOCKCREATOR_KILL killing the socket creator + +The socket creator is being terminated the aggressive way, by sending it +sigkill. This should not happen usually. + + + + +BIND10_SOCKCREATOR_TERMINATE terminating socket creator + +The boss module sends a request to terminate to the socket creator. + + + + +BIND10_SOCKCREATOR_TRANSPORT_ERROR transport error when talking to the socket creator: %1 + +Either sending or receiving data from the socket creator failed with the given +error. The creator probably crashed or some serious OS-level problem happened, +as the communication happens only on local host. + + + + +BIND10_SOCKET_CREATED successfully created socket %1 + +The socket creator successfully created and sent a requested socket, it has +the given file number. + + + + +BIND10_SOCKET_ERROR error on %1 call in the creator: %2/%3 + +The socket creator failed to create the requested socket. It failed on the +indicated OS API function with given error. + + + + +BIND10_SOCKET_GET requesting socket [%1]:%2 of type %3 from the creator + +The boss forwards a request for a socket to the socket creator. + + + + +BIND10_STARTED_PROCESS started %1 + +The given process has successfully been started. + + + + +BIND10_STARTED_PROCESS_PID started %1 (PID %2) + +The given process has successfully been started, and has the given PID. + + + + +BIND10_STARTING starting BIND10: %1 + +Informational message on startup that shows the full version. + + + + +BIND10_STARTING_PROCESS starting process %1 + +The boss module is starting the given process. + + + + +BIND10_STARTING_PROCESS_PORT starting process %1 (to listen on port %2) + +The boss module is starting the given process, which will listen on the +given port number. + + + + +BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2#%3) + +The boss module is starting the given process, which will listen on the +given address and port number (written as <address>#<port>). + + + + +BIND10_STARTUP_COMPLETE BIND 10 started + +All modules have been successfully started, and BIND 10 is now running. + + + + +BIND10_STARTUP_ERROR error during startup: %1 + +There was a fatal error when BIND10 was trying to start. The error is +shown, and BIND10 will now shut down. + + + + +BIND10_START_AS_NON_ROOT starting %1 as a user, not root. This might fail. + +The given module is being started or restarted without root privileges. +If the module needs these privileges, it may have problems starting. +Note that this issue should be resolved by the pending 'socket-creator' +process; once that has been implemented, modules should not need root +privileges anymore. See tickets #800 and #801 for more information. + + + + +BIND10_STOP_PROCESS asking %1 to shut down + +The boss module is sending a shutdown command to the given module over +the message channel. + + + + +BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited + +An unknown child process has exited. The PID is printed, but no further +action will be taken by the boss process. + + + + +CACHE_ENTRY_MISSING_RRSET missing RRset to generate message for %1 + +The cache tried to generate the complete answer message. It knows the structure +of the message, but some of the RRsets to be put there are not in cache (they +probably expired already). Therefore it pretends the message was not found. + + + + +CACHE_LOCALZONE_FOUND found entry with key %1 in local zone data + +Debug message, noting that the requested data was successfully found in the +local zone data of the cache. + + + + +CACHE_LOCALZONE_UNKNOWN entry with key %1 not found in local zone data + +Debug message. The requested data was not found in the local zone data. + + + + +CACHE_LOCALZONE_UPDATE updating local zone element at key %1 + +Debug message issued when there's update to the local zone section of cache. + + + + +CACHE_MESSAGES_DEINIT deinitialized message cache + +Debug message. It is issued when the server deinitializes the message cache. + + + + +CACHE_MESSAGES_EXPIRED found an expired message entry for %1 in the message cache + +Debug message. The requested data was found in the message cache, but it +already expired. Therefore the cache removes the entry and pretends it found +nothing. + + + + +CACHE_MESSAGES_FOUND found a message entry for %1 in the message cache + +Debug message. We found the whole message in the cache, so it can be returned +to user without any other lookups. + + + + +CACHE_MESSAGES_INIT initialized message cache for %1 messages of class %2 + +Debug message issued when a new message cache is issued. It lists the class +of messages it can hold and the maximum size of the cache. + + + + +CACHE_MESSAGES_REMOVE removing old instance of %1/%2/%3 first + +Debug message. This may follow CACHE_MESSAGES_UPDATE and indicates that, while +updating, the old instance is being removed prior of inserting a new one. + + + + +CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3 + +Debug message, noting that the given message can not be cached. This is because +there's no SOA record in the message. See RFC 2308 section 5 for more +information. + + + + +CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache + +Debug message. The message cache didn't find any entry for the given key. + + + + +CACHE_MESSAGES_UPDATE updating message entry %1/%2/%3 + +Debug message issued when the message cache is being updated with a new +message. Either the old instance is removed or, if none is found, new one +is created. + + + + +CACHE_RESOLVER_DEEPEST looking up deepest NS for %1/%2 + +Debug message. The resolver cache is looking up the deepest known nameserver, +so the resolution doesn't have to start from the root. + + + + +CACHE_RESOLVER_INIT initializing resolver cache for class %1 + +Debug message. The resolver cache is being created for this given class. + + + + +CACHE_RESOLVER_INIT_INFO initializing resolver cache for class %1 + +Debug message, the resolver cache is being created for this given class. The +difference from CACHE_RESOLVER_INIT is only in different format of passed +information, otherwise it does the same. + + + + +CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data + +Debug message. The resolver cache found a complete message for the user query +in the zone data. + + + + +CACHE_RESOLVER_LOCAL_RRSET RRset for %1/%2 found in local zone data + +Debug message. The resolver cache found a requested RRset in the local zone +data. + + + + +CACHE_RESOLVER_LOOKUP_MSG looking up message in resolver cache for %1/%2 + +Debug message. The resolver cache is trying to find a message to answer the +user query. + + + + +CACHE_RESOLVER_LOOKUP_RRSET looking up RRset in resolver cache for %1/%2 + +Debug message. The resolver cache is trying to find an RRset (which usually +originates as internally from resolver). + + + + +CACHE_RESOLVER_NO_QUESTION answer message for %1/%2 has empty question section + +The cache tried to fill in found data into the response message. But it +discovered the message contains no question section, which is invalid. +This is likely a programmer error, please submit a bug report. + + + + +CACHE_RESOLVER_UNKNOWN_CLASS_MSG no cache for class %1 + +Debug message. While trying to lookup a message in the resolver cache, it was +discovered there's no cache for this class at all. Therefore no message is +found. + + + + +CACHE_RESOLVER_UNKNOWN_CLASS_RRSET no cache for class %1 + +Debug message. While trying to lookup an RRset in the resolver cache, it was +discovered there's no cache for this class at all. Therefore no data is found. + + + + +CACHE_RESOLVER_UPDATE_MSG updating message for %1/%2/%3 + +Debug message. The resolver is updating a message in the cache. + + + + +CACHE_RESOLVER_UPDATE_RRSET updating RRset for %1/%2/%3 + +Debug message. The resolver is updating an RRset in the cache. + + + + +CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG no cache for class %1 + +Debug message. While trying to insert a message into the cache, it was +discovered that there's no cache for the class of message. Therefore +the message will not be cached. + + + + +CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET no cache for class %1 + +Debug message. While trying to insert an RRset into the cache, it was +discovered that there's no cache for the class of the RRset. Therefore +the message will not be cached. + + + + +CACHE_RRSET_EXPIRED found expired RRset %1/%2/%3 + +Debug message. The requested data was found in the RRset cache. However, it is +expired, so the cache removed it and is going to pretend nothing was found. + + + + +CACHE_RRSET_INIT initializing RRset cache for %1 RRsets of class %2 + +Debug message. The RRset cache to hold at most this many RRsets for the given +class is being created. + + + + +CACHE_RRSET_LOOKUP looking up %1/%2/%3 in RRset cache + +Debug message. The resolver is trying to look up data in the RRset cache. + + + + +CACHE_RRSET_NOT_FOUND no RRset found for %1/%2/%3 + +Debug message which can follow CACHE_RRSET_LOOKUP. This means the data is not +in the cache. + + + + +CACHE_RRSET_REMOVE_OLD removing old RRset for %1/%2/%3 to make space for new one + +Debug message which can follow CACHE_RRSET_UPDATE. During the update, the cache +removed an old instance of the RRset to replace it with the new one. + + + + +CACHE_RRSET_UNTRUSTED not replacing old RRset for %1/%2/%3, it has higher trust level + +Debug message which can follow CACHE_RRSET_UPDATE. The cache already holds the +same RRset, but from more trusted source, so the old one is kept and new one +ignored. + + + + +CACHE_RRSET_UPDATE updating RRset %1/%2/%3 in the cache + +Debug message. The RRset is updating its data with this given RRset. + + + CC_ASYNC_READ_FAILED asynchronous read failed @@ -629,15 +1254,15 @@ Debug message, we're about to send a message over the command channel. This happens when garbage comes over the command channel or some kind of confusion happens in the program. The data received from the socket make no sense if we interpret it as lengths of message. The first one is total length -of message, the second length of the header. The header and it's length -(2 bytes) is counted in the total length. +of the message; the second is the length of the header. The header +and its length (2 bytes) is counted in the total length. CC_LENGTH_NOT_READY length not ready -There should be data representing length of message on the socket, but it +There should be data representing the length of message on the socket, but it is not there. @@ -746,6 +1371,17 @@ are now applied, and no action from the administrator is necessary. + +CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2 + +The configuration manager sent a configuration update to a module, but +the module responded with an answer that could not be parsed. The answer +message appears to be invalid JSON data, or not decodable to a string. +This is likely to be a problem in the module in question. The update is +assumed to have failed, and will not be stored. + + + CFGMGR_CC_SESSION_ERROR Error connecting to command channel: %1 @@ -794,6 +1430,123 @@ daemon will now shut down. + +CMDCTL_BAD_CONFIG_DATA error in config data: %1 + +There was an error reading the updated configuration data. The specific +error is printed. + + + + +CMDCTL_BAD_PASSWORD bad password for user: %1 + +A login attempt was made to b10-cmdctl, but the password was wrong. +Users can be managed with the tool b10-cmdctl-usermgr. + + + + +CMDCTL_CC_SESSION_ERROR error reading from cc channel: %1 + +There was a problem reading from the command and control channel. The +most likely cause is that the message bus daemon is not running. + + + + +CMDCTL_CC_SESSION_TIMEOUT timeout on cc channel + +A timeout occurred when waiting for essential data from the cc session. +This usually occurs when b10-cfgmgr is not running or not responding. +Since we are waiting for essential information, this is a fatal error, +and the cmdctl daemon will now shut down. + + + + +CMDCTL_COMMAND_ERROR error in command %1 to module %2: %3 + +An error was encountered sending the given command to the given module. +Either there was a communication problem with the module, or the module +was not able to process the command, and sent back an error. The +specific error is printed in the message. + + + + +CMDCTL_COMMAND_SENT command '%1' to module '%2' was sent + +This debug message indicates that the given command has been sent to +the given module. + + + + +CMDCTL_NO_SUCH_USER username not found in user database: %1 + +A login attempt was made to b10-cmdctl, but the username was not known. +Users can be added with the tool b10-cmdctl-usermgr. + + + + +CMDCTL_NO_USER_ENTRIES_READ failed to read user information, all users will be denied + +The b10-cmdctl daemon was unable to find any user data in the user +database file. Either it was unable to read the file (in which case +this message follows a message CMDCTL_USER_DATABASE_READ_ERROR +containing a specific error), or the file was empty. Users can be added +with the tool b10-cmdctl-usermgr. + + + + +CMDCTL_SEND_COMMAND sending command %1 to module %2 + +This debug message indicates that the given command is being sent to +the given module. + + + + +CMDCTL_SSL_SETUP_FAILURE_USER_DENIED failed to create an SSL connection (user denied): %1 + +The user was denied because the SSL connection could not successfully +be set up. The specific error is given in the log message. Possible +causes may be that the ssl request itself was bad, or the local key or +certificate file could not be read. + + + + +CMDCTL_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down + +There was a keyboard interrupt signal to stop the cmdctl daemon. The +daemon will now shut down. + + + + +CMDCTL_UNCAUGHT_EXCEPTION uncaught exception: %1 + +The b10-cmdctl daemon encountered an uncaught exception and +will now shut down. This is indicative of a programming error and +should not happen under normal circumstances. The exception message +is printed. + + + + +CMDCTL_USER_DATABASE_READ_ERROR failed to read user database file %1: %2 + +The b10-cmdctl daemon was unable to read the user database file. The +file may be unreadable for the daemon, or it may be corrupted. In the +latter case, it can be recreated with b10-cmdctl-usermgr. The specific +error is printed in the log message. + + + CONFIG_CCSESSION_MSG error in CC session message: %1 @@ -829,6 +1582,15 @@ running configuration manager. + +CONFIG_GET_FAILED error getting configuration from cfgmgr: %1 + +The configuration manager returned an error response when the module +requested its configuration. The full error message answer from the +configuration manager is appended to the log error. + + + CONFIG_JSON_PARSE JSON parse error in %1: %2 @@ -838,6 +1600,56 @@ and that the contents are valid JSON. + +CONFIG_LOG_CONFIG_ERRORS error(s) in logging configuration: %1 + +There was a logging configuration update, but the internal validator +for logging configuration found that it contained errors. The errors +are shown, and the update is ignored. + + + + +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 +logger that matches the logger specification for the program. The logging +configuration for the program will be updated with the information. + + + + +CONFIG_LOG_IGNORE_EXPLICIT ignoring 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 logger. As this does not match the logger specification for the +program, it has been ignored. + + + + +CONFIG_LOG_IGNORE_WILD ignoring logging configuration for wildcard logger %1 + +This is a debug message. When processing the "loggers" part of the +configuration file, the configuration library found the named wildcard +entry (one containing the "*" character) that matched a logger already +matched by an explicitly named entry. The configuration is ignored. + + + + +CONFIG_LOG_WILD_MATCH will use logging configuration for wildcard logger %1 + +This is a debug message. When processing the "loggers" part of +the configuration file, the configuration library found the named +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_MOD_SPEC_FORMAT module specification error in %1: %2 @@ -869,7 +1681,8 @@ is included in the message. DATASRC_CACHE_CREATE creating the hotspot cache -Debug information that the hotspot cache was created at startup. +This is a debug message issued during startup when the hotspot cache +is created. @@ -881,39 +1694,37 @@ Debug information. The hotspot cache is being destroyed. -DATASRC_CACHE_DISABLE disabling the cache +DATASRC_CACHE_DISABLE disabling the hotspot cache -The hotspot cache is disabled from now on. It is not going to store -information or return anything. +A debug message issued when the hotspot cache is disabled. -DATASRC_CACHE_ENABLE enabling the cache +DATASRC_CACHE_ENABLE enabling the hotspot cache -The hotspot cache is enabled from now on. +A debug message issued when the hotspot cache is enabled. -DATASRC_CACHE_EXPIRED the item '%1' is expired +DATASRC_CACHE_EXPIRED item '%1' in the hotspot cache has expired -Debug information. There was an attempt to look up an item in the hotspot -cache. And the item was actually there, but it was too old, so it was removed -instead and nothing is reported (the external behaviour is the same as with -CACHE_NOT_FOUND). +A debug message issued when a hotspot cache lookup located the item but it +had expired. The item was removed and the program proceeded as if the item +had not been found. DATASRC_CACHE_FOUND the item '%1' was found -Debug information. An item was successfully looked up in the hotspot cache. +Debug information. An item was successfully located in the hotspot cache. -DATASRC_CACHE_FULL cache is full, dropping oldest +DATASRC_CACHE_FULL hotspot cache is full, dropping oldest Debug information. After inserting an item into the hotspot cache, the maximum number of items was exceeded, so the least recently used item will @@ -922,39 +1733,39 @@ be dropped. This should be directly followed by CACHE_REMOVE. -DATASRC_CACHE_INSERT inserting item '%1' into the cache +DATASRC_CACHE_INSERT inserting item '%1' into the hotspot cache -Debug information. It means a new item is being inserted into the hotspot +A debug message indicating that a new item is being inserted into the hotspot cache. -DATASRC_CACHE_NOT_FOUND the item '%1' was not found +DATASRC_CACHE_NOT_FOUND the item '%1' was not found in the hotspot cache -Debug information. It was attempted to look up an item in the hotspot cache, -but it is not there. +A debug message issued when hotspot cache was searched for the specified +item but it was not found. -DATASRC_CACHE_OLD_FOUND older instance of cache item found, replacing +DATASRC_CACHE_OLD_FOUND older instance of hotspot cache item '%1' found, replacing Debug information. While inserting an item into the hotspot cache, an older -instance of an item with the same name was found. The old instance will be -removed. This should be directly followed by CACHE_REMOVE. +instance of an item with the same name was found; the old instance will be +removed. This will be directly followed by CACHE_REMOVE. -DATASRC_CACHE_REMOVE removing '%1' from the cache +DATASRC_CACHE_REMOVE removing '%1' from the hotspot cache Debug information. An item is being removed from the hotspot cache. -DATASRC_CACHE_SLOTS setting the cache size to '%1', dropping '%2' items +DATASRC_CACHE_SLOTS setting the hotspot cache size to '%1', dropping '%2' items The maximum allowed number of items of the hotspot cache is set to the given number. If there are too many, some of them will be dropped. The size of 0 @@ -962,11 +1773,109 @@ means no limit. + +DATASRC_DATABASE_FIND_ERROR error retrieving data from datasource %1: %2 + +This was an internal error while reading data from a datasource. This can either +mean the specific data source implementation is not behaving correctly, or the +data it provides is invalid. The current search is aborted. +The error message contains specific information about the error. + + + + +DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3 + +Debug information. The database data source is looking up records with the given +name and type in the database. + + + + +DATASRC_DATABASE_FIND_TTL_MISMATCH TTL values differ in %1 for elements of %2/%3/%4, setting to %5 + +The datasource backend provided resource records for the given RRset with +different TTL values. The TTL of the RRSET is set to the lowest value, which +is printed in the log message. + + + + +DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from datasource %1: %2 + +There was an uncaught general exception while reading data from a datasource. +This most likely points to a logic error in the code, and can be considered a +bug. The current search is aborted. Specific information about the exception is +printed in this error message. + + + + +DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR uncaught error retrieving data from datasource %1: %2 + +There was an uncaught ISC exception while reading data from a datasource. This +most likely points to a logic error in the code, and can be considered a bug. +The current search is aborted. Specific information about the exception is +printed in this error message. + + + + +DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %2 in %1 + +When searching for a domain, the program met a delegation to a different zone +at the given domain name. It will return that one instead. + + + + +DATASRC_DATABASE_FOUND_DELEGATION_EXACT Found delegation at %2 (exact match) in %1 + +The program found the domain requested, but it is a delegation point to a +different zone, therefore it is not authoritative for this domain name. +It will return the NS record instead. + + + + +DATASRC_DATABASE_FOUND_DNAME Found DNAME at %2 in %1 + +When searching for a domain, the program met a DNAME redirection to a different +place in the domain space at the given domain name. It will return that one +instead. + + + + +DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4 + +The data returned by the database backend did not contain any data for the given +domain name, class and type. + + + + +DATASRC_DATABASE_FOUND_NXRRSET search in datasource %1 resulted in NXRRSET for %2/%3/%4 + +The data returned by the database backend contained data for the given domain +name and class, but not for the given type. + + + + +DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %2 + +The data returned by the database backend contained data for the given domain +name, and it either matches the type or has a relevant type. The RRset that is +returned is printed. + + + DATASRC_DO_QUERY handling query for '%1/%2' -Debug information. We're processing some internal query for given name and -type. +A debug message indicating that a query for the given name and RR type is being +processed. @@ -980,8 +1889,9 @@ Debug information. An RRset is being added to the in-memory data source. DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1' -Debug information. Some special marks above each * in wildcard name are needed. -They are being added now for this name. +This is a debug message issued during the processing of a wildcard +name. The internal domain name tree is scanned and some nodes are +specially marked to allow the wildcard lookup to succeed. @@ -1066,9 +1976,9 @@ Debug information. A DNAME was found instead of the requested information. DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1' -It was requested for DNAME and NS records to be put into the same domain -which is not the apex (the top of the zone). This is forbidden by RFC -2672, section 3. This indicates a problem with provided data. +A request was made for DNAME and NS records to be put into the same +domain which is not the apex (the top of the zone). This is forbidden +by RFC 2672 (section 3) and indicates a problem with provided data. @@ -1120,8 +2030,8 @@ Debug information. The content of master file is being loaded into the memory. - -DATASRC_MEM_NOTFOUND requested domain '%1' not found + +DATASRC_MEM_NOT_FOUND requested domain '%1' not found Debug information. The requested domain does not exist. @@ -1229,15 +2139,15 @@ different tools. DATASRC_META_ADD adding a data source into meta data source -Debug information. Yet another data source is being added into the meta data -source. (probably at startup or reconfiguration) +This is a debug message issued during startup or reconfiguration. +Another data source is being added into the meta data source. DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2' -It was attempted to add a data source into a meta data source. But their +It was attempted to add a data source into a meta data source, but their classes do not match. @@ -1297,7 +2207,7 @@ information for it. -DATASRC_QUERY_CACHED data for %1/%2 found in cache +DATASRC_QUERY_CACHED data for %1/%2 found in hotspot cache Debug information. The requested data were found in the hotspot cache, so no query is sent to the real data source. @@ -1305,7 +2215,7 @@ no query is sent to the real data source. -DATASRC_QUERY_CHECK_CACHE checking cache for '%1/%2' +DATASRC_QUERY_CHECK_CACHE checking hotspot cache for '%1/%2' Debug information. While processing a query, lookup to the hotspot cache is being made. @@ -1331,10 +2241,9 @@ way down to the given domain. DATASRC_QUERY_EMPTY_CNAME CNAME at '%1' is empty -There was an CNAME and it was being followed. But it contains no records, -so there's nowhere to go. There will be no answer. This indicates a problem -with supplied data. -We tried to follow +A CNAME chain was being followed and an entry was found that pointed +to a domain name that had no RRsets associated with it. As a result, +the query cannot be answered. This indicates a problem with supplied data. @@ -1350,15 +2259,15 @@ DNAME is empty (it has no records). This indicates problem with supplied data. DATASRC_QUERY_FAIL query failed Some subtask of query processing failed. The reason should have been reported -already. We are returning SERVFAIL. +already and a SERVFAIL will be returned to the querying system. DATASRC_QUERY_FOLLOW_CNAME following CNAME at '%1' -Debug information. The domain is a CNAME (or a DNAME and we created a CNAME -for it already), so it's being followed. +Debug information. The domain is a CNAME (or a DNAME and a CNAME for it +has already been created) and the search is following this chain. @@ -1407,14 +2316,14 @@ Debug information. The last DO_QUERY is an auth query. DATASRC_QUERY_IS_GLUE glue query (%1/%2) -Debug information. The last DO_QUERY is query for glue addresses. +Debug information. The last DO_QUERY is a query for glue addresses. DATASRC_QUERY_IS_NOGLUE query for non-glue addresses (%1/%2) -Debug information. The last DO_QUERY is query for addresses that are not +Debug information. The last DO_QUERY is a query for addresses that are not glue. @@ -1422,7 +2331,7 @@ glue. DATASRC_QUERY_IS_REF query for referral (%1/%2) -Debug information. The last DO_QUERY is query for referral information. +Debug information. The last DO_QUERY is a query for referral information. @@ -1469,7 +2378,7 @@ error already. -DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class) +DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring hotspot cache for ANY query (%1/%2 in %3 class) Debug information. The hotspot cache is ignored for authoritative ANY queries for consistency reasons. @@ -1477,7 +2386,7 @@ for consistency reasons. -DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class) +DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring hotspot cache for ANY query (%1/%2 in %3 class) Debug information. The hotspot cache is ignored for ANY queries for consistency reasons. @@ -1515,8 +2424,8 @@ Debug information. A sure query is being processed now. - -DATASRC_QUERY_PROVENX_FAIL unable to prove nonexistence of '%1' + +DATASRC_QUERY_PROVE_NX_FAIL unable to prove nonexistence of '%1' The user wants DNSSEC and we discovered the entity doesn't exist (either domain or the record). But there was an error getting NSEC/NSEC3 record @@ -1553,9 +2462,9 @@ error already. DATASRC_QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1' -Debug information. While answering a query, a DNAME was met. The DNAME itself -will be returned, but along with it a CNAME for clients which don't understand -DNAMEs will be synthesized. +This is a debug message. While answering a query, a DNAME was encountered. The +DNAME itself will be returned, along with a synthesized CNAME for clients that +do not understand the DNAME RR. @@ -1601,8 +2510,8 @@ exact kind was hopefully already reported. - -DATASRC_QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2) + +DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL unable to prove nonexistence of '%1' (%2) While processing a wildcard, it wasn't possible to prove nonexistence of the given domain or record. The code is 1 for error and 2 for not implemented. @@ -1624,6 +2533,20 @@ Debug information. The SQLite data source is closing the database file. + +DATASRC_SQLITE_CONNCLOSE Closing sqlite database + +The database file is no longer needed and is being closed. + + + + +DATASRC_SQLITE_CONNOPEN Opening sqlite database file '%1' + +The database file is being opened so it can start providing data. + + + DATASRC_SQLITE_CREATE SQLite data source created @@ -1638,6 +2561,13 @@ Debug information. An instance of SQLite data source is being destroyed. + +DATASRC_SQLITE_DROPCONN SQLite3Database is being deinitialized + +The object around a database connection is being destroyed. + + + DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1' @@ -1646,8 +2576,8 @@ should hold this domain. - -DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it + +DATASRC_SQLITE_ENCLOSURE_NOT_FOUND no zone contains '%1' Debug information. The last SQLITE_ENCLOSURE query was unsuccessful; there's no such zone in our data. @@ -1742,6 +2672,13 @@ But it doesn't contain that zone. + +DATASRC_SQLITE_NEWCONN SQLite3Database is being initialized + +A wrapper object to hold database connection is being initialized. + + + DATASRC_SQLITE_OPEN opening SQLite database '%1' @@ -1753,15 +2690,22 @@ the provided file. DATASRC_SQLITE_PREVIOUS looking for name previous to '%1' -Debug information. We're trying to look up name preceding the supplied one. +This is a debug message. The name given was not found, so the program +is searching for the next name higher up the hierarchy (e.g. if +www.example.com were queried for and not found, the software searches +for the "previous" name, example.com). DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1' -The SQLite data source tried to identify name preceding this one. But this -one is not contained in any zone in the data source. +The name given was not found, so the program is searching for the next +name higher up the hierarchy (e.g. if www.example.com were queried +for and not found, the software searches for the "previous" name, +example.com). However, this name is not contained in any zone in the +data source. This is an error since it indicates a problem in the earlier +processing of the query. @@ -1774,11 +2718,11 @@ no data, but it will be ready for use. - -DATASRC_STATIC_BAD_CLASS static data source can handle CH only + +DATASRC_STATIC_CLASS_NOT_CH static data source can handle CH class only -For some reason, someone asked the static data source a query that is not in -the CH class. +An error message indicating that a query requesting a RR for a class other +that CH was sent to the static data source (which only handles CH queries). @@ -1851,27 +2795,25 @@ destination should be one of "console", "file", or "syslog". LOG_BAD_SEVERITY unrecognized log severity: %1 A logger severity value was given that was not recognized. The severity -should be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL". +should be one of "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". LOG_BAD_STREAM bad log console output stream: %1 -A log console output stream was given that was not recognized. The output -stream should be one of "stdout", or "stderr" +Logging has been configured so that output is written to the terminal +(console) but the stream on which it is to be written is not recognised. +Allowed values are "stdout" and "stderr". LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code -During start-up, BIND10 detected that the given message identification had -been defined multiple times in the BIND10 code. - -This has no ill-effects other than the possibility that an erronous -message may be logged. However, as it is indicative of a programming -error, please log a bug report. +During start-up, BIND 10 detected that the given message identification +had been defined multiple times in the BIND 10 code. This indicates a +programming error; please submit a bug report. @@ -1879,7 +2821,9 @@ error, please log a bug report. LOG_DUPLICATE_NAMESPACE line %1: duplicate $NAMESPACE directive found When reading a message file, more than one $NAMESPACE directive was found. -Such a condition is regarded as an error and the read will be abandoned. +(This directive is used to set a C++ namespace when generating header +files during software development.) Such a condition is regarded as an +error and the read will be abandoned. @@ -1966,10 +2910,10 @@ There may be several reasons why this message may appear: - The program outputting the message may not use that particular message (e.g. it originates in a module not used by the program.) -- The local file was written for an earlier version of the BIND10 software +- The local file was written for an earlier version of the BIND 10 software and the later version no longer generates that message. -Whatever the reason, there is no impact on the operation of BIND10. +Whatever the reason, there is no impact on the operation of BIND 10. @@ -1990,7 +2934,7 @@ This error is generated when the compiler finds a $PREFIX directive with more than one argument. Note: the $PREFIX directive is deprecated and will be removed in a future -version of BIND10. +version of BIND 10. @@ -2002,17 +2946,17 @@ a prefix to be added to the symbol names when a C++ file is created. As such, it must adhere to restrictions on C++ symbol names (e.g. may only contain alphanumeric characters or underscores, and may nor start with a digit). A $PREFIX directive was found with an argument (given -in the message) that violates those restictions. +in the message) that violates those restrictions. Note: the $PREFIX directive is deprecated and will be removed in a future -version of BIND10. +version of BIND 10. LOG_READING_LOCAL_FILE reading local message file %1 -This is an informational message output by BIND10 when it starts to read +This is an informational message output by BIND 10 when it starts to read a local message file. (A local message file may replace the text of one of more messages; the ID of the message will not be changed though.) @@ -2042,6 +2986,117 @@ to the named output file. + +NOTIFY_OUT_INVALID_ADDRESS invalid address %1#%2: %3 + +The notify_out library tried to send a notify message to the given +address, but it appears to be an invalid address. The configuration +for secondary nameservers might contain a typographic error, or a +different BIND 10 module has forgotten to validate its data before +sending this module a notify command. As such, this should normally +not happen, and points to an oversight in a different module. + + + + +NOTIFY_OUT_REPLY_BAD_OPCODE bad opcode in notify reply from %1#%2: %3 + +The notify_out library sent a notify message to the nameserver at +the given address, but the response did not have the opcode set to +NOTIFY. The opcode in the response is printed. Since there was a +response, no more notifies will be sent to this server for this +notification event. + + + + +NOTIFY_OUT_REPLY_BAD_QID bad QID in notify reply from %1#%2: got %3, should be %4 + +The notify_out library sent a notify message to the nameserver at +the given address, but the query id in the response does not match +the one we sent. Since there was a response, no more notifies will +be sent to this server for this notification event. + + + + +NOTIFY_OUT_REPLY_BAD_QUERY_NAME bad query name in notify reply from %1#%2: got %3, should be %4 + +The notify_out library sent a notify message to the nameserver at +the given address, but the query name in the response does not match +the one we sent. Since there was a response, no more notifies will +be sent to this server for this notification event. + + + + +NOTIFY_OUT_REPLY_QR_NOT_SET QR flags set to 0 in reply to notify from %1#%2 + +The notify_out library sent a notify message to the namesever at the +given address, but the reply did not have the QR bit set to one. +Since there was a response, no more notifies will be sent to this +server for this notification event. + + + + +NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION uncaught exception: %1 + +There was an uncaught exception in the handling of a notify reply +message, either in the message parser, or while trying to extract data +from the parsed message. The error is printed, and notify_out will +treat the response as a bad message, but this does point to a +programming error, since all exceptions should have been caught +explicitly. Please file a bug report. Since there was a response, +no more notifies will be sent to this server for this notification +event. + + + + +NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries (%3) exceeded + +The maximum number of retries for the notify target has been exceeded. +Either the address of the secondary nameserver is wrong, or it is not +responding. + + + + +NOTIFY_OUT_SENDING_NOTIFY sending notify to %1#%2 + +A notify message is sent to the secondary nameserver at the given +address. + + + + +NOTIFY_OUT_SOCKET_ERROR socket error sending notify to %1#%2: %3 + +There was a network error while trying to send a notify message to +the given address. The address might be unreachable. The socket +error is printed and should provide more information. + + + + +NOTIFY_OUT_SOCKET_RECV_ERROR socket error reading notify reply from %1#%2: %3 + +There was a network error while trying to read a notify reply +message from the given address. The socket error is printed and should +provide more information. + + + + +NOTIFY_OUT_TIMEOUT retry notify to %1#%2 + +The notify message to the given address (noted as address#port) has +timed out, and the message will be resent until the max retry limit +is reached. + + + NSAS_FIND_NS_ADDRESS asking resolver to obtain A and AAAA records for %1 @@ -2327,11 +3382,11 @@ to the specified nameserver. RESLIB_TEST_SERVER setting test server to %1(%2) -This is an internal debugging message and is only generated in unit tests. -It indicates that all upstream queries from the resolver are being routed to -the specified server, regardless of the address of the nameserver to which -the query would normally be routed. As it should never be seen in normal -operation, it is a warning message instead of a debug message. +This is a warning message only generated in unit tests. It indicates +that all upstream queries from the resolver are being routed to the +specified server, regardless of the address of the nameserver to which +the query would normally be routed. If seen during normal operation, +please submit a bug report. @@ -2347,8 +3402,8 @@ whose address is given in the message. RESLIB_TIMEOUT query <%1> to %2 timed out -A debug message indicating that the specified query has timed out and as -there are no retries left, an error will be reported. +A debug message indicating that the specified upstream query has timed out and +there are no retries left. @@ -2382,107 +3437,117 @@ tuple is being sent to a nameserver whose address is given in the message. RESOLVER_AXFR_TCP AXFR request received over TCP -A debug message, the resolver received a NOTIFY message over TCP. The server -cannot process it and will return an error message to the sender with the -RCODE set to NOTIMP. +This is a debug message output when the resolver received a request for +an AXFR (full transfer of a zone) over TCP. Only authoritative servers +are able to handle AXFR requests, so the resolver will return an error +message to the sender with the RCODE set to NOTIMP. RESOLVER_AXFR_UDP AXFR request received over UDP -A debug message, the resolver received a NOTIFY message over UDP. The server -cannot process it (and in any case, an AXFR request should be sent over TCP) -and will return an error message to the sender with the RCODE set to FORMERR. +This is a debug message output when the resolver received a request for +an AXFR (full transfer of a zone) over UDP. Only authoritative servers +are able to handle AXFR requests (and in any case, an AXFR request should +be sent over TCP), so the resolver will return an error message to the +sender with the RCODE set to NOTIMP. RESOLVER_CLIENT_TIME_SMALL client timeout of %1 is too small -An error indicating that the configuration value specified for the query -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the client timeout was found to be too small. The configuration +update was abandoned and the parameters were not changed. RESOLVER_CONFIG_CHANNEL configuration channel created -A debug message, output when the resolver has successfully established a -connection to the configuration channel. +This is a debug message output when the resolver has successfully +established a connection to the configuration channel. RESOLVER_CONFIG_ERROR error in configuration: %1 -An error was detected in a configuration update received by the resolver. This -may be in the format of the configuration message (in which case this is a -programming error) or it may be in the data supplied (in which case it is -a user error). The reason for the error, given as a parameter in the message, -will give more details. +An error was detected in a configuration update received by the +resolver. This may be in the format of the configuration message (in +which case this is a programming error) or it may be in the data supplied +(in which case it is a user error). The reason for the error, included +in the message, will give more details. The configuration update is +not applied and the resolver parameters were not changed. RESOLVER_CONFIG_LOADED configuration loaded -A debug message, output when the resolver configuration has been successfully -loaded. +This is a debug message output when the resolver configuration has been +successfully loaded. RESOLVER_CONFIG_UPDATED configuration updated: %1 -A debug message, the configuration has been updated with the specified -information. +This is a debug message output when the resolver configuration is being +updated with the specified information. RESOLVER_CREATED main resolver object created -A debug message, output when the Resolver() object has been created. +This is a debug message indicating that the main resolver object has +been created. RESOLVER_DNS_MESSAGE_RECEIVED DNS message received: %1 -A debug message, this always precedes some other logging message and is the -formatted contents of the DNS packet that the other message refers to. +This is a debug message from the resolver listing the contents of a +received DNS message. RESOLVER_DNS_MESSAGE_SENT DNS message of %1 bytes sent: %2 -A debug message, this contains details of the response sent back to the querying -system. +This is a debug message containing details of the response returned by +the resolver to the querying system. RESOLVER_FAILED resolver failed, reason: %1 -This is an error message output when an unhandled exception is caught by the -resolver. All it can do is to shut down. +This is an error message output when an unhandled exception is caught +by the resolver. After this, the resolver will shut itself down. +Please submit a bug report. RESOLVER_FORWARD_ADDRESS setting forward address %1(%2) -This message may appear multiple times during startup, and it lists the -forward addresses used by the resolver when running in forwarding mode. +If the resolver is running in forward mode, this message will appear +during startup to list the forward address. If multiple addresses are +specified, it will appear once for each address. RESOLVER_FORWARD_QUERY processing forward query -The received query has passed all checks and is being forwarded to upstream +This is a debug message indicating that a query received by the resolver +has passed a set of checks (message is well-formed, it is allowed by the +ACL, it is a supported opcode, etc.) and is being forwarded to upstream servers. @@ -2490,204 +3555,217 @@ servers. RESOLVER_HEADER_ERROR message received, exception when processing header: %1 -A debug message noting that an exception occurred during the processing of -a received packet. The packet has been dropped. +This is a debug message from the resolver noting that an exception +occurred during the processing of a received packet. The packet has +been dropped. RESOLVER_IXFR IXFR request received -The resolver received a NOTIFY message over TCP. The server cannot process it -and will return an error message to the sender with the RCODE set to NOTIMP. +This is a debug message indicating that the resolver received a request +for an IXFR (incremental transfer of a zone). Only authoritative servers +are able to handle IXFR requests, so the resolver will return an error +message to the sender with the RCODE set to NOTIMP. RESOLVER_LOOKUP_TIME_SMALL lookup timeout of %1 is too small -An error indicating that the configuration value specified for the lookup -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the lookup timeout was found to be too small. The configuration +update will not be applied. RESOLVER_MESSAGE_ERROR error parsing received message: %1 - returning %2 -A debug message noting that the resolver received a message and the -parsing of the body of the message failed due to some error (although -the parsing of the header succeeded). The message parameters give a -textual description of the problem and the RCODE returned. +This is a debug message noting that parsing of the body of a received +message by the resolver failed due to some error (although the parsing of +the header succeeded). The message parameters give a textual description +of the problem and the RCODE returned. RESOLVER_NEGATIVE_RETRIES negative number of retries (%1) specified in the configuration -An error message indicating that the resolver configuration has specified a -negative retry count. Only zero or positive values are valid. +This error is issued when a resolver configuration update has specified +a negative retry count: only zero or positive values are valid. The +configuration update was abandoned and the parameters were not changed. RESOLVER_NON_IN_PACKET non-IN class request received, returning REFUSED message -A debug message, the resolver has received a DNS packet that was not IN class. -The resolver cannot handle such packets, so is returning a REFUSED response to -the sender. +This debug message is issued when resolver has received a DNS packet that +was not IN (Internet) class. The resolver cannot handle such packets, +so is returning a REFUSED response to the sender. RESOLVER_NORMAL_QUERY processing normal query -The received query has passed all checks and is being processed by the resolver. +This is a debug message indicating that the query received by the resolver +has passed a set of checks (message is well-formed, it is allowed by the +ACL, it is a supported opcode, etc.) and is being processed by the resolver. RESOLVER_NOTIFY_RECEIVED NOTIFY arrived but server is not authoritative -The resolver received a NOTIFY message. As the server is not authoritative it -cannot process it, so it returns an error message to the sender with the RCODE -set to NOTAUTH. +The resolver has received a NOTIFY message. As the server is not +authoritative it cannot process it, so it returns an error message to +the sender with the RCODE set to NOTAUTH. RESOLVER_NOT_ONE_QUESTION query contained %1 questions, exactly one question was expected -A debug message, the resolver received a query that contained the number of -entires in the question section detailed in the message. This is a malformed -message, as a DNS query must contain only one question. The resolver will -return a message to the sender with the RCODE set to FORMERR. +This debug message indicates that the resolver received a query that +contained the number of entries in the question section detailed in +the message. This is a malformed message, as a DNS query must contain +only one question. The resolver will return a message to the sender +with the RCODE set to FORMERR. RESOLVER_NO_ROOT_ADDRESS no root addresses available -A warning message during startup, indicates that no root addresses have been -set. This may be because the resolver will get them from a priming query. +A warning message issued during resolver startup, this indicates that +no root addresses have been set. This may be because the resolver will +get them from a priming query. RESOLVER_PARSE_ERROR error parsing received message: %1 - returning %2 -A debug message noting that the resolver received a message and the parsing -of the body of the message failed due to some non-protocol related reason -(although the parsing of the header succeeded). The message parameters give -a textual description of the problem and the RCODE returned. +This is a debug message noting that the resolver received a message and +the parsing of the body of the message failed due to some non-protocol +related reason (although the parsing of the header succeeded). +The message parameters give a textual description of the problem and +the RCODE returned. RESOLVER_PRINT_COMMAND print message command, arguments are: %1 -This message is logged when a "print_message" command is received over the -command channel. +This debug message is logged when a "print_message" command is received +by the resolver over the command channel. RESOLVER_PROTOCOL_ERROR protocol error parsing received message: %1 - returning %2 -A debug message noting that the resolver received a message and the parsing -of the body of the message failed due to some protocol error (although the -parsing of the header succeeded). The message parameters give a textual -description of the problem and the RCODE returned. +This is a debug message noting that the resolver received a message and +the parsing of the body of the message failed due to some protocol error +(although the parsing of the header succeeded). The message parameters +give a textual description of the problem and the RCODE returned. RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4 -A debug message that indicates an incoming query is accepted in terms of -the query ACL. The log message shows the query in the form of -<query name>/<query type>/<query class>, and the client that sends the -query in the form of <Source IP address>#<source port>. +This debug message is produced by the resolver when an incoming query +is accepted in terms of the query ACL. The log message shows the query +in the form of <query name>/<query type>/<query class>, and the client +that sends the query in the form of <Source IP address>#<source port>. RESOLVER_QUERY_DROPPED query dropped: '%1/%2/%3' from %4 -An informational message that indicates an incoming query is dropped -in terms of the query ACL. Unlike the RESOLVER_QUERY_REJECTED -case, the server does not return any response. The log message -shows the query in the form of <query name>/<query type>/<query -class>, and the client that sends the query in the form of <Source -IP address>#<source port>. +This is an informational message that indicates an incoming query has +been dropped by the resolver because of the query ACL. Unlike the +RESOLVER_QUERY_REJECTED case, the server does not return any response. +The log message shows the query in the form of <query name>/<query +type>/<query class>, and the client that sends the query in the form of +<Source IP address>#<source port>. RESOLVER_QUERY_REJECTED query rejected: '%1/%2/%3' from %4 -An informational message that indicates an incoming query is rejected -in terms of the query ACL. This results in a response with an RCODE of -REFUSED. The log message shows the query in the form of <query -name>/<query type>/<query class>, and the client that sends the -query in the form of <Source IP address>#<source port>. +This is an informational message that indicates an incoming query has +been rejected by the resolver because of the query ACL. This results +in a response with an RCODE of REFUSED. The log message shows the query +in the form of <query name>/<query type>/<query class>, and the client +that sends the query in the form of <Source IP address>#<source port>. RESOLVER_QUERY_SETUP query setup -A debug message noting that the resolver is creating a RecursiveQuery object. +This is a debug message noting that the resolver is creating a +RecursiveQuery object. RESOLVER_QUERY_SHUTDOWN query shutdown -A debug message noting that the resolver is destroying a RecursiveQuery object. +This is a debug message noting that the resolver is destroying a +RecursiveQuery object. RESOLVER_QUERY_TIME_SMALL query timeout of %1 is too small -An error indicating that the configuration value specified for the query -timeout is too small. +During the update of the resolver's configuration parameters, the value +of the query timeout was found to be too small. The configuration +parameters were not changed. RESOLVER_RECEIVED_MESSAGE resolver has received a DNS message -A debug message indicating that the resolver has received a message. Depending -on the debug settings, subsequent log output will indicate the nature of the -message. +This is a debug message indicating that the resolver has received a +DNS message. Depending on the debug settings, subsequent log output +will indicate the nature of the message. RESOLVER_RECURSIVE running in recursive mode -This is an informational message that appears at startup noting that the -resolver is running in recursive mode. +This is an informational message that appears at startup noting that +the resolver is running in recursive mode. RESOLVER_SERVICE_CREATED service object created -A debug message, output when the main service object (which handles the -received queries) is created. +This debug message is output when resolver creates the main service object +(which handles the received queries). RESOLVER_SET_PARAMS query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4 -A debug message, lists the parameters being set for the resolver. These are: +This debug message lists the parameters being set for the resolver. These are: query timeout: the timeout (in ms) used for queries originated by the resolver -to upstream servers. Client timeout: the interval to resolver a query by +to upstream servers. Client timeout: the interval to resolve a query by a client: after this time, the resolver sends back a SERVFAIL to the client -whilst continuing to resolver the query. Lookup timeout: the time at which the +whilst continuing to resolve the query. Lookup timeout: the time at which the resolver gives up trying to resolve a query. Retry count: the number of times the resolver will retry a query to an upstream server if it gets a timeout. @@ -2696,7 +3774,7 @@ resolution of the client query might require a large number of queries to upstream nameservers. Even if none of these queries timeout, the total time taken to perform all the queries may exceed the client timeout. When this happens, a SERVFAIL is returned to the client, but the resolver continues -with the resolution process. Data received is added to the cache. However, +with the resolution process; data received is added to the cache. However, there comes a time - the lookup timeout - when even the resolver gives up. At this point it will wait for pending upstream queries to complete or timeout and drop the query. @@ -2706,23 +3784,24 @@ timeout and drop the query. RESOLVER_SET_QUERY_ACL query ACL is configured -A debug message that appears when a new query ACL is configured for the -resolver. +This debug message is generated when a new query ACL is configured for +the resolver. RESOLVER_SET_ROOT_ADDRESS setting root address %1(%2) -This message may appear multiple times during startup; it lists the root -addresses used by the resolver. +This message gives the address of one of the root servers used by the +resolver. It is output during startup and may appear multiple times, +once for each root server address. RESOLVER_SHUTDOWN resolver shutdown complete -This information message is output when the resolver has shut down. +This informational message is output when the resolver has shut down. @@ -2744,17 +3823,367 @@ An informational message, this is output when the resolver starts up. RESOLVER_UNEXPECTED_RESPONSE received unexpected response, ignoring -A debug message noting that the server has received a response instead of a -query and is ignoring it. +This is a debug message noting that the resolver received a DNS response +packet on the port on which is it listening for queries. The packet +has been ignored. RESOLVER_UNSUPPORTED_OPCODE opcode %1 not supported by the resolver -A debug message, the resolver received a message with an unsupported opcode -(it can only process QUERY opcodes). It will return a message to the sender -with the RCODE set to NOTIMP. +This is debug message output when the resolver received a message with an +unsupported opcode (it can only process QUERY opcodes). It will return +a message to the sender with the RCODE set to NOTIMP. + + + + +SRVCOMM_ADDRESSES_NOT_LIST the address and port specification is not a list in %1 + +This points to an error in configuration. What was supposed to be a list of +IP address - port pairs isn't a list at all but something else. + + + + +SRVCOMM_ADDRESS_FAIL failed to listen on addresses (%1) + +The server failed to bind to one of the address/port pair it should according +to configuration, for reason listed in the message (usually because that pair +is already used by other service or missing privileges). The server will try +to recover and bind the address/port pairs it was listening to before (if any). + + + + +SRVCOMM_ADDRESS_MISSING address specification is missing "address" or "port" element in %1 + +This points to an error in configuration. An address specification in the +configuration is missing either an address or port and so cannot be used. The +specification causing the error is given in the message. + + + + +SRVCOMM_ADDRESS_TYPE address specification type is invalid in %1 + +This points to an error in configuration. An address specification in the +configuration malformed. The specification causing the error is given in the +message. A valid specification contains an address part (which must be a string +and must represent a valid IPv4 or IPv6 address) and port (which must be an +integer in the range valid for TCP/UDP ports on your system). + + + + +SRVCOMM_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2) + +The recovery of old addresses after SRVCOMM_ADDRESS_FAIL also failed for +the reason listed. + +The condition indicates problems with the server and/or the system on +which it is running. The server will continue running to allow +reconfiguration, but will not be listening on any address or port until +an administrator does so. + + + + +SRVCOMM_ADDRESS_VALUE address to set: %1#%2 + +Debug message. This lists one address and port value of the set of +addresses we are going to listen on (eg. there will be one log message +per pair). This appears only after SRVCOMM_SET_LISTEN, but might +be hidden, as it has higher debug level. + + + + +SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring + +Debug message indicating that the server is deinitializing the TSIG keyring. + + + + +SRVCOMM_KEYS_INIT initializing TSIG keyring + +Debug message indicating that the server is initializing the global TSIG +keyring. This should be seen only at server start. + + + + +SRVCOMM_KEYS_UPDATE updating TSIG keyring + +Debug message indicating new keyring is being loaded from configuration (either +on startup or as a result of configuration update). + + + + +SRVCOMM_PORT_RANGE port out of valid range (%1 in %2) + +This points to an error in configuration. The port in an address +specification is outside the valid range of 0 to 65535. + + + + +SRVCOMM_SET_LISTEN setting addresses to listen to + +Debug message, noting that the server is about to start listening on a +different set of IP addresses and ports than before. + + + + +STATHTTPD_BAD_OPTION_VALUE bad command line argument: %1 + +The stats-httpd module was called with a bad command-line argument +and will not start. + + + + +STATHTTPD_CC_SESSION_ERROR error connecting to message bus: %1 + +The stats-httpd module was unable to connect to the BIND 10 command +and control bus. A likely problem is that the message bus daemon +(b10-msgq) is not running. The stats-httpd module will now shut down. + + + + +STATHTTPD_CLOSING closing %1#%2 + +The stats-httpd daemon will stop listening for requests on the given +address and port number. + + + + +STATHTTPD_CLOSING_CC_SESSION stopping cc session + +Debug message indicating that the stats-httpd module is disconnecting +from the command and control bus. + + + + +STATHTTPD_HANDLE_CONFIG reading configuration: %1 + +The stats-httpd daemon has received new configuration data and will now +process it. The (changed) data is printed. + + + + +STATHTTPD_RECEIVED_SHUTDOWN_COMMAND shutdown command received + +A shutdown command was sent to the stats-httpd module, and it will +now shut down. + + + + +STATHTTPD_RECEIVED_STATUS_COMMAND received command to return status + +A status command was sent to the stats-httpd module, and it will +respond with 'Stats Httpd is up.' and its PID. + + + + +STATHTTPD_RECEIVED_UNKNOWN_COMMAND received unknown command: %1 + +An unknown command has been sent to the stats-httpd module. The +stats-httpd module will respond with an error, and the command will +be ignored. + + + + +STATHTTPD_SERVER_ERROR HTTP server error: %1 + +An internal error occurred while handling an HTTP request. An HTTP 500 +response will be sent back, and the specific error is printed. This +is an error condition that likely points to a module that is not +responding correctly to statistic requests. + + + + +STATHTTPD_SERVER_INIT_ERROR HTTP server initialization error: %1 + +There was a problem initializing the HTTP server in the stats-httpd +module upon receiving its configuration data. The most likely cause +is a port binding problem or a bad configuration value. The specific +error is printed in the message. The new configuration is ignored, +and an error is sent back. + + + + +STATHTTPD_SHUTDOWN shutting down + +The stats-httpd daemon is shutting down. + + + + +STATHTTPD_STARTED listening on %1#%2 + +The stats-httpd daemon will now start listening for requests on the +given address and port number. + + + + +STATHTTPD_STARTING_CC_SESSION starting cc session + +Debug message indicating that the stats-httpd module is connecting to +the command and control bus. + + + + +STATHTTPD_START_SERVER_INIT_ERROR HTTP server initialization error: %1 + +There was a problem initializing the HTTP server in the stats-httpd +module upon startup. The most likely cause is that it was not able +to bind to the listening port. The specific error is printed, and the +module will shut down. + + + + +STATHTTPD_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down + +There was a keyboard interrupt signal to stop the stats-httpd +daemon. The daemon will now shut down. + + + + +STATHTTPD_UNKNOWN_CONFIG_ITEM unknown configuration item: %1 + +The stats-httpd daemon received a configuration update from the +configuration manager. However, one of the items in the +configuration is unknown. The new configuration is ignored, and an +error is sent back. As possible cause is that there was an upgrade +problem, and the stats-httpd version is out of sync with the rest of +the system. + + + + +STATS_BAD_OPTION_VALUE bad command line argument: %1 + +The stats module was called with a bad command-line argument and will +not start. + + + + +STATS_CC_SESSION_ERROR error connecting to message bus: %1 + +The stats module was unable to connect to the BIND 10 command and +control bus. A likely problem is that the message bus daemon +(b10-msgq) is not running. The stats module will now shut down. + + + + +STATS_RECEIVED_NEW_CONFIG received new configuration: %1 + +This debug message is printed when the stats module has received a +configuration update from the configuration manager. + + + + +STATS_RECEIVED_REMOVE_COMMAND received command to remove %1 + +A remove command for the given name was sent to the stats module, and +the given statistics value will now be removed. It will not appear in +statistics reports until it appears in a statistics update from a +module again. + + + + +STATS_RECEIVED_RESET_COMMAND received command to reset all statistics + +The stats module received a command to clear all collected statistics. +The data is cleared until it receives an update from the modules again. + + + + +STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics + +The stats module received a command to show all statistics that it has +collected. + + + + +STATS_RECEIVED_SHOW_NAME_COMMAND received command to show statistics for %1 + +The stats module received a command to show the statistics that it has +collected for the given item. + + + + +STATS_RECEIVED_SHUTDOWN_COMMAND shutdown command received + +A shutdown command was sent to the stats module and it will now shut down. + + + + +STATS_RECEIVED_STATUS_COMMAND received command to return status + +A status command was sent to the stats module. It will return a +response indicating that it is running normally. + + + + +STATS_RECEIVED_UNKNOWN_COMMAND received unknown command: %1 + +An unknown command has been sent to the stats module. The stats module +will respond with an error and the command will be ignored. + + + + +STATS_SEND_REQUEST_BOSS requesting boss to send statistics + +This debug message is printed when a request is sent to the boss module +to send its data to the stats module. + + + + +STATS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down + +There was a keyboard interrupt signal to stop the stats module. The +daemon will now shut down. + + + + +STATS_UNKNOWN_COMMAND_IN_SPEC unknown command in specification file: %1 + +The specification file for the stats module contains a command that +is unknown in the implementation. The most likely cause is an +installation problem, where the specification file stats.spec is +from a different version of BIND 10 than the stats module itself. +Please check your installation. @@ -2966,7 +4395,7 @@ most likely cause is that the msgq daemon is not running. XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response -There was a problem reading a response from antoher module over the +There was a problem reading a response from another module over the command and control channel. The most likely cause is that the configuration manager b10-cfgmgr is not running. @@ -3047,6 +4476,25 @@ in the code. + +XFROUT_QUERY_DROPPED request to transfer %1/%2 to [%3]:%4 dropped + +The xfrout process silently dropped a request to transfer zone to given host. +This is required by the ACLs. The %1 and %2 represent the zone name and class, +the %3 and %4 the IP address and port of the peer requesting the transfer. + + + + +XFROUT_QUERY_REJECTED request to transfer %1/%2 to [%3]:%4 rejected + +The xfrout process rejected (by REFUSED rcode) a request to transfer zone to +given host. This is because of ACLs. The %1 and %2 represent the zone name and +class, the %3 and %4 the IP address and port of the peer requesting the +transfer. + + + XFROUT_RECEIVED_SHUTDOWN_COMMAND shutdown command received @@ -3120,6 +4568,235 @@ on, but the file is in use. The most likely cause is that another xfrout daemon process is still running. This xfrout daemon (the one printing this message) will not start. + + + +ZONEMGR_CCSESSION_ERROR command channel session error: %1 + +An error was encountered on the command channel. The message indicates +the nature of the error. + + + + +ZONEMGR_JITTER_TOO_BIG refresh_jitter is too big, setting to 0.5 + +The value specified in the configuration for the refresh jitter is too large +so its value has been set to the maximum of 0.5. + + + + +ZONEMGR_KEYBOARD_INTERRUPT exiting zonemgr process as result of keyboard interrupt + +An informational message output when the zone manager was being run at a +terminal and it was terminated via a keyboard interrupt signal. + + + + +ZONEMGR_LOAD_ZONE loading zone %1 (class %2) + +This is a debug message indicating that the zone of the specified class +is being loaded. + + + + +ZONEMGR_NO_MASTER_ADDRESS internal BIND 10 command did not contain address of master + +A command received by the zone manager from the Auth module did not +contain the address of the master server from which a NOTIFY message +was received. This may be due to an internal programming error; please +submit a bug report. + + + + +ZONEMGR_NO_SOA zone %1 (class %2) does not have an SOA record + +When loading the named zone of the specified class the zone manager +discovered that the data did not contain an SOA record. The load has +been abandoned. + + + + +ZONEMGR_NO_TIMER_THREAD trying to stop zone timer thread but it is not running + +An attempt was made to stop the timer thread (used to track when zones +should be refreshed) but it was not running. This may indicate an +internal program error. Please submit a bug report. + + + + +ZONEMGR_NO_ZONE_CLASS internal BIND 10 command did not contain class of zone + +A command received by the zone manager from another BIND 10 module did +not contain the class of the zone on which the zone manager should act. +This may be due to an internal programming error; please submit a +bug report. + + + + +ZONEMGR_NO_ZONE_NAME internal BIND 10 command did not contain name of zone + +A command received by the zone manager from another BIND 10 module did +not contain the name of the zone on which the zone manager should act. +This may be due to an internal programming error; please submit a +bug report. + + + + +ZONEMGR_RECEIVE_NOTIFY received NOTIFY command for zone %1 (class %2) + +This is a debug message indicating that the zone manager has received a +NOTIFY command over the command channel. The command is sent by the Auth +process when it is acting as a slave server for the zone and causes the +zone manager to record the master server for the zone and start a timer; +when the timer expires, the master will be polled to see if it contains +new data. + + + + +ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command + +This is a debug message indicating that the zone manager has received +a SHUTDOWN command over the command channel from the Boss process. +It will act on this command and shut down. + + + + +ZONEMGR_RECEIVE_UNKNOWN received unknown command '%1' + +This is a warning message indicating that the zone manager has received +the stated command over the command channel. The command is not known +to the zone manager and although the command is ignored, its receipt +may indicate an internal error. Please submit a bug report. + + + + +ZONEMGR_RECEIVE_XFRIN_FAILED received XFRIN FAILED command for zone %1 (class %2) + +This is a debug message indicating that the zone manager has received +an XFRIN FAILED command over the command channel. The command is sent +by the Xfrin process when a transfer of zone data into the system has +failed, and causes the zone manager to schedule another transfer attempt. + + + + +ZONEMGR_RECEIVE_XFRIN_SUCCESS received XFRIN SUCCESS command for zone %1 (class %2) + +This is a debug message indicating that the zone manager has received +an XFRIN SUCCESS command over the command channel. The command is sent +by the Xfrin process when the transfer of zone data into the system has +succeeded, and causes the data to be loaded and served by BIND 10. + + + + +ZONEMGR_REFRESH_ZONE refreshing zone %1 (class %2) + +The zone manager is refreshing the named zone of the specified class +with updated information. + + + + +ZONEMGR_SELECT_ERROR error with select(): %1 + +An attempt to wait for input from a socket failed. The failing operation +is a call to the operating system's select() function, which failed for +the given reason. + + + + +ZONEMGR_SEND_FAIL failed to send command to %1, session has been closed + +The zone manager attempted to send a command to the named BIND 10 module, +but the send failed. The session between the modules has been closed. + + + + +ZONEMGR_SESSION_ERROR unable to establish session to command channel daemon + +The zonemgr process was not able to be started because it could not +connect to the command channel daemon. The most usual cause of this +problem is that the daemon is not running. + + + + +ZONEMGR_SESSION_TIMEOUT timeout on session to command channel daemon + +The zonemgr process was not able to be started because it timed out when +connecting to the command channel daemon. The most usual cause of this +problem is that the daemon is not running. + + + + +ZONEMGR_SHUTDOWN zone manager has shut down + +A debug message, output when the zone manager has shut down completely. + + + + +ZONEMGR_STARTING zone manager starting + +A debug message output when the zone manager starts up. + + + + +ZONEMGR_TIMER_THREAD_RUNNING trying to start timer thread but one is already running + +This message is issued when an attempt is made to start the timer +thread (which keeps track of when zones need a refresh) but one is +already running. It indicates either an error in the program logic or +a problem with stopping a previous instance of the timer. Please submit +a bug report. + + + + +ZONEMGR_UNKNOWN_ZONE_FAIL zone %1 (class %2) is not known to the zone manager + +An XFRIN operation has failed but the zone that was the subject of the +operation is not being managed by the zone manager. This may indicate +an error in the program (as the operation should not have been initiated +if this were the case). Please submit a bug report. + + + + +ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1 (class %2) is not known to the zone manager + +A NOTIFY was received but the zone that was the subject of the operation +is not being managed by the zone manager. This may indicate an error +in the program (as the operation should not have been initiated if this +were the case). Please submit a bug report. + + + + +ZONEMGR_UNKNOWN_ZONE_SUCCESS zone %1 (class %2) is not known to the zone manager + +An XFRIN operation has succeeded but the zone received is not being +managed by the zone manager. This may indicate an error in the program +(as the operation should not have been initiated if this were the case). +Please submit a bug report. + diff --git a/src/bin/bind10/bind10.8 b/src/bin/bind10/bind10.8 index d5ab9053b3..1af4f14848 100644 --- a/src/bin/bind10/bind10.8 +++ b/src/bin/bind10/bind10.8 @@ -2,12 +2,12 @@ .\" Title: bind10 .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" Date: March 31, 2011 +.\" Date: August 11, 2011 .\" Manual: BIND10 .\" Source: BIND10 .\" Language: English .\" -.TH "BIND10" "8" "March 31, 2011" "BIND10" "BIND10" +.TH "BIND10" "8" "August 11, 2011" "BIND10" "BIND10" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -107,6 +107,18 @@ Display more about what is going on for \fBbind10\fR and its child processes\&. .RE +.SH "STATISTICS DATA" +.PP +The statistics data collected by the +\fBb10\-stats\fR +daemon include: +.PP +bind10\&.boot_time +.RS 4 +The date and time that the +\fBbind10\fR +process started\&. This is represented in ISO 8601 format\&. +.RE .SH "SEE ALSO" .PP diff --git a/src/bin/resolver/b10-resolver.8 b/src/bin/resolver/b10-resolver.8 index 849092c007..9161ec2eca 100644 --- a/src/bin/resolver/b10-resolver.8 +++ b/src/bin/resolver/b10-resolver.8 @@ -2,12 +2,12 @@ .\" Title: b10-resolver .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" Date: February 17, 2011 +.\" Date: August 17, 2011 .\" Manual: BIND10 .\" Source: BIND10 .\" Language: English .\" -.TH "B10\-RESOLVER" "8" "February 17, 2011" "BIND10" "BIND10" +.TH "B10\-RESOLVER" "8" "August 17, 2011" "BIND10" "BIND10" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -54,7 +54,7 @@ must be either a valid numeric user ID or a valid user name\&. By default the da .PP \fB\-v\fR .RS 4 -Enabled verbose mode\&. This enables diagnostic messages to STDERR\&. +Enable verbose mode\&. This sets logging to the maximum debugging level\&. .RE .SH "CONFIGURATION AND COMMANDS" .PP @@ -77,6 +77,25 @@ string and number\&. The defaults are address ::1 port 53 and address 127\&.0\&.0\&.1 port 53\&. .PP + + + + + +\fIquery_acl\fR +is a list of query access control rules\&. The list items are the +\fIaction\fR +string and the +\fIfrom\fR +or +\fIkey\fR +strings\&. The possible actions are ACCEPT, REJECT and DROP\&. The +\fIfrom\fR +is a remote (source) IPv4 or IPv6 address or special keyword\&. The +\fIkey\fR +is a TSIG key name\&. The default configuration accepts queries from 127\&.0\&.0\&.1 and ::1\&. +.PP + \fIretries\fR is the number of times to retry (resend query) after a query timeout (\fItimeout_query\fR)\&. The default is 3\&. .PP @@ -88,7 +107,7 @@ to use directly as root servers to start resolving\&. The list items are the \fIaddress\fR string and \fIport\fR -number\&. If empty, a hardcoded address for F\-root (192\&.5\&.5\&.241) is used\&. +number\&. By default, a hardcoded address for l\&.root\-servers\&.net (199\&.7\&.83\&.42 or 2001:500:3::42) is used\&. .PP \fItimeout_client\fR @@ -121,8 +140,7 @@ BIND 10 Guide\&. .PP The \fBb10\-resolver\fR -daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&. Iteration was introduced in January 2011\&. - +daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&. Iteration was introduced in January 2011\&. Caching was implemented in February 2011\&. Access control was introduced in June 2011\&. .SH "COPYRIGHT" .br Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC") diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8 index f69e4d37fa..b88af6ccca 100644 --- a/src/bin/stats/b10-stats.8 +++ b/src/bin/stats/b10-stats.8 @@ -1,22 +1,13 @@ '\" t .\" Title: b10-stats .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] -.\" Generator: DocBook XSL Stylesheets v1.76.1 -.\" Date: Oct 15, 2010 +.\" Generator: DocBook XSL Stylesheets v1.75.2 +.\" Date: August 11, 2011 .\" Manual: BIND10 .\" Source: BIND10 .\" Language: English .\" -.TH "B10\-STATS" "8" "Oct 15, 2010" "BIND10" "BIND10" -.\" ----------------------------------------------------------------- -.\" * Define some portability stuff -.\" ----------------------------------------------------------------- -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" http://bugs.debian.org/507673 -.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' +.TH "B10\-STATS" "8" "August 11, 2011" "BIND10" "BIND10" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -47,7 +38,7 @@ and so on\&. It waits for coming data from other modules, then other modules sen \fBb10\-stats\fR invokes "sendstats" command for \fBbind10\fR -after its initial starting because it\*(Aqs sure to collect statistics data from +after its initial starting because it\'s sure to collect statistics data from \fBbind10\fR\&. .SH "OPTIONS" .PP @@ -59,6 +50,84 @@ This \fBb10\-stats\fR switches to verbose mode\&. It sends verbose messages to STDOUT\&. .RE +.SH "CONFIGURATION AND COMMANDS" +.PP +The +\fBb10\-stats\fR +command does not have any configurable settings\&. +.PP +The configuration commands are: +.PP + + +\fBremove\fR +removes the named statistics name and data\&. +.PP + + +\fBreset\fR +will reset all statistics data to default values except for constant names\&. This may re\-add previously removed statistics names\&. +.PP + +\fBset\fR +.PP + +\fBshow\fR +will send the statistics data in JSON format\&. By default, it outputs all the statistics data it has collected\&. An optional item name may be specified to receive individual output\&. +.PP + +\fBshutdown\fR +will shutdown the +\fBb10\-stats\fR +process\&. (Note that the +\fBbind10\fR +parent may restart it\&.) +.PP + +\fBstatus\fR +simply indicates that the daemon is running\&. +.SH "STATISTICS DATA" +.PP +The +\fBb10\-stats\fR +daemon contains these statistics: +.PP +report_time +.RS 4 +The latest report date and time in ISO 8601 format\&. +.RE +.PP +stats\&.boot_time +.RS 4 +The date and time when this daemon was started in ISO 8601 format\&. This is a constant which can\'t be reset except by restarting +\fBb10\-stats\fR\&. +.RE +.PP +stats\&.last_update_time +.RS 4 +The date and time (in ISO 8601 format) when this daemon last received data from another component\&. +.RE +.PP +stats\&.lname +.RS 4 +This is the name used for the +\fBb10\-msgq\fR +command\-control channel\&. (This is a constant which can\'t be reset except by restarting +\fBb10\-stats\fR\&.) +.RE +.PP +stats\&.start_time +.RS 4 +This is the date and time (in ISO 8601 format) when this daemon started collecting data\&. +.RE +.PP +stats\&.timestamp +.RS 4 +The current date and time represented in seconds since UNIX epoch (1970\-01\-01T0 0:00:00Z) with precision (delimited with a period) up to one hundred thousandth of second\&. +.RE +.PP +See other manual pages for explanations for their statistics that are kept track by +\fBb10\-stats\fR\&. .SH "FILES" .PP /usr/local/share/bind10\-devel/stats\&.spec @@ -82,7 +151,7 @@ BIND 10 Guide\&. .PP The \fBb10\-stats\fR -daemon was initially designed and implemented by Naoki Kambe of JPRS in Oct 2010\&. +daemon was initially designed and implemented by Naoki Kambe of JPRS in October 2010\&. .SH "COPYRIGHT" .br Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC") diff --git a/src/bin/xfrin/b10-xfrin.8 b/src/bin/xfrin/b10-xfrin.8 index 3ea2293a8a..7f73213a0d 100644 --- a/src/bin/xfrin/b10-xfrin.8 +++ b/src/bin/xfrin/b10-xfrin.8 @@ -71,6 +71,9 @@ is a list of zones known to the daemon\&. The list items are: \fIname\fR (the zone name), +\fIclass\fR +(defaults to +\(lqIN\(rq), \fImaster_addr\fR (the zone master to transfer from), \fImaster_port\fR @@ -125,7 +128,7 @@ to define the class (defaults to \fImaster\fR to define the IP address of the authoritative server to transfer from, and \fIport\fR -to define the port number on the authoritative server (defaults to 53)\&. If the address or port is not specified, it will use the values previously defined in the +to define the port number on the authoritative server (defaults to 53)\&. If the address or port is not specified, it will use the value previously defined in the \fIzones\fR configuration\&. .PP From 507b231626a2e0289288f48b1e4613b569cdd8b2 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 17 Aug 2011 10:01:47 -0500 Subject: [PATCH 574/974] [master] use a tab before the keyword type. to be consistent --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index dc701e3af8..a3c702e037 100644 --- a/ChangeLog +++ b/ChangeLog @@ -37,7 +37,7 @@ returns is str or byte. (Trac #1021, git 486bf91e0ecc5fbecfe637e1e75ebe373d42509b) -273. [func] vorner +273. [func] vorner It is possible to specify ACL for the xfrout module. It is in the ACL configuration key and has the usual ACL syntax. It currently supports only the source address. Default ACL accepts everything. From 540c6a5f5b25d935a8193fd835c1ba83dba02fd5 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 17 Aug 2011 10:12:52 -0500 Subject: [PATCH 575/974] [master] add Dima to authors.bind --- src/lib/datasrc/static_datasrc.cc | 1 + src/lib/datasrc/tests/static_unittest.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc index 65229a05d0..fd43e1ca6d 100644 --- a/src/lib/datasrc/static_datasrc.cc +++ b/src/lib/datasrc/static_datasrc.cc @@ -70,6 +70,7 @@ StaticDataSrcImpl::StaticDataSrcImpl() : authors = RRsetPtr(new RRset(authors_name, RRClass::CH(), RRType::TXT(), RRTTL(0))); authors->addRdata(generic::TXT("Chen Zhengzhang")); // Jerry + authors->addRdata(generic::TXT("Dmitriy Volodin")); authors->addRdata(generic::TXT("Evan Hunt")); authors->addRdata(generic::TXT("Haidong Wang")); // Ocean authors->addRdata(generic::TXT("Han Feng")); diff --git a/src/lib/datasrc/tests/static_unittest.cc b/src/lib/datasrc/tests/static_unittest.cc index a11e889f1e..4c9fe42edb 100644 --- a/src/lib/datasrc/tests/static_unittest.cc +++ b/src/lib/datasrc/tests/static_unittest.cc @@ -53,6 +53,7 @@ protected: // NOTE: in addition, the order of the following items matter. authors_data.push_back("Chen Zhengzhang"); + authors_data.push_back("Dmitriy Volodin"); authors_data.push_back("Evan Hunt"); authors_data.push_back("Haidong Wang"); authors_data.push_back("Han Feng"); From 2ecb4add323e3c4ba56641d28e35dd79013ff9cf Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 17 Aug 2011 17:31:23 +0200 Subject: [PATCH 576/974] [master] add a late changelog entry for trac #926 --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index a3c702e037..271db41e3b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +281. [func] jelte + Added a new type for configuration data: "named set". This allows for + similar configuration as the current "list" type, but with strings + instead of indices as identifiers. The intended use is for instance + /foo/zones/example.org/bar instead of /foo/zones[2]/bar. Currently this + new type is not in use yet. + (Trac #926, git 06aeefc4787c82db7f5443651f099c5af47bd4d6) + 280. [func] jerry libdns++: Implement the MINFO rrtype according to RFC1035. (Trac #1113, git 7a9a19d6431df02d48a7bc9de44f08d9450d3a37) From 58d6de47f6e189ff0b648b4f2f74e6d5df85d749 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 17 Aug 2011 10:37:56 -0500 Subject: [PATCH 577/974] [master] fix long line; added a timestamp for future release --- ChangeLog | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 271db41e3b..02076a1e02 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,11 @@ +bind10-devel-20110819 released on August 19, 2011 + 281. [func] jelte Added a new type for configuration data: "named set". This allows for similar configuration as the current "list" type, but with strings instead of indices as identifiers. The intended use is for instance - /foo/zones/example.org/bar instead of /foo/zones[2]/bar. Currently this - new type is not in use yet. + /foo/zones/example.org/bar instead of /foo/zones[2]/bar. Currently + this new type is not in use yet. (Trac #926, git 06aeefc4787c82db7f5443651f099c5af47bd4d6) 280. [func] jerry From 2bd6dc4ac6ac61705517df297320fa79b308b9e3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 17 Aug 2011 10:31:29 -0700 Subject: [PATCH 578/974] [1068] implemented deleting RRset --- src/lib/datasrc/database.cc | 47 ++++- src/lib/datasrc/database.h | 8 + src/lib/datasrc/tests/database_unittest.cc | 217 ++++++++++++++++++++- src/lib/datasrc/zone.h | 5 + 4 files changed, 265 insertions(+), 12 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index a7d63c5803..5631bda1d9 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -338,7 +338,8 @@ DatabaseClient::Updater::Updater(shared_ptr accessor, db_name_(accessor->getDBName()), zone_name_(zone_name), zone_class_(zone_class), finder_(new Finder(accessor_, zone_id_)), - add_columns_(DatabaseAccessor::ADD_COLUMN_COUNT) + add_columns_(DatabaseAccessor::ADD_COLUMN_COUNT), + del_params_(DatabaseAccessor::DEL_PARAM_COUNT) { logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED) .arg(zone_name_).arg(zone_class_).arg(db_name_); @@ -379,12 +380,12 @@ DatabaseClient::Updater::addRRset(const RRset& rrset) { << rrset.getType()); } - add_columns_.clear(); - add_columns_[DatabaseAccessor::ADD_NAME] = rrset.getName().toText(); - add_columns_[DatabaseAccessor::ADD_REV_NAME] = + add_columns_.assign(DatabaseAccessor::ADD_COLUMN_COUNT, ""); + add_columns_.at(DatabaseAccessor::ADD_NAME) = rrset.getName().toText(); + add_columns_.at(DatabaseAccessor::ADD_REV_NAME) = rrset.getName().reverse().toText(); - add_columns_[DatabaseAccessor::ADD_TTL] = rrset.getTTL().toText(); - add_columns_[DatabaseAccessor::ADD_TYPE] = rrset.getType().toText(); + add_columns_.at(DatabaseAccessor::ADD_TTL) = rrset.getTTL().toText(); + add_columns_.at(DatabaseAccessor::ADD_TYPE) = rrset.getType().toText(); for (; !it->isLast(); it->next()) { if (rrset.getType() == RRType::RRSIG()) { // XXX: the current interface (based on the current sqlite3 @@ -394,14 +395,44 @@ DatabaseClient::Updater::addRRset(const RRset& rrset) { // the interface, but until then we have to conform to the schema. const generic::RRSIG& rrsig_rdata = dynamic_cast(it->getCurrent()); - add_columns_[DatabaseAccessor::ADD_SIGTYPE] = + add_columns_.at(DatabaseAccessor::ADD_SIGTYPE) = rrsig_rdata.typeCovered().toText(); } - add_columns_[DatabaseAccessor::ADD_RDATA] = it->getCurrent().toText(); + add_columns_.at(DatabaseAccessor::ADD_RDATA) = + it->getCurrent().toText(); accessor_->addRecordToZone(add_columns_); } } +void +DatabaseClient::Updater::deleteRRset(const RRset& rrset) { + if (committed_) { + isc_throw(DataSourceError, "Delete attempt after commit on zone: " + << zone_name_ << "/" << zone_class_); + } + if (rrset.getClass() != zone_class_) { + isc_throw(DataSourceError, "An RRset of a different class is being " + << "deleted from " << zone_name_ << "/" << zone_class_ + << ": " << rrset.toText()); + } + + RdataIteratorPtr it = rrset.getRdataIterator(); + if (it->isLast()) { + isc_throw(DataSourceError, "An empty RRset is being deleted for " + << rrset.getName() << "/" << zone_class_ << "/" + << rrset.getType()); + } + + del_params_.assign(DatabaseAccessor::DEL_PARAM_COUNT, ""); + del_params_.at(DatabaseAccessor::DEL_NAME) = rrset.getName().toText(); + del_params_.at(DatabaseAccessor::DEL_TYPE) = rrset.getType().toText(); + for (; !it->isLast(); it->next()) { + del_params_.at(DatabaseAccessor::DEL_RDATA) = + it->getCurrent().toText(); + accessor_->deleteRecordInZone(del_params_); + } +} + void DatabaseClient::Updater::commit() { if (committed_) { diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 3a9292cc50..4de7e2f279 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -165,6 +165,12 @@ public: ADD_RDATA = 5 ///< Full text representation of the record's RDATA }; + enum DeleteRecordParams { + DEL_NAME = 0, ///< The owner name of the record (a domain name) + DEL_TYPE = 1, ///< The RRType of the record (A/NS/TXT etc.) + DEL_RDATA = 2 ///< Full text representation of the record's RDATA + }; + /// TBD virtual std::pair startUpdateZone(const std::string& zone_name, bool replace) = 0; @@ -362,6 +368,7 @@ public: ~Updater(); virtual ZoneFinder& getFinder(); virtual void addRRset(const isc::dns::RRset& rrset); + virtual void deleteRRset(const isc::dns::RRset& rrset); virtual void commit(); private: @@ -373,6 +380,7 @@ public: isc::dns::RRClass zone_class_; boost::scoped_ptr finder_; std::vector add_columns_; + std::vector del_params_; }; /** diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 570118904e..0eeb5c6d99 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -28,7 +28,10 @@ #include +#include #include +#include +#include using namespace isc::datasrc; using namespace std; @@ -158,7 +161,33 @@ public: // remember this one so that test cases can check it. columns_lastadded = columns; } - virtual void deleteRecordInZone(const vector&) {} + + // Helper predicate class used in deleteRecordInZone(). + struct deleteMatch { + deleteMatch(const string& type, const string& rdata) : + type_(type), rdata_(rdata) + {} + bool operator()(const vector& row) const { + return (row[0] == type_ && row[3] == rdata_); + } + const string& type_; + const string& rdata_; + }; + + virtual void deleteRecordInZone(const vector& params) { + vector > records = + update_records[params[DatabaseAccessor::DEL_NAME]]; + records.erase(remove_if(records.begin(), records.end(), + deleteMatch( + params[DatabaseAccessor::DEL_TYPE], + params[DatabaseAccessor::DEL_RDATA])), + records.end()); + if (records.empty()) { + update_records.erase(params[DatabaseAccessor::DEL_NAME]); + } else { + update_records[params[DatabaseAccessor::DEL_NAME]] = records; + } + } bool searchRunning() const { return (search_running_); @@ -356,7 +385,7 @@ protected: rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); // Adding an IN/A RDATA. Intentionally using different data - // than the initial data configured MockAccessor::fillData(). + // than the initial data configured in MockAccessor::fillData(). rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), "192.0.2.2")); @@ -931,6 +960,24 @@ TEST_F(DatabaseClientTest, addRRsetToCurrentZone) { } } +TEST_F(DatabaseClientTest, addMultipleRRs) { + // Similar to the previous case, but the added RRset contains multiple + // RRs. + updater = client_->startUpdateZone(Name("example.org"), false); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "192.0.2.3")); + updater->addRRset(*rrset); + expected_rdatas.clear(); + expected_rdatas.push_back("192.0.2.1"); + expected_rdatas.push_back("192.0.2.2"); + expected_rdatas.push_back("192.0.2.3"); + { + SCOPED_TRACE("add multiple RRs"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } +} + TEST_F(DatabaseClientTest, addRRsetOfLargerTTL) { // Similar to the previous one, but the TTL of the added RRset is larger // than that of the existing record. The finder should use the smaller @@ -1025,7 +1072,169 @@ TEST_F(DatabaseClientTest, addAfterCommit) { EXPECT_THROW(updater->addRRset(*rrset), DataSourceError); } -// delete rrset without rdata; not necessarily harmful, but treat it as an error. -// delete after commit. should error +TEST_F(DatabaseClientTest, deleteRRset) { + rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "192.0.2.1")); + // Delete one RR from an RRset + updater = client_->startUpdateZone(Name("example.org"), false); + updater->deleteRRset(*rrset); + + // Delete the only RR of a name + rrset.reset(new RRset(Name("cname.example.org"), RRClass::IN(), + RRType::CNAME(), rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "www.example.org")); + updater->deleteRRset(*rrset); + + // The updater finder should immediately see the deleted results. + { + SCOPED_TRACE("delete RRset"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::NXRRSET, empty_rdatas, empty_rdatas); + doFindTest(updater->getFinder(), Name("cname.example.org"), + qtype, qtype, rrttl, ZoneFinder::NXDOMAIN, + empty_rdatas, empty_rdatas); + } + + // before committing the change, the original finder should see the + // original record. + { + SCOPED_TRACE("delete RRset before commit"); + expected_rdatas.push_back("192.0.2.1"); + doFindTest(*finder, qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + + expected_rdatas.clear(); + expected_rdatas.push_back("www.example.org."); + doFindTest(*finder, Name("cname.example.org"), qtype, + RRType::CNAME(), rrttl, + ZoneFinder::CNAME, expected_rdatas, empty_rdatas); + } + + // once committed, the record should be removed from the original finder's + // view, too. + updater->commit(); + { + SCOPED_TRACE("delete RRset after commit"); + doFindTest(*finder, qname, qtype, qtype, rrttl, + ZoneFinder::NXRRSET, empty_rdatas, empty_rdatas); + doFindTest(*finder, Name("cname.example.org"), + qtype, qtype, rrttl, ZoneFinder::NXDOMAIN, + empty_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, deleteRRsetToNXDOMAIN) { + // similar to the previous case, but it removes the only record of the + // given name. a subsequent find() should result in NXDOMAIN. + rrset.reset(new RRset(Name("cname.example.org"), RRClass::IN(), + RRType::CNAME(), rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "www.example.org")); + + updater = client_->startUpdateZone(Name("example.org"), false); + updater->deleteRRset(*rrset); + { + SCOPED_TRACE("delete RRset to NXDOMAIN"); + doFindTest(updater->getFinder(), Name("cname.example.org"), qtype, + qtype, rrttl, ZoneFinder::NXDOMAIN, empty_rdatas, + empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, deleteMultipleRRs) { + rrset.reset(new RRset(qname, RRClass::IN(), RRType::AAAA(), rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "2001:db8::1")); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "2001:db8::2")); + + updater = client_->startUpdateZone(Name("example.org"), false); + updater->deleteRRset(*rrset); + + { + SCOPED_TRACE("delete multiple RRs"); + doFindTest(updater->getFinder(), qname, RRType::AAAA(), qtype, rrttl, + ZoneFinder::NXRRSET, empty_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, partialDelete) { + rrset.reset(new RRset(qname, RRClass::IN(), RRType::AAAA(), rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "2001:db8::1")); + // This does not exist in the test data source: + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "2001:db8::3")); + + // deleteRRset should succeed "silently", and subsequent find() should + // find the remaining RR. + updater = client_->startUpdateZone(Name("example.org"), false); + updater->deleteRRset(*rrset); + { + SCOPED_TRACE("partial delete"); + expected_rdatas.push_back("2001:db8::2"); + doFindTest(updater->getFinder(), qname, RRType::AAAA(), + RRType::AAAA(), rrttl, ZoneFinder::SUCCESS, + expected_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, deleteNoMatch) { + // similar to the previous test, but there's not even a match in the + // specified RRset. Essentially there's no difference in the result. + updater = client_->startUpdateZone(Name("example.org"), false); + updater->deleteRRset(*rrset); + { + SCOPED_TRACE("delete no match"); + expected_rdatas.push_back("192.0.2.1"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, deleteWithDifferentTTL) { + // Our delete interface simply ignores TTL (may change in a future version) + rrset.reset(new RRset(qname, RRClass::IN(), qtype, RRTTL(1800))); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "192.0.2.1")); + updater = client_->startUpdateZone(Name("example.org"), false); + updater->deleteRRset(*rrset); + { + SCOPED_TRACE("delete RRset with a different TTL"); + doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, + ZoneFinder::NXRRSET, empty_rdatas, empty_rdatas); + } +} + +TEST_F(DatabaseClientTest, deleteDeviantRR) { + updater = client_->startUpdateZone(Name("example.org"), false); + + // RR class mismatch. This should be detected and rejected. + rrset.reset(new RRset(qname, RRClass::CH(), RRType::TXT(), rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "test text")); + EXPECT_THROW(updater->deleteRRset(*rrset), DataSourceError); + + // Out-of-zone owner name. At a higher level this should be rejected, + // but it doesn't happen in this interface. + rrset.reset(new RRset(Name("example.com"), RRClass::IN(), qtype, rrttl)); + rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), + "192.0.2.100")); + EXPECT_NO_THROW(updater->deleteRRset(*rrset)); +} + +TEST_F(DatabaseClientTest, deleteAfterCommit) { + updater = client_->startUpdateZone(Name("example.org"), false); + updater->commit(); + EXPECT_THROW(updater->deleteRRset(*rrset), DataSourceError); +} + +TEST_F(DatabaseClientTest, deleteEmptyRRset) { + updater = client_->startUpdateZone(Name("example.org"), false); + rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); + EXPECT_THROW(updater->deleteRRset(*rrset), DataSourceError); +} } diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 5315942d5f..a2648993d6 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -237,6 +237,11 @@ public: /// This method must not be called once commit() is performed. virtual void addRRset(const isc::dns::RRset& rrset) = 0; + /// TBD + /// + /// how to handle TTL? + virtual void deleteRRset(const isc::dns::RRset& rrset) = 0; + /// TBD /// /// This operation can only be performed at most once. A duplicate call From 92dcac243a4a2924bab85d1519a0c7a20853f9cc Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 17 Aug 2011 12:15:42 -0700 Subject: [PATCH 579/974] [1068] a major refactoring: templated DatabaseClientTest using the gtest's typed test feature so that we can share (most of) the tests for various types of database backends. --- src/lib/datasrc/tests/database_unittest.cc | 1051 +++++++++++--------- 1 file changed, 557 insertions(+), 494 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 0eeb5c6d99..b8800ab9ba 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -371,9 +371,13 @@ private: } }; +template class DatabaseClientTest : public ::testing::Test { protected: - DatabaseClientTest() : qname("www.example.org"), qtype("A"), rrttl(3600) { + DatabaseClientTest() : zname("example.org"), qname("www.example.org"), + qclass(RRClass::IN()), qtype(RRType::A()), + rrttl(3600) + { createClient(); // set up the commonly used finder. @@ -383,13 +387,13 @@ protected: finder = dynamic_pointer_cast( zone.zone_finder); - rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); + rrset.reset(new RRset(qname, qclass, qtype, rrttl)); // Adding an IN/A RDATA. Intentionally using different data // than the initial data configured in MockAccessor::fillData(). rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), "192.0.2.2")); - rrsigset.reset(new RRset(qname, RRClass::IN(), RRType::RRSIG(), + rrsigset.reset(new RRset(qname, qclass, RRType::RRSIG(), rrttl)); rrsigset->addRdata(rdata::createRdata(rrsigset->getType(), rrsigset->getClass(), @@ -402,20 +406,58 @@ protected: * times per test. */ void createClient() { - current_accessor_ = new MockAccessor(); - client_.reset(new DatabaseClient(RRClass::IN(), + current_accessor_ = new ACCESSOR_TYPE(); + client_.reset(new DatabaseClient(qclass, shared_ptr( current_accessor_))); } + + bool searchRunning(bool expected = false) const { + try { + const MockAccessor* mock_accessor = + dynamic_cast(current_accessor_); + return (mock_accessor->searchRunning()); + } catch (const bad_cast&) { + return (expected); + } + } + + bool isRollbacked(bool expected = false) const { + try { + const MockAccessor* mock_accessor = + dynamic_cast(current_accessor_); + return (mock_accessor->isRollbacked()); + } catch (const bad_cast&) { + return (expected); + } + } + + void checkLastAdded(const char* const expected[]) const { + try { + const MockAccessor* mock_accessor = + dynamic_cast(current_accessor_); + int i = 0; + BOOST_FOREACH(const string& column, + mock_accessor->getLastAdded()) { + EXPECT_EQ(expected[i++], column); + } + } catch (const bad_cast&) { + ; + } + } + // Will be deleted by client_, just keep the current value for comparison. - MockAccessor* current_accessor_; + //MockAccessor* current_accessor_; + DatabaseAccessor* current_accessor_; shared_ptr client_; const std::string database_name_; // The zone finder of the test zone commonly used in various tests. shared_ptr finder; + const Name zname; // the zone name stored in the test data source const Name qname; // commonly used name to be found + const RRClass qclass; // commonly used RR class with qname const RRType qtype; // commonly used RR type with qname const RRTTL rrttl; // commonly used RR TTL RRsetPtr rrset; // for adding/deleting an RRset @@ -441,46 +483,51 @@ protected: } }; -TEST_F(DatabaseClientTest, zoneNotFound) { - DataSourceClient::FindResult zone(client_->findZone(Name("example.com"))); +typedef ::testing::Types TestAccessorTypes; +TYPED_TEST_CASE(DatabaseClientTest, TestAccessorTypes); + +TYPED_TEST(DatabaseClientTest, zoneNotFound) { + DataSourceClient::FindResult zone( + this->client_->findZone(Name("example.com"))); EXPECT_EQ(result::NOTFOUND, zone.code); } -TEST_F(DatabaseClientTest, exactZone) { - DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); +TYPED_TEST(DatabaseClientTest, exactZone) { + DataSourceClient::FindResult zone( this->client_->findZone(this->zname)); EXPECT_EQ(result::SUCCESS, zone.code); - checkZoneFinder(zone); + this->checkZoneFinder(zone); } -TEST_F(DatabaseClientTest, superZone) { - DataSourceClient::FindResult zone(client_->findZone(Name( +TYPED_TEST(DatabaseClientTest, superZone) { + DataSourceClient::FindResult zone(this->client_->findZone(Name( "sub.example.org"))); EXPECT_EQ(result::PARTIALMATCH, zone.code); - checkZoneFinder(zone); + this->checkZoneFinder(zone); } -TEST_F(DatabaseClientTest, noAccessorException) { +// This test doesn't depend on derived accessor class, so we use TEST(). +TEST(GenericDatabaseClientTest, noAccessorException) { EXPECT_THROW(DatabaseClient(RRClass::IN(), shared_ptr()), isc::InvalidParameter); } -TEST_F(DatabaseClientTest, startUpdate) { +TYPED_TEST(DatabaseClientTest, startUpdate) { // startUpdate will succeed only when there's an exact match zone. EXPECT_EQ(ZoneUpdaterPtr(), - client_->startUpdateZone(Name("example.com"), false)); + this->client_->startUpdateZone(Name("example.com"), false)); EXPECT_NE(ZoneUpdaterPtr(), - client_->startUpdateZone(Name("example.org"), false)); + this->client_->startUpdateZone(this->zname, false)); + EXPECT_EQ(ZoneUpdaterPtr(), - client_->startUpdateZone(Name("sub.example.org"), false)); + this->client_->startUpdateZone(Name("sub.example.org"), false)); } -namespace { // checks if the given rrset matches the // given name, class, type and rdatas void checkRRset(isc::dns::ConstRRsetPtr rrset, - const isc::dns::Name& name, + const Name& name, const isc::dns::RRClass& rrclass, const isc::dns::RRType& rrtype, const isc::dns::RRTTL& rrttl, @@ -497,7 +544,7 @@ checkRRset(isc::dns::ConstRRsetPtr rrset, void doFindTest(ZoneFinder& finder, - const isc::dns::Name& name, + const Name& name, const isc::dns::RRType& type, const isc::dns::RRType& expected_type, const isc::dns::RRTTL expected_ttl, @@ -523,718 +570,734 @@ doFindTest(ZoneFinder& finder, EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset); } } -} // end anonymous namespace -TEST_F(DatabaseClientTest, find) { - EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); - EXPECT_FALSE(current_accessor_->searchRunning()); +TYPED_TEST(DatabaseClientTest, find) { + EXPECT_EQ(READONLY_ZONE_ID, this->finder->zone_id()); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - doFindTest(*finder, isc::dns::Name("www.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + doFindTest(*this->finder, Name("www.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); + + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_rdatas.push_back("192.0.2.2"); + doFindTest(*this->finder, Name("www2.example.org."), + this->qtype, this->qtype, + this->rrttl, ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); - doFindTest(*finder, isc::dns::Name("www2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); - - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("2001:db8::1"); - expected_rdatas.push_back("2001:db8::2"); - doFindTest(*finder, isc::dns::Name("www.example.org."), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("2001:db8::1"); + this->expected_rdatas.push_back("2001:db8::2"); + doFindTest(*this->finder, Name("www.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), + this->rrttl, ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - doFindTest(*finder, isc::dns::Name("www.example.org."), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + doFindTest(*this->finder, Name("www.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), - isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->rrttl, ZoneFinder::NXRRSET, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); - doFindTest(*finder, isc::dns::Name("cname.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::CNAME(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("www.example.org."); + doFindTest(*this->finder, Name("cname.example.org."), + this->qtype, isc::dns::RRType::CNAME(), this->rrttl, ZoneFinder::CNAME, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); - doFindTest(*finder, isc::dns::Name("cname.example.org."), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("www.example.org."); + doFindTest(*this->finder, Name("cname.example.org."), isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - doFindTest(*finder, isc::dns::Name("doesnotexist.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::NXDOMAIN, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + doFindTest(*this->finder, Name("doesnotexist.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::NXDOMAIN, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("signed1.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("signed1.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("2001:db8::1"); - expected_rdatas.push_back("2001:db8::2"); - expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("signed1.example.org."), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("2001:db8::1"); + this->expected_rdatas.push_back("2001:db8::2"); + this->expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("signed1.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - doFindTest(*finder, isc::dns::Name("signed1.example.org."), - isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + doFindTest(*this->finder, Name("signed1.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), this->rrttl, ZoneFinder::NXRRSET, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); - expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("signedcname1.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::CNAME(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("www.example.org."); + this->expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("signedcname1.example.org."), + this->qtype, isc::dns::RRType::CNAME(), this->rrttl, ZoneFinder::CNAME, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("signed2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("signed2.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); + + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("2001:db8::2"); + this->expected_rdatas.push_back("2001:db8::1"); + this->expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("signed2.example.org."), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), this->rrttl, ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("2001:db8::2"); - expected_rdatas.push_back("2001:db8::1"); - expected_sig_rdatas.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("signed2.example.org."), - isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); - - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - doFindTest(*finder, isc::dns::Name("signed2.example.org."), - isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + doFindTest(*this->finder, Name("signed2.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), this->rrttl, ZoneFinder::NXRRSET, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); - expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("signedcname2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::CNAME(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("www.example.org."); + this->expected_sig_rdatas.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("signedcname2.example.org."), + this->qtype, isc::dns::RRType::CNAME(), this->rrttl, ZoneFinder::CNAME, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("acnamesig1.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("acnamesig1.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); + + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("acnamesig2.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); + + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("acnamesig3.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); + + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_rdatas.push_back("192.0.2.2"); + doFindTest(*this->finder, Name("ttldiff1.example.org."), + this->qtype, this->qtype, isc::dns::RRTTL(360), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("acnamesig2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_rdatas.push_back("192.0.2.2"); + doFindTest(*this->finder, Name("ttldiff2.example.org."), + this->qtype, this->qtype, isc::dns::RRTTL(360), ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("acnamesig3.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); - - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); - doFindTest(*finder, isc::dns::Name("ttldiff1.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(360), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); - - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); - doFindTest(*finder, isc::dns::Name("ttldiff2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(360), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); - - EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_THROW(this->finder->find(Name("badcname1.example.org."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("badcname2.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("badcname2.example.org."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("badcname3.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("badcname3.example.org."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("badrdata.example.org."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("badtype.example.org."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("badttl.example.org."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("badsig.example.org."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("badsig.example.org."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); + EXPECT_FALSE(this->searchRunning()); // Trigger the hardcoded exceptions and see if find() has cleaned up - EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_THROW(this->finder->find(Name("dsexception.in.search."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("iscexception.in.search."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find( + Name("basicexception.in.search."), + this->qtype, NULL, ZoneFinder::FIND_DEFAULT), std::exception); - EXPECT_FALSE(current_accessor_->searchRunning()); + EXPECT_FALSE(this->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_THROW(this->finder->find(Name("dsexception.in.getnext."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("iscexception.in.getnext."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_accessor_->searchRunning()); - EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("basicexception.in.getnext."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), std::exception); - EXPECT_FALSE(current_accessor_->searchRunning()); + EXPECT_FALSE(this->searchRunning()); // This RRSIG has the wrong sigtype field, which should be // an error if we decide to keep using that field // Right now the field is ignored, so it does not error - expected_rdatas.clear(); - expected_sig_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(*finder, isc::dns::Name("badsigtype.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas, expected_sig_rdatas); - EXPECT_FALSE(current_accessor_->searchRunning()); + this->expected_rdatas.clear(); + this->expected_sig_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + doFindTest(*this->finder, Name("badsigtype.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); + EXPECT_FALSE(this->searchRunning()); } -TEST_F(DatabaseClientTest, updaterFinder) { - updater = client_->startUpdateZone(Name("example.org"), false); - ASSERT_TRUE(updater); +TYPED_TEST(DatabaseClientTest, updaterFinder) { + this->updater = this->client_->startUpdateZone(this->zname, false); + ASSERT_TRUE(this->updater); // If this update isn't replacing the zone, the finder should work // just like the normal find() case. EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( - updater->getFinder()).zone_id()); - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - doFindTest(updater->getFinder(), Name("www.example.org."), - qtype, qtype, rrttl, ZoneFinder::SUCCESS, - expected_rdatas, empty_rdatas); + this->updater->getFinder()).zone_id()); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + doFindTest(this->updater->getFinder(), Name("www.example.org."), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); // When replacing the zone, the updater's finder shouldn't see anything // in the zone until something is added. - updater = client_->startUpdateZone(Name("example.org"), true); - ASSERT_TRUE(updater); + this->updater = this->client_->startUpdateZone(this->zname, true); + ASSERT_TRUE(this->updater); EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( - updater->getFinder()).zone_id()); - doFindTest(updater->getFinder(), Name("www.example.org."), + this->updater->getFinder()).zone_id()); + doFindTest(this->updater->getFinder(), Name("www.example.org."), RRType::A(), RRType::A(), RRTTL(3600), ZoneFinder::NXDOMAIN, - empty_rdatas, empty_rdatas); + this->empty_rdatas, this->empty_rdatas); } -TEST_F(DatabaseClientTest, flushZone) { +TYPED_TEST(DatabaseClientTest, flushZone) { // A simple update case: flush the entire zone // Before update, the name exists. - ZoneFinderPtr finder = client_->findZone(Name("example.org")).zone_finder; - EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname, qtype).code); + ZoneFinderPtr finder = this->client_->findZone(this->zname).zone_finder; + EXPECT_EQ(ZoneFinder::SUCCESS, this->finder->find(this->qname, + this->qtype).code); // start update in the replace mode. the normal finder should still // be able to see the record, but the updater's finder shouldn't. - updater = client_->startUpdateZone(Name("example.org"), true); - EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname, qtype).code); + this->updater = this->client_->startUpdateZone(this->zname, true); + EXPECT_EQ(ZoneFinder::SUCCESS, this->finder->find(this->qname, + this->qtype).code); EXPECT_EQ(ZoneFinder::NXDOMAIN, - updater->getFinder().find(qname, qtype).code); + this->updater->getFinder().find(this->qname, this->qtype).code); // commit the update. now the normal finder shouldn't see it. - updater->commit(); - EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(qname, qtype).code); + this->updater->commit(); + EXPECT_EQ(ZoneFinder::NXDOMAIN, this->finder->find(this->qname, + this->qtype).code); // Check rollback wasn't accidentally performed. - EXPECT_FALSE(current_accessor_->isRollbacked()); + EXPECT_FALSE(this->isRollbacked()); } -TEST_F(DatabaseClientTest, updateCancel) { + +TYPED_TEST(DatabaseClientTest, updateCancel) { // similar to the previous test, but destruct the updater before commit. - ZoneFinderPtr finder = client_->findZone(Name("example.org")).zone_finder; - EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname, qtype).code); + ZoneFinderPtr finder = this->client_->findZone(this->zname).zone_finder; + EXPECT_EQ(ZoneFinder::SUCCESS, this->finder->find(this->qname, + this->qtype).code); - updater = client_->startUpdateZone(Name("example.org"), true); + this->updater = this->client_->startUpdateZone(this->zname, true); EXPECT_EQ(ZoneFinder::NXDOMAIN, - updater->getFinder().find(qname, qtype).code); + this->updater->getFinder().find(this->qname, this->qtype).code); // DB should not have been rolled back yet. - EXPECT_FALSE(current_accessor_->isRollbacked()); - updater.reset(); // destruct without commit + EXPECT_FALSE(this->isRollbacked()); + this->updater.reset(); // destruct without commit // reset() should have triggered rollback (although it doesn't affect // anything to the mock accessor implementation except for the result of // isRollbacked()) - EXPECT_TRUE(current_accessor_->isRollbacked()); - EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname, qtype).code); + EXPECT_TRUE(this->isRollbacked(true)); + EXPECT_EQ(ZoneFinder::SUCCESS, this->finder->find(this->qname, + this->qtype).code); } -TEST_F(DatabaseClientTest, duplicateCommit) { +TYPED_TEST(DatabaseClientTest, duplicateCommit) { // duplicate commit. should result in exception. - updater = client_->startUpdateZone(Name("example.org"), true); - updater->commit(); - EXPECT_THROW(updater->commit(), DataSourceError); + this->updater = this->client_->startUpdateZone(this->zname, true); + this->updater->commit(); + EXPECT_THROW(this->updater->commit(), DataSourceError); } -TEST_F(DatabaseClientTest, addRRsetToNewZone) { +TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) { // Add a single RRset to a fresh empty zone - updater = client_->startUpdateZone(Name("example.org"), true); - updater->addRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, true); + this->updater->addRRset(*this->rrset); - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.2"); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.2"); { SCOPED_TRACE("add RRset"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } // Similar to the previous case, but with RRSIG - updater = client_->startUpdateZone(Name("example.org"), true); - updater->addRRset(*rrset); - updater->addRRset(*rrsigset); + this->updater = this->client_->startUpdateZone(this->zname, true); + this->updater->addRRset(*this->rrset); + this->updater->addRRset(*this->rrsigset); - // confirm the expected columns were passed to the mock accessor. + // confirm the expected columns were passed to the accessor (if checkable). const char* const rrsig_added[] = { "www.example.org.", "org.example.www.", "3600", "RRSIG", "A", "A 5 3 0 20000101000000 20000201000000 0 example.org. FAKEFAKEFAKE" }; - int i = 0; - BOOST_FOREACH(const string& column, current_accessor_->getLastAdded()) { - EXPECT_EQ(rrsig_added[i++], column); - } + this->checkLastAdded(rrsig_added); - expected_sig_rdatas.clear(); - expected_sig_rdatas.push_back(rrsig_added[DatabaseAccessor::ADD_RDATA]); + this->expected_sig_rdatas.clear(); + this->expected_sig_rdatas.push_back( + rrsig_added[DatabaseAccessor::ADD_RDATA]); { SCOPED_TRACE("add RRset with RRSIG"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, expected_sig_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->expected_sig_rdatas); } } -TEST_F(DatabaseClientTest, addRRsetToCurrentZone) { +TYPED_TEST(DatabaseClientTest, addRRsetToCurrentZone) { // Similar to the previous test, but not replacing the existing data. - updater = client_->startUpdateZone(Name("example.org"), false); - updater->addRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->addRRset(*this->rrset); // We should see both old and new data. - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_rdatas.push_back("192.0.2.2"); { SCOPED_TRACE("add RRset"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } - updater->commit(); + this->updater->commit(); { SCOPED_TRACE("add RRset after commit"); - doFindTest(*finder, qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + doFindTest(*this->finder, this->qname, this->qtype, this->qtype, + this->rrttl, ZoneFinder::SUCCESS, this->expected_rdatas, + this->empty_rdatas); } } -TEST_F(DatabaseClientTest, addMultipleRRs) { +TYPED_TEST(DatabaseClientTest, addMultipleRRs) { // Similar to the previous case, but the added RRset contains multiple // RRs. - updater = client_->startUpdateZone(Name("example.org"), false); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "192.0.2.3")); - updater->addRRset(*rrset); - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); - expected_rdatas.push_back("192.0.2.3"); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "192.0.2.3")); + this->updater->addRRset(*this->rrset); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_rdatas.push_back("192.0.2.2"); + this->expected_rdatas.push_back("192.0.2.3"); { SCOPED_TRACE("add multiple RRs"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, addRRsetOfLargerTTL) { +TYPED_TEST(DatabaseClientTest, addRRsetOfLargerTTL) { // Similar to the previous one, but the TTL of the added RRset is larger // than that of the existing record. The finder should use the smaller // one. - updater = client_->startUpdateZone(Name("example.org"), false); - rrset->setTTL(RRTTL(7200)); - updater->addRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->rrset->setTTL(RRTTL(7200)); + this->updater->addRRset(*this->rrset); - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_rdatas.push_back("192.0.2.2"); { SCOPED_TRACE("add RRset of larger TTL"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, addRRsetOfSmallerTTL) { +TYPED_TEST(DatabaseClientTest, addRRsetOfSmallerTTL) { // Similar to the previous one, but the added RRset has a smaller TTL. // The added TTL should be used by the finder. - updater = client_->startUpdateZone(Name("example.org"), false); - rrset->setTTL(RRTTL(1800)); - updater->addRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->rrset->setTTL(RRTTL(1800)); + this->updater->addRRset(*this->rrset); - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.2"); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_rdatas.push_back("192.0.2.2"); { SCOPED_TRACE("add RRset of smaller TTL"); - doFindTest(updater->getFinder(), qname, qtype, qtype, RRTTL(1800), - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, RRTTL(1800), ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, addSameRR) { +TYPED_TEST(DatabaseClientTest, addSameRR) { // Add the same RR as that is already in the data source. // Currently the add interface doesn't try to suppress the duplicate, // neither does the finder. We may want to revisit it in future versions. - updater = client_->startUpdateZone(Name("example.org"), false); - rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "192.0.2.1")); - updater->addRRset(*rrset); - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.1"); - expected_rdatas.push_back("192.0.2.1"); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->rrset.reset(new RRset(this->qname, this->qclass, this->qtype, + this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "192.0.2.1")); + this->updater->addRRset(*this->rrset); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.1"); + this->expected_rdatas.push_back("192.0.2.1"); { SCOPED_TRACE("add same RR"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, addDeviantRR) { - updater = client_->startUpdateZone(Name("example.org"), false); +TYPED_TEST(DatabaseClientTest, addDeviantRR) { + this->updater = this->client_->startUpdateZone(this->zname, false); // RR class mismatch. This should be detected and rejected. - rrset.reset(new RRset(qname, RRClass::CH(), RRType::TXT(), rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "test text")); - EXPECT_THROW(updater->addRRset(*rrset), DataSourceError); + this->rrset.reset(new RRset(this->qname, RRClass::CH(), RRType::TXT(), + this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "test text")); + EXPECT_THROW(this->updater->addRRset(*this->rrset), DataSourceError); // Out-of-zone owner name. At a higher level this should be rejected, // but it doesn't happen in this interface. - rrset.reset(new RRset(Name("example.com"), RRClass::IN(), qtype, rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "192.0.2.100")); - updater->addRRset(*rrset); + this->rrset.reset(new RRset(Name("example.com"), this->qclass, this->qtype, + this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "192.0.2.100")); + this->updater->addRRset(*this->rrset); - expected_rdatas.clear(); - expected_rdatas.push_back("192.0.2.100"); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("192.0.2.100"); { // Note: with the find() implementation being more strict about // zone cuts, this test may fail. Then the test should be updated. SCOPED_TRACE("add out-of-zone RR"); - doFindTest(updater->getFinder(), Name("example.com"), qtype, qtype, - rrttl, ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), Name("example.com"), + this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, addEmptyRRset) { - updater = client_->startUpdateZone(Name("example.org"), false); - rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); - EXPECT_THROW(updater->addRRset(*rrset), DataSourceError); +TYPED_TEST(DatabaseClientTest, addEmptyRRset) { + this->updater = this->client_->startUpdateZone(this->zname, false); + this->rrset.reset(new RRset(this->qname, this->qclass, this->qtype, + this->rrttl)); + EXPECT_THROW(this->updater->addRRset(*this->rrset), DataSourceError); } -TEST_F(DatabaseClientTest, addAfterCommit) { - updater = client_->startUpdateZone(Name("example.org"), false); - updater->commit(); - EXPECT_THROW(updater->addRRset(*rrset), DataSourceError); +TYPED_TEST(DatabaseClientTest, addAfterCommit) { + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->commit(); + EXPECT_THROW(this->updater->addRRset(*this->rrset), DataSourceError); } -TEST_F(DatabaseClientTest, deleteRRset) { - rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "192.0.2.1")); +TYPED_TEST(DatabaseClientTest, deleteRRset) { + this->rrset.reset(new RRset(this->qname, this->qclass, this->qtype, + this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "192.0.2.1")); // Delete one RR from an RRset - updater = client_->startUpdateZone(Name("example.org"), false); - updater->deleteRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->deleteRRset(*this->rrset); // Delete the only RR of a name - rrset.reset(new RRset(Name("cname.example.org"), RRClass::IN(), - RRType::CNAME(), rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "www.example.org")); - updater->deleteRRset(*rrset); + this->rrset.reset(new RRset(Name("cname.example.org"), this->qclass, + RRType::CNAME(), this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "www.example.org")); + this->updater->deleteRRset(*this->rrset); // The updater finder should immediately see the deleted results. { SCOPED_TRACE("delete RRset"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::NXRRSET, empty_rdatas, empty_rdatas); - doFindTest(updater->getFinder(), Name("cname.example.org"), - qtype, qtype, rrttl, ZoneFinder::NXDOMAIN, - empty_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::NXRRSET, + this->empty_rdatas, this->empty_rdatas); + doFindTest(this->updater->getFinder(), Name("cname.example.org"), + this->qtype, this->qtype, this->rrttl, ZoneFinder::NXDOMAIN, + this->empty_rdatas, this->empty_rdatas); } // before committing the change, the original finder should see the // original record. { SCOPED_TRACE("delete RRset before commit"); - expected_rdatas.push_back("192.0.2.1"); - doFindTest(*finder, qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + this->expected_rdatas.push_back("192.0.2.1"); + doFindTest(*this->finder, this->qname, this->qtype, this->qtype, + this->rrttl, ZoneFinder::SUCCESS, this->expected_rdatas, + this->empty_rdatas); - expected_rdatas.clear(); - expected_rdatas.push_back("www.example.org."); - doFindTest(*finder, Name("cname.example.org"), qtype, - RRType::CNAME(), rrttl, - ZoneFinder::CNAME, expected_rdatas, empty_rdatas); + this->expected_rdatas.clear(); + this->expected_rdatas.push_back("www.example.org."); + doFindTest(*this->finder, Name("cname.example.org"), this->qtype, + RRType::CNAME(), this->rrttl, ZoneFinder::CNAME, + this->expected_rdatas, this->empty_rdatas); } // once committed, the record should be removed from the original finder's // view, too. - updater->commit(); + this->updater->commit(); { SCOPED_TRACE("delete RRset after commit"); - doFindTest(*finder, qname, qtype, qtype, rrttl, - ZoneFinder::NXRRSET, empty_rdatas, empty_rdatas); - doFindTest(*finder, Name("cname.example.org"), - qtype, qtype, rrttl, ZoneFinder::NXDOMAIN, - empty_rdatas, empty_rdatas); + doFindTest(*this->finder, this->qname, this->qtype, this->qtype, + this->rrttl, ZoneFinder::NXRRSET, this->empty_rdatas, + this->empty_rdatas); + doFindTest(*this->finder, Name("cname.example.org"), + this->qtype, this->qtype, this->rrttl, ZoneFinder::NXDOMAIN, + this->empty_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, deleteRRsetToNXDOMAIN) { +TYPED_TEST(DatabaseClientTest, deleteRRsetToNXDOMAIN) { // similar to the previous case, but it removes the only record of the // given name. a subsequent find() should result in NXDOMAIN. - rrset.reset(new RRset(Name("cname.example.org"), RRClass::IN(), - RRType::CNAME(), rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "www.example.org")); + this->rrset.reset(new RRset(Name("cname.example.org"), this->qclass, + RRType::CNAME(), this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "www.example.org")); - updater = client_->startUpdateZone(Name("example.org"), false); - updater->deleteRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->deleteRRset(*this->rrset); { SCOPED_TRACE("delete RRset to NXDOMAIN"); - doFindTest(updater->getFinder(), Name("cname.example.org"), qtype, - qtype, rrttl, ZoneFinder::NXDOMAIN, empty_rdatas, - empty_rdatas); + doFindTest(this->updater->getFinder(), Name("cname.example.org"), + this->qtype, this->qtype, this->rrttl, ZoneFinder::NXDOMAIN, + this->empty_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, deleteMultipleRRs) { - rrset.reset(new RRset(qname, RRClass::IN(), RRType::AAAA(), rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "2001:db8::1")); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "2001:db8::2")); +TYPED_TEST(DatabaseClientTest, deleteMultipleRRs) { + this->rrset.reset(new RRset(this->qname, this->qclass, RRType::AAAA(), + this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "2001:db8::1")); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "2001:db8::2")); - updater = client_->startUpdateZone(Name("example.org"), false); - updater->deleteRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->deleteRRset(*this->rrset); { SCOPED_TRACE("delete multiple RRs"); - doFindTest(updater->getFinder(), qname, RRType::AAAA(), qtype, rrttl, - ZoneFinder::NXRRSET, empty_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, RRType::AAAA(), + this->qtype, this->rrttl, ZoneFinder::NXRRSET, + this->empty_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, partialDelete) { - rrset.reset(new RRset(qname, RRClass::IN(), RRType::AAAA(), rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "2001:db8::1")); +TYPED_TEST(DatabaseClientTest, partialDelete) { + this->rrset.reset(new RRset(this->qname, this->qclass, RRType::AAAA(), + this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "2001:db8::1")); // This does not exist in the test data source: - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "2001:db8::3")); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "2001:db8::3")); // deleteRRset should succeed "silently", and subsequent find() should // find the remaining RR. - updater = client_->startUpdateZone(Name("example.org"), false); - updater->deleteRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->deleteRRset(*this->rrset); { SCOPED_TRACE("partial delete"); - expected_rdatas.push_back("2001:db8::2"); - doFindTest(updater->getFinder(), qname, RRType::AAAA(), - RRType::AAAA(), rrttl, ZoneFinder::SUCCESS, - expected_rdatas, empty_rdatas); + this->expected_rdatas.push_back("2001:db8::2"); + doFindTest(this->updater->getFinder(), this->qname, RRType::AAAA(), + RRType::AAAA(), this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, deleteNoMatch) { +TYPED_TEST(DatabaseClientTest, deleteNoMatch) { // similar to the previous test, but there's not even a match in the // specified RRset. Essentially there's no difference in the result. - updater = client_->startUpdateZone(Name("example.org"), false); - updater->deleteRRset(*rrset); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->deleteRRset(*this->rrset); { SCOPED_TRACE("delete no match"); - expected_rdatas.push_back("192.0.2.1"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::SUCCESS, expected_rdatas, empty_rdatas); + this->expected_rdatas.push_back("192.0.2.1"); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::SUCCESS, + this->expected_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, deleteWithDifferentTTL) { +TYPED_TEST(DatabaseClientTest, deleteWithDifferentTTL) { // Our delete interface simply ignores TTL (may change in a future version) - rrset.reset(new RRset(qname, RRClass::IN(), qtype, RRTTL(1800))); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "192.0.2.1")); - updater = client_->startUpdateZone(Name("example.org"), false); - updater->deleteRRset(*rrset); + this->rrset.reset(new RRset(this->qname, this->qclass, this->qtype, + RRTTL(1800))); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "192.0.2.1")); + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->deleteRRset(*this->rrset); { SCOPED_TRACE("delete RRset with a different TTL"); - doFindTest(updater->getFinder(), qname, qtype, qtype, rrttl, - ZoneFinder::NXRRSET, empty_rdatas, empty_rdatas); + doFindTest(this->updater->getFinder(), this->qname, this->qtype, + this->qtype, this->rrttl, ZoneFinder::NXRRSET, + this->empty_rdatas, this->empty_rdatas); } } -TEST_F(DatabaseClientTest, deleteDeviantRR) { - updater = client_->startUpdateZone(Name("example.org"), false); +TYPED_TEST(DatabaseClientTest, deleteDeviantRR) { + this->updater = this->client_->startUpdateZone(this->zname, false); // RR class mismatch. This should be detected and rejected. - rrset.reset(new RRset(qname, RRClass::CH(), RRType::TXT(), rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "test text")); - EXPECT_THROW(updater->deleteRRset(*rrset), DataSourceError); + this->rrset.reset(new RRset(this->qname, RRClass::CH(), RRType::TXT(), + this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "test text")); + EXPECT_THROW(this->updater->deleteRRset(*this->rrset), DataSourceError); // Out-of-zone owner name. At a higher level this should be rejected, // but it doesn't happen in this interface. - rrset.reset(new RRset(Name("example.com"), RRClass::IN(), qtype, rrttl)); - rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), - "192.0.2.100")); - EXPECT_NO_THROW(updater->deleteRRset(*rrset)); + this->rrset.reset(new RRset(Name("example.com"), this->qclass, this->qtype, + this->rrttl)); + this->rrset->addRdata(rdata::createRdata(this->rrset->getType(), + this->rrset->getClass(), + "192.0.2.100")); + EXPECT_NO_THROW(this->updater->deleteRRset(*this->rrset)); } -TEST_F(DatabaseClientTest, deleteAfterCommit) { - updater = client_->startUpdateZone(Name("example.org"), false); - updater->commit(); - EXPECT_THROW(updater->deleteRRset(*rrset), DataSourceError); +TYPED_TEST(DatabaseClientTest, deleteAfterCommit) { + this->updater = this->client_->startUpdateZone(this->zname, false); + this->updater->commit(); + EXPECT_THROW(this->updater->deleteRRset(*this->rrset), DataSourceError); } -TEST_F(DatabaseClientTest, deleteEmptyRRset) { - updater = client_->startUpdateZone(Name("example.org"), false); - rrset.reset(new RRset(qname, RRClass::IN(), qtype, rrttl)); - EXPECT_THROW(updater->deleteRRset(*rrset), DataSourceError); +TYPED_TEST(DatabaseClientTest, deleteEmptyRRset) { + this->updater = this->client_->startUpdateZone(this->zname, false); + this->rrset.reset(new RRset(this->qname, this->qclass, this->qtype, + this->rrttl)); + EXPECT_THROW(this->updater->deleteRRset(*this->rrset), DataSourceError); } } From d60bb44c243f27053589b5501529b0001404373f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 17 Aug 2011 16:11:15 -0700 Subject: [PATCH 580/974] [1068] refactored the test, 2nd phase: tested sqlite3 accessor as well as mock accessor. right now some of the tests for sqlite3 fail. --- src/lib/datasrc/tests/database_unittest.cc | 322 ++++++++++++--------- 1 file changed, 181 insertions(+), 141 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index b8800ab9ba..5b65a1f5ef 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -40,9 +41,91 @@ using namespace isc::dns; namespace { +// Imaginary zone IDs used in the mock accessor below. const int READONLY_ZONE_ID = 42; const int WRITABLE_ZONE_ID = 4200; +// Commonly used test data +const char* const TEST_RECORDS[][5] = { + // some plain data + {"www.example.org.", "A", "3600", "", "192.0.2.1"}, + {"www.example.org.", "AAAA", "3600", "", "2001:db8::1"}, + {"www.example.org.", "AAAA", "3600", "", "2001:db8::2"}, + + {"www2.example.org.", "A", "3600", "", "192.0.2.1"}, + {"www2.example.org.","AAAA", "3600", "", "2001:db8::1"}, + {"www2.example.org.", "A", "3600", "", "192.0.2.2"}, + + {"cname.example.org.", "CNAME", "3600", "", "www.example.org."}, + + // some DNSSEC-'signed' data + {"signed1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"signed1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"signed1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"}, + {"signed1.example.org.", "AAAA", "3600", "", "2001:db8::1"}, + {"signed1.example.org.", "AAAA", "3600", "", "2001:db8::2"}, + {"signed1.example.org.", "RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + {"signedcname1.example.org.", "CNAME", "3600", "", "www.example.org."}, + {"signedcname1.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + // special case might fail; sig is for cname, which isn't there (should be ignored) + // (ignoring of 'normal' other type is done above by www.) + {"acnamesig1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"acnamesig1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"acnamesig1.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + // let's pretend we have a database that is not careful + // about the order in which it returns data + {"signed2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"signed2.example.org.", "AAAA", "3600", "", "2001:db8::2"}, + {"signed2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"}, + {"signed2.example.org.", "A", "3600", "", "192.0.2.1"}, + {"signed2.example.org.", "RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"signed2.example.org.", "AAAA", "3600", "", "2001:db8::1"}, + + {"signedcname2.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"signedcname2.example.org.", "CNAME", "3600", "", "www.example.org."}, + + {"acnamesig2.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"acnamesig2.example.org.", "A", "3600", "", "192.0.2.1"}, + {"acnamesig2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + {"acnamesig3.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"acnamesig3.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"acnamesig3.example.org.", "A", "3600", "", "192.0.2.1"}, + + {"ttldiff1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"ttldiff1.example.org.", "A", "360", "", "192.0.2.2"}, + + {"ttldiff2.example.org.", "A", "360", "", "192.0.2.1"}, + {"ttldiff2.example.org.", "A", "3600", "", "192.0.2.2"}, + + // also add some intentionally bad data + {"badcname1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"badcname1.example.org.", "CNAME", "3600", "", "www.example.org."}, + + {"badcname2.example.org.", "CNAME", "3600", "", "www.example.org."}, + {"badcname2.example.org.", "A", "3600", "", "192.0.2.1"}, + + {"badcname3.example.org.", "CNAME", "3600", "", "www.example.org."}, + {"badcname3.example.org.", "CNAME", "3600", "", "www.example2.org."}, + + {"badrdata.example.org.", "A", "3600", "", "bad"}, + + {"badtype.example.org.", "BAD_TYPE", "3600", "", "192.0.2.1"}, + + {"badttl.example.org.", "A", "badttl", "", "192.0.2.1"}, + + {"badsig.example.org.", "A", "badttl", "", "192.0.2.1"}, + {"badsig.example.org.", "RRSIG", "3600", "", "A 5 3 3600 somebaddata 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + {"badsigtype.example.org.", "A", "3600", "", "192.0.2.1"}, + {"badsigtype.example.org.", "RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + {NULL, NULL, NULL, NULL, NULL}, +}; + /* * A virtual database database that pretends it contains single zone -- * example.org. @@ -277,97 +360,40 @@ private: // might not come in 'normal' order) // It shall immediately fail if you try to add the same name twice. void fillData() { - // some plain data - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("AAAA", "3600", "", "2001:db8::1"); - addRecord("AAAA", "3600", "", "2001:db8::2"); - addCurName("www.example.org."); + const char* prev_name = NULL; + for (int i = 0; TEST_RECORDS[i][0] != NULL; ++i) { + if (prev_name != NULL && + strcmp(prev_name, TEST_RECORDS[i][0]) != 0) { + addCurName(prev_name); + } + prev_name = TEST_RECORDS[i][0]; + addRecord(TEST_RECORDS[i][1], TEST_RECORDS[i][2], + TEST_RECORDS[i][3], TEST_RECORDS[i][4]); + } + addCurName(prev_name); + } +}; - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("AAAA", "3600", "", "2001:db8::1"); - addRecord("A", "3600", "", "192.0.2.2"); - addCurName("www2.example.org."); +class TestSQLite3Accessor : public SQLite3Accessor { +public: + TestSQLite3Accessor() : SQLite3Accessor( + TEST_DATA_BUILDDIR "/rwtest.sqlite3.copied", + RRClass::IN()) + { + startUpdateZone("example.org.", true); + vector columns; + for (int i = 0; TEST_RECORDS[i][0] != NULL; ++i) { + columns.assign(DatabaseAccessor::ADD_COLUMN_COUNT, ""); + columns[ADD_NAME] = TEST_RECORDS[i][0]; + columns[ADD_REV_NAME] = Name(columns[ADD_NAME]).reverse().toText(); + columns[ADD_TYPE] = TEST_RECORDS[i][1]; + columns[ADD_TTL] = TEST_RECORDS[i][2]; + columns[ADD_SIGTYPE] = TEST_RECORDS[i][3]; + columns[ADD_RDATA] = TEST_RECORDS[i][4]; - addRecord("CNAME", "3600", "", "www.example.org."); - addCurName("cname.example.org."); - - // some DNSSEC-'signed' data - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - addRecord("AAAA", "3600", "", "2001:db8::1"); - addRecord("AAAA", "3600", "", "2001:db8::2"); - addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("signed1.example.org."); - addRecord("CNAME", "3600", "", "www.example.org."); - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("signedcname1.example.org."); - // special case might fail; sig is for cname, which isn't there (should be ignored) - // (ignoring of 'normal' other type is done above by www.) - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("acnamesig1.example.org."); - - // let's pretend we have a database that is not careful - // about the order in which it returns data - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("AAAA", "3600", "", "2001:db8::2"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("AAAA", "3600", "", "2001:db8::1"); - addCurName("signed2.example.org."); - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("CNAME", "3600", "", "www.example.org."); - addCurName("signedcname2.example.org."); - - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("acnamesig2.example.org."); - - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("acnamesig3.example.org."); - - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("A", "360", "", "192.0.2.2"); - addCurName("ttldiff1.example.org."); - addRecord("A", "360", "", "192.0.2.1"); - addRecord("A", "3600", "", "192.0.2.2"); - addCurName("ttldiff2.example.org."); - - // also add some intentionally bad data - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("CNAME", "3600", "", "www.example.org."); - addCurName("badcname1.example.org."); - - addRecord("CNAME", "3600", "", "www.example.org."); - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("badcname2.example.org."); - - addRecord("CNAME", "3600", "", "www.example.org."); - addRecord("CNAME", "3600", "", "www.example2.org."); - addCurName("badcname3.example.org."); - - addRecord("A", "3600", "", "bad"); - addCurName("badrdata.example.org."); - - addRecord("BAD_TYPE", "3600", "", "192.0.2.1"); - addCurName("badtype.example.org."); - - addRecord("A", "badttl", "", "192.0.2.1"); - addCurName("badttl.example.org."); - - addRecord("A", "badttl", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 somebaddata 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("badsig.example.org."); - - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("badsigtype.example.org."); + addRecordToZone(columns); + } + commitUpdateZone(); } }; @@ -389,7 +415,7 @@ protected: rrset.reset(new RRset(qname, qclass, qtype, rrttl)); // Adding an IN/A RDATA. Intentionally using different data - // than the initial data configured in MockAccessor::fillData(). + // than the initial data configured in TEST_RECORDS. rrset->addRdata(rdata::createRdata(rrset->getType(), rrset->getClass(), "192.0.2.2")); @@ -407,33 +433,34 @@ protected: */ void createClient() { current_accessor_ = new ACCESSOR_TYPE(); + is_mock_ = (dynamic_cast(current_accessor_) != NULL); client_.reset(new DatabaseClient(qclass, shared_ptr( current_accessor_))); } bool searchRunning(bool expected = false) const { - try { + if (is_mock_) { const MockAccessor* mock_accessor = dynamic_cast(current_accessor_); return (mock_accessor->searchRunning()); - } catch (const bad_cast&) { + } else { return (expected); } } bool isRollbacked(bool expected = false) const { - try { + if (is_mock_) { const MockAccessor* mock_accessor = dynamic_cast(current_accessor_); return (mock_accessor->isRollbacked()); - } catch (const bad_cast&) { + } else { return (expected); } } void checkLastAdded(const char* const expected[]) const { - try { + if (is_mock_) { const MockAccessor* mock_accessor = dynamic_cast(current_accessor_); int i = 0; @@ -441,13 +468,14 @@ protected: mock_accessor->getLastAdded()) { EXPECT_EQ(expected[i++], column); } - } catch (const bad_cast&) { - ; } } + // Some tests only work for MockAccessor. We remember whether our accessor + // is of that type. + bool is_mock_; + // Will be deleted by client_, just keep the current value for comparison. - //MockAccessor* current_accessor_; DatabaseAccessor* current_accessor_; shared_ptr client_; const std::string database_name_; @@ -478,12 +506,14 @@ protected: dynamic_pointer_cast(zone.zone_finder)); ASSERT_NE(shared_ptr(), finder) << "Wrong type of finder"; - EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); + if (is_mock_) { + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); + } EXPECT_EQ(current_accessor_, &finder->getAccessor()); } }; -typedef ::testing::Types TestAccessorTypes; +typedef ::testing::Types TestAccessorTypes; TYPED_TEST_CASE(DatabaseClientTest, TestAccessorTypes); TYPED_TEST(DatabaseClientTest, zoneNotFound) { @@ -572,7 +602,9 @@ doFindTest(ZoneFinder& finder, } TYPED_TEST(DatabaseClientTest, find) { - EXPECT_EQ(READONLY_ZONE_ID, this->finder->zone_id()); + if (this->is_mock_) { + EXPECT_EQ(READONLY_ZONE_ID, this->finder->zone_id()); + } EXPECT_FALSE(this->searchRunning()); this->expected_rdatas.clear(); @@ -800,38 +832,41 @@ TYPED_TEST(DatabaseClientTest, find) { DataSourceError); EXPECT_FALSE(this->searchRunning()); - // Trigger the hardcoded exceptions and see if find() has cleaned up - EXPECT_THROW(this->finder->find(Name("dsexception.in.search."), - this->qtype, NULL, - ZoneFinder::FIND_DEFAULT), - DataSourceError); - EXPECT_FALSE(this->searchRunning()); - EXPECT_THROW(this->finder->find(Name("iscexception.in.search."), - this->qtype, NULL, - ZoneFinder::FIND_DEFAULT), - DataSourceError); - EXPECT_FALSE(this->searchRunning()); - EXPECT_THROW(this->finder->find( - Name("basicexception.in.search."), - this->qtype, NULL, ZoneFinder::FIND_DEFAULT), - std::exception); - EXPECT_FALSE(this->searchRunning()); + // Trigger the hardcoded exceptions and see if find() has cleaned up. + // This can only work for a mock accessor. + if (this->is_mock_) { + EXPECT_THROW(this->finder->find(Name("dsexception.in.search."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("iscexception.in.search."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find( + Name("basicexception.in.search."), + this->qtype, NULL, ZoneFinder::FIND_DEFAULT), + std::exception); + EXPECT_FALSE(this->searchRunning()); - EXPECT_THROW(this->finder->find(Name("dsexception.in.getnext."), - this->qtype, NULL, - ZoneFinder::FIND_DEFAULT), - DataSourceError); - EXPECT_FALSE(this->searchRunning()); - EXPECT_THROW(this->finder->find(Name("iscexception.in.getnext."), - this->qtype, NULL, - ZoneFinder::FIND_DEFAULT), - DataSourceError); - EXPECT_FALSE(this->searchRunning()); - EXPECT_THROW(this->finder->find(Name("basicexception.in.getnext."), - this->qtype, NULL, - ZoneFinder::FIND_DEFAULT), - std::exception); - EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("dsexception.in.getnext."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("iscexception.in.getnext."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_FALSE(this->searchRunning()); + EXPECT_THROW(this->finder->find(Name("basicexception.in.getnext."), + this->qtype, NULL, + ZoneFinder::FIND_DEFAULT), + std::exception); + EXPECT_FALSE(this->searchRunning()); + } // This RRSIG has the wrong sigtype field, which should be // an error if we decide to keep using that field @@ -852,21 +887,26 @@ TYPED_TEST(DatabaseClientTest, updaterFinder) { // If this update isn't replacing the zone, the finder should work // just like the normal find() case. - EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( - this->updater->getFinder()).zone_id()); + if (this->is_mock_) { + EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( + this->updater->getFinder()).zone_id()); + } this->expected_rdatas.clear(); this->expected_rdatas.push_back("192.0.2.1"); - doFindTest(this->updater->getFinder(), Name("www.example.org."), + doFindTest(this->updater->getFinder(), this->qname, this->qtype, this->qtype, this->rrttl, ZoneFinder::SUCCESS, this->expected_rdatas, this->empty_rdatas); // When replacing the zone, the updater's finder shouldn't see anything // in the zone until something is added. + this->updater.reset(); this->updater = this->client_->startUpdateZone(this->zname, true); ASSERT_TRUE(this->updater); - EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( - this->updater->getFinder()).zone_id()); - doFindTest(this->updater->getFinder(), Name("www.example.org."), + if (this->is_mock_) { + EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( + this->updater->getFinder()).zone_id()); + } + doFindTest(this->updater->getFinder(), this->qname, RRType::A(), RRType::A(), RRTTL(3600), ZoneFinder::NXDOMAIN, this->empty_rdatas, this->empty_rdatas); } @@ -875,15 +915,14 @@ TYPED_TEST(DatabaseClientTest, flushZone) { // A simple update case: flush the entire zone // Before update, the name exists. - ZoneFinderPtr finder = this->client_->findZone(this->zname).zone_finder; EXPECT_EQ(ZoneFinder::SUCCESS, this->finder->find(this->qname, this->qtype).code); // start update in the replace mode. the normal finder should still // be able to see the record, but the updater's finder shouldn't. this->updater = this->client_->startUpdateZone(this->zname, true); - EXPECT_EQ(ZoneFinder::SUCCESS, this->finder->find(this->qname, - this->qtype).code); + EXPECT_EQ(ZoneFinder::SUCCESS, + this->finder->find(this->qname, this->qtype).code); EXPECT_EQ(ZoneFinder::NXDOMAIN, this->updater->getFinder().find(this->qname, this->qtype).code); @@ -941,6 +980,7 @@ TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) { } // Similar to the previous case, but with RRSIG + this->updater.reset(); this->updater = this->client_->startUpdateZone(this->zname, true); this->updater->addRRset(*this->rrset); this->updater->addRRset(*this->rrsigset); From ce3be84b9f772fda5f08947fec92764119989019 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Thu, 18 Aug 2011 13:59:07 +0800 Subject: [PATCH 581/974] [trac1153] avoid zonemgr exits on empty zones --- src/bin/zonemgr/tests/zonemgr_test.py | 42 +++++++++------- src/bin/zonemgr/zonemgr.py.in | 71 ++++++++++----------------- 2 files changed, 50 insertions(+), 63 deletions(-) diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py index 496ce6bdec..60f904972e 100644 --- a/src/bin/zonemgr/tests/zonemgr_test.py +++ b/src/bin/zonemgr/tests/zonemgr_test.py @@ -304,8 +304,8 @@ class TestZonemgrRefresh(unittest.TestCase): def get_zone_soa2(zone_name, db_file): return None sqlite3_ds.get_zone_soa = get_zone_soa2 - self.assertRaises(ZonemgrException, self.zone_refresh.zonemgr_add_zone, \ - ZONE_NAME_CLASS1_IN) + self.zone_refresh.zonemgr_add_zone(ZONE_NAME_CLASS2_IN) + self.assertFalse(ZONE_NAME_CLASS2_IN in self.zone_refresh._zonemgr_refresh_info) sqlite3_ds.get_zone_soa = old_get_zone_soa def test_zone_handle_notify(self): @@ -440,6 +440,8 @@ class TestZonemgrRefresh(unittest.TestCase): "class": "IN" } ] } self.zone_refresh.update_config_data(config_data) + self.assertTrue(("example.net.", "IN") in + self.zone_refresh._zonemgr_refresh_info) # update all values config_data = { @@ -479,14 +481,15 @@ class TestZonemgrRefresh(unittest.TestCase): "secondary_zones": [ { "name": "doesnotexist", "class": "IN" } ] } - self.assertRaises(ZonemgrException, - self.zone_refresh.update_config_data, - config_data) - self.assertEqual(60, self.zone_refresh._lowerbound_refresh) - self.assertEqual(30, self.zone_refresh._lowerbound_retry) - self.assertEqual(19800, self.zone_refresh._max_transfer_timeout) - self.assertEqual(0.25, self.zone_refresh._refresh_jitter) - self.assertEqual(0.35, self.zone_refresh._reload_jitter) + self.zone_refresh.update_config_data(config_data) + self.assertFalse(("doesnotexist.", "IN") + in self.zone_refresh._zonemgr_refresh_info) + # The other configs should be updated successful + self.assertEqual(61, self.zone_refresh._lowerbound_refresh) + self.assertEqual(31, self.zone_refresh._lowerbound_retry) + self.assertEqual(19801, self.zone_refresh._max_transfer_timeout) + self.assertEqual(0.21, self.zone_refresh._refresh_jitter) + self.assertEqual(0.71, self.zone_refresh._reload_jitter) # Make sure we accept 0 as a value config_data = { @@ -526,10 +529,11 @@ class TestZonemgrRefresh(unittest.TestCase): self.zone_refresh._zonemgr_refresh_info) # This one does not exist config.set_zone_list_from_name_classes(["example.net", "CH"]) - self.assertRaises(ZonemgrException, - self.zone_refresh.update_config_data, config) - # So it should not affect the old ones - self.assertTrue(("example.net.", "IN") in + self.zone_refresh.update_config_data(config) + self.assertFalse(("example.net.", "CH") in + self.zone_refresh._zonemgr_refresh_info) + # Simply skip the zone, the other configs should be updated successful + self.assertFalse(("example.net.", "IN") in self.zone_refresh._zonemgr_refresh_info) # Make sure it works even when we "accidentally" forget the final dot config.set_zone_list_from_name_classes([("example.net", "IN")]) @@ -596,15 +600,17 @@ class TestZonemgr(unittest.TestCase): config_data3 = {"refresh_jitter" : 0.7} self.zonemgr.config_handler(config_data3) self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter")) - # The zone doesn't exist in database, it should be rejected + # The zone doesn't exist in database, simply skip it and log an error self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None, config_data1) config_data1["secondary_zones"] = [{"name": "nonexistent.example", "class": "IN"}] - self.assertNotEqual(self.zonemgr.config_handler(config_data1), + self.assertEqual(self.zonemgr.config_handler(config_data1), {"result": [0]}) - # As it is rejected, the old value should be kept - self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter")) + # other configs should be updated successful + self.assertFalse(("nonexistent.example.", "IN") in + self.zonemgr._zone_refresh._zonemgr_refresh_info) + self.assertEqual(0.1, self.zonemgr._config_data.get("refresh_jitter")) def test_get_db_file(self): self.assertEqual("initdb.file", self.zonemgr.get_db_file()) diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index 87a00921a5..dfd4933666 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -202,7 +202,7 @@ class ZonemgrRefresh: zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file) if not zone_soa: logger.error(ZONEMGR_NO_SOA, zone_name_class[0], zone_name_class[1]) - raise ZonemgrException("[b10-zonemgr] zone (%s, %s) doesn't have soa." % zone_name_class) + return zone_info["zone_soa_rdata"] = zone_soa[7] zone_info["zone_state"] = ZONE_OK zone_info["last_refresh_time"] = self._get_current_time() @@ -420,12 +420,6 @@ class ZonemgrRefresh: def update_config_data(self, new_config): """ update ZonemgrRefresh config """ - # TODO: we probably want to store all this info in a nice - # class, so that we don't have to backup and restore every - # single value. - # TODO2: We also don't use get_default_value yet - backup = self._zonemgr_refresh_info.copy() - # Get a new value, but only if it is defined (commonly used below) # We don't use "value or default", because if value would be # 0, we would take default @@ -435,58 +429,45 @@ class ZonemgrRefresh: else: return default - # store the values so we can restore them if there is a problem - lowerbound_refresh_backup = self._lowerbound_refresh self._lowerbound_refresh = val_or_default( new_config.get('lowerbound_refresh'), self._lowerbound_refresh) - lowerbound_retry_backup = self._lowerbound_retry self._lowerbound_retry = val_or_default( new_config.get('lowerbound_retry'), self._lowerbound_retry) - max_transfer_timeout_backup = self._max_transfer_timeout self._max_transfer_timeout = val_or_default( new_config.get('max_transfer_timeout'), self._max_transfer_timeout) - refresh_jitter_backup = self._refresh_jitter self._refresh_jitter = val_or_default( new_config.get('refresh_jitter'), self._refresh_jitter) - reload_jitter_backup = self._reload_jitter self._reload_jitter = val_or_default( new_config.get('reload_jitter'), self._reload_jitter) - try: - required = {} - secondary_zones = new_config.get('secondary_zones') - if secondary_zones is not None: - # Add new zones - for secondary_zone in new_config.get('secondary_zones'): - name = secondary_zone['name'] - # Be tolerant to sclerotic users who forget the final dot - if name[-1] != '.': - name = name + '.' - name_class = (name, secondary_zone['class']) - required[name_class] = True - # Add it only if it isn't there already - if not name_class in self._zonemgr_refresh_info: - self.zonemgr_add_zone(name_class) - # Drop the zones that are no longer there - # Do it in two phases, python doesn't like deleting while iterating - to_drop = [] - for old_zone in self._zonemgr_refresh_info: - if not old_zone in required: - to_drop.append(old_zone) - for drop in to_drop: - del self._zonemgr_refresh_info[drop] - # If we are not able to find it in database, restore the original - except: - self._zonemgr_refresh_info = backup - self._lowerbound_refresh = lowerbound_refresh_backup - self._lowerbound_retry = lowerbound_retry_backup - self._max_transfer_timeout = max_transfer_timeout_backup - self._refresh_jitter = refresh_jitter_backup - self._reload_jitter = reload_jitter_backup - raise + + required = {} + secondary_zones = new_config.get('secondary_zones') + if secondary_zones is not None: + # Add new zones + for secondary_zone in new_config.get('secondary_zones'): + name = secondary_zone['name'] + # Be tolerant to sclerotic users who forget the final dot + if name[-1] != '.': + name = name + '.' + name_class = (name, secondary_zone['class']) + required[name_class] = True + # Add it only if it isn't there already + if not name_class in self._zonemgr_refresh_info: + # If we are not able to find it in database, simply skip + # it and log an error + self.zonemgr_add_zone(name_class) + # Drop the zones that are no longer there + # Do it in two phases, python doesn't like deleting while iterating + to_drop = [] + for old_zone in self._zonemgr_refresh_info: + if not old_zone in required: + to_drop.append(old_zone) + for drop in to_drop: + del self._zonemgr_refresh_info[drop] class Zonemgr: """Zone manager class.""" From ec8fed8c805b513ea15ad76eb380c639dba88548 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Thu, 18 Aug 2011 15:59:09 +0800 Subject: [PATCH 582/974] [trac1153] set retry timer for empty zones database --- src/bin/zonemgr/tests/zonemgr_test.py | 26 +++++++++++++++++++------- src/bin/zonemgr/zonemgr.py.in | 23 ++++++++++++++--------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py index 60f904972e..67893c8f7b 100644 --- a/src/bin/zonemgr/tests/zonemgr_test.py +++ b/src/bin/zonemgr/tests/zonemgr_test.py @@ -152,6 +152,16 @@ class TestZonemgrRefresh(unittest.TestCase): self.assertTrue((time1 + 3600 * (1 - self.zone_refresh._refresh_jitter)) <= zone_timeout) self.assertTrue(zone_timeout <= time2 + 3600) + # No soa rdata + self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"] = None + time3 = time.time() + self.zone_refresh._set_zone_retry_timer(ZONE_NAME_CLASS1_IN) + zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"] + time4 = time.time() + self.assertTrue((time3 + self.zone_refresh._lowerbound_retry * (1 - self.zone_refresh._refresh_jitter)) + <= zone_timeout) + self.assertTrue(zone_timeout <= time4 + self.zone_refresh._lowerbound_retry) + def test_zone_not_exist(self): self.assertFalse(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS1_IN)) self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS1_CH)) @@ -305,7 +315,7 @@ class TestZonemgrRefresh(unittest.TestCase): return None sqlite3_ds.get_zone_soa = get_zone_soa2 self.zone_refresh.zonemgr_add_zone(ZONE_NAME_CLASS2_IN) - self.assertFalse(ZONE_NAME_CLASS2_IN in self.zone_refresh._zonemgr_refresh_info) + self.assertTrue(self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_IN]["zone_soa_rdata"] is None) sqlite3_ds.get_zone_soa = old_get_zone_soa def test_zone_handle_notify(self): @@ -482,8 +492,9 @@ class TestZonemgrRefresh(unittest.TestCase): "class": "IN" } ] } self.zone_refresh.update_config_data(config_data) - self.assertFalse(("doesnotexist.", "IN") - in self.zone_refresh._zonemgr_refresh_info) + name_class = ("doesnotexist.", "IN") + self.assertTrue(self.zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"] + is None) # The other configs should be updated successful self.assertEqual(61, self.zone_refresh._lowerbound_refresh) self.assertEqual(31, self.zone_refresh._lowerbound_retry) @@ -532,7 +543,7 @@ class TestZonemgrRefresh(unittest.TestCase): self.zone_refresh.update_config_data(config) self.assertFalse(("example.net.", "CH") in self.zone_refresh._zonemgr_refresh_info) - # Simply skip the zone, the other configs should be updated successful + # Simply skip loading soa for the zone, the other configs should be updated successful self.assertFalse(("example.net.", "IN") in self.zone_refresh._zonemgr_refresh_info) # Make sure it works even when we "accidentally" forget the final dot @@ -600,7 +611,7 @@ class TestZonemgr(unittest.TestCase): config_data3 = {"refresh_jitter" : 0.7} self.zonemgr.config_handler(config_data3) self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter")) - # The zone doesn't exist in database, simply skip it and log an error + # The zone doesn't exist in database, simply skip loading soa for it and log an warning self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None, config_data1) config_data1["secondary_zones"] = [{"name": "nonexistent.example", @@ -608,8 +619,9 @@ class TestZonemgr(unittest.TestCase): self.assertEqual(self.zonemgr.config_handler(config_data1), {"result": [0]}) # other configs should be updated successful - self.assertFalse(("nonexistent.example.", "IN") in - self.zonemgr._zone_refresh._zonemgr_refresh_info) + name_class = ("nonexistent.example.", "IN") + self.assertTrue(self.zonemgr._zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"] + is None) self.assertEqual(0.1, self.zonemgr._config_data.get("refresh_jitter")) def test_get_db_file(self): diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index dfd4933666..2a5ae1cdbb 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -142,7 +142,10 @@ class ZonemgrRefresh: """Set zone next refresh time after zone refresh fail. now + retry - retry_jitter <= next_refresh_time <= now + retry """ - zone_retry_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[RETRY_OFFSET]) + if (self._get_zone_soa_rdata(zone_name_class) is not None): + zone_retry_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[RETRY_OFFSET]) + else: + zone_retry_time = 0.0 zone_retry_time = max(self._lowerbound_retry, zone_retry_time) self._set_zone_timer(zone_name_class, zone_retry_time, self._refresh_jitter * zone_retry_time) @@ -174,7 +177,8 @@ class ZonemgrRefresh: raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't " "belong to zonemgr" % zone_name_class) # Is zone expired? - if (self._zone_is_expired(zone_name_class)): + if ((self._get_zone_soa_rdata(zone_name_class) is not None) and + self._zone_is_expired(zone_name_class)): self._set_zone_state(zone_name_class, ZONE_EXPIRED) else: self._set_zone_state(zone_name_class, ZONE_OK) @@ -200,15 +204,17 @@ class ZonemgrRefresh: logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_LOAD_ZONE, zone_name_class[0], zone_name_class[1]) zone_info = {} zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file) - if not zone_soa: - logger.error(ZONEMGR_NO_SOA, zone_name_class[0], zone_name_class[1]) - return - zone_info["zone_soa_rdata"] = zone_soa[7] + if zone_soa is None: + logger.warn(ZONEMGR_NO_SOA, zone_name_class[0], zone_name_class[1]) + zone_info["zone_soa_rdata"] = None + zone_reload_jitter = 0 + else: + zone_info["zone_soa_rdata"] = zone_soa[7] + zone_reload_jitter = float(zone_soa[7].split(" ")[RETRY_OFFSET]) zone_info["zone_state"] = ZONE_OK zone_info["last_refresh_time"] = self._get_current_time() self._zonemgr_refresh_info[zone_name_class] = zone_info # Imposes some random jitters to avoid many zones need to do refresh at the same time. - zone_reload_jitter = float(zone_soa[7].split(" ")[RETRY_OFFSET]) zone_reload_jitter = max(self._lowerbound_retry, zone_reload_jitter) self._set_zone_timer(zone_name_class, zone_reload_jitter, self._reload_jitter * zone_reload_jitter) @@ -457,8 +463,7 @@ class ZonemgrRefresh: required[name_class] = True # Add it only if it isn't there already if not name_class in self._zonemgr_refresh_info: - # If we are not able to find it in database, simply skip - # it and log an error + # If we are not able to find it in database, log an warning self.zonemgr_add_zone(name_class) # Drop the zones that are no longer there # Do it in two phases, python doesn't like deleting while iterating From 2dfa0983e4680f321a3d4f1bd0d826abd88f455c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 18 Aug 2011 01:21:53 -0700 Subject: [PATCH 583/974] [1068] added a master source of writable data source DB file, and a make rule to generate a writable file for actual tests. --- src/lib/datasrc/tests/testdata/Makefile.am | 5 +++++ src/lib/datasrc/tests/testdata/rwtest.sqlite3 | Bin 0 -> 11264 bytes 2 files changed, 5 insertions(+) create mode 100644 src/lib/datasrc/tests/testdata/Makefile.am create mode 100644 src/lib/datasrc/tests/testdata/rwtest.sqlite3 diff --git a/src/lib/datasrc/tests/testdata/Makefile.am b/src/lib/datasrc/tests/testdata/Makefile.am new file mode 100644 index 0000000000..b4866d9471 --- /dev/null +++ b/src/lib/datasrc/tests/testdata/Makefile.am @@ -0,0 +1,5 @@ +CLEANFILES = *.copied +BUILT_SOURCES = rwtest.sqlite3.copied + +rwtest.sqlite3.copied: rwtest.sqlite3 + cp $(srcdir)/rwtest.sqlite3 $@ diff --git a/src/lib/datasrc/tests/testdata/rwtest.sqlite3 b/src/lib/datasrc/tests/testdata/rwtest.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..ce95a1d7feba6455bee4f98f98b66c87f60bc13d GIT binary patch literal 11264 zcmeI0&2G~`5XX1ze31$XBqSelU@lQ(5h|oU0Jy1BSs0t2B@NS&cn-ZlGf=GB)vArLY+1=mdKaSl#G>4G~uP4FSiC~xH2&Lo+ z03oCtA6xM;FQvFb=Z&R$s#~a*$;0pW^UPTO$p*@^i(rEdB!C1qLf}^Jc04cBChBq{ z0l-O-z@`YuRHAgI|4SlYZfY4gFA_ikf1kiTnkUt})H`#=rz3A?5}fQ97XOn^`CV=|Kp8*ocE&=xb5AXlk zjX&-@+XR^Yb2KD0q+g}a(zk5W_$Rx6MGK0m(rM%zkG!xy@WxK>trvvDi7&RKMoZT^ zI&`!>QwMPjYROl?usZsI-U7?+fYmik_586^R1}3?yyP>ypZH$LPqN^TcR_9FE<8MM z8PBxV5j@q8>fk$L589oUVI3@7L&G*rwj#@JXl=a?f!iNBVYtQ)_VuRLH9N3vSle}Q z{V??UuxHz*u31;k=T?u@rd&{}3hfPj*E{3u>>W?pf_OcbY{Rhj^%syH2HTpop~kG$ z3&<6fN`-z>IEldPPXaegE_s1Q(z%)ko2Qq8HOGBZQa9omMWaPDt|kcFXnMLn7~$|F zZFfCZPUI{{O6B^O6e9JcFR~O@upj|Knx&Ki{aCKMu>uE9IN Date: Thu, 18 Aug 2011 01:23:20 -0700 Subject: [PATCH 584/974] [1068] added clone() method for DatabaseAccessor, and used it in the updater factory. this made the remaining database tests successful with sqlite3 accessor. --- src/lib/datasrc/database.cc | 9 ++- src/lib/datasrc/database.h | 3 + src/lib/datasrc/sqlite3_accessor.cc | 22 +++++- src/lib/datasrc/sqlite3_accessor.h | 17 +++++ src/lib/datasrc/tests/database_unittest.cc | 71 ++++++++++++++----- .../tests/sqlite3_accessor_unittest.cc | 35 +++++++-- 6 files changed, 128 insertions(+), 29 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 5631bda1d9..7d050d6030 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -319,15 +319,14 @@ ZoneUpdaterPtr DatabaseClient::startUpdateZone(const isc::dns::Name& name, bool replace) const { - // TODO: create a dedicated accessor - - const std::pair zone(accessor_->startUpdateZone(name.toText(), - replace)); + shared_ptr update_accessor(accessor_->clone()); + const std::pair zone(update_accessor->startUpdateZone( + name.toText(), replace)); if (!zone.first) { return (ZoneUpdaterPtr()); } - return (ZoneUpdaterPtr(new Updater(accessor_, zone.second, + return (ZoneUpdaterPtr(new Updater(update_accessor, zone.second, name.toText(), rrclass_))); } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 4de7e2f279..950b22c7ef 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -171,6 +171,9 @@ public: DEL_RDATA = 2 ///< Full text representation of the record's RDATA }; + /// TBD + virtual boost::shared_ptr clone() = 0; + /// TBD virtual std::pair startUpdateZone(const std::string& zone_name, bool replace) = 0; diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 2959b9ae48..b13143442d 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -115,8 +115,9 @@ private: }; SQLite3Accessor::SQLite3Accessor(const std::string& filename, - const isc::dns::RRClass& rrclass) : + const isc::dns::RRClass& rrclass) : dbparameters_(new SQLite3Parameters), + filename_(filename), class_(rrclass.toText()), database_name_("sqlite3_" + isc::util::Filename(filename).nameAndExtension()) @@ -126,6 +127,25 @@ SQLite3Accessor::SQLite3Accessor(const std::string& filename, open(filename); } +SQLite3Accessor::SQLite3Accessor(const std::string& filename, + const string& rrclass) : + dbparameters_(new SQLite3Parameters), + filename_(filename), + class_(rrclass), + database_name_("sqlite3_" + + isc::util::Filename(filename).nameAndExtension()) +{ + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN); + + open(filename); +} + +boost::shared_ptr +SQLite3Accessor::clone() { + return (boost::shared_ptr(new SQLite3Accessor(filename_, + class_))); +} + namespace { // This is a helper class to initialize a Sqlite3 DB safely. An object of diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index bada46a32b..d9d7d59da4 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -68,12 +68,27 @@ public: */ SQLite3Accessor(const std::string& filename, const isc::dns::RRClass& rrclass); + + /** + * \brief Constructor + * + * Same as the other version, but takes rrclass as a bare string. + * we should obsolete the other version and unify the constructor to + * this version; the SQLite3Accessor is expected to be "dumb" and + * shouldn't care about DNS specific information such as RRClass. + */ + SQLite3Accessor(const std::string& filename, const std::string& rrclass); + /** * \brief Destructor * * Closes the database. */ ~SQLite3Accessor(); + + /// TBD + virtual boost::shared_ptr clone(); + /** * \brief Look up a zone * @@ -174,6 +189,8 @@ private: /// \brief Private database data SQLite3Parameters* dbparameters_; + /// \brief The filename of the DB (necessary for clone()) + const std::string filename_; /// \brief The class for which the queries are done const std::string class_; /// \brief Opens the database diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 5b65a1f5ef..50cfd3d0c3 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -137,9 +137,21 @@ public: MockAccessor() : search_running_(false), rollbacked_(false), database_name_("mock_database") { + readonly_records = &readonly_records_master; + update_records = &update_records_master; + empty_records = &empty_records_master; fillData(); } + virtual shared_ptr clone() { + shared_ptr cloned_accessor(new MockAccessor()); + cloned_accessor->readonly_records = &readonly_records_master; + cloned_accessor->update_records = &update_records_master; + cloned_accessor->empty_records = &empty_records_master; + latest_clone_ = cloned_accessor; + return (cloned_accessor); + } + virtual pair getZone(const Name& name) const { return (getZone(name.toText())); } @@ -217,15 +229,15 @@ public: // we use an empty set; otherwise we use a writable copy of the // original. if (replace) { - update_records.clear(); + update_records->clear(); } else { - update_records = readonly_records; + *update_records = *readonly_records; } return (pair(true, WRITABLE_ZONE_ID)); } virtual void commitUpdateZone() { - readonly_records = update_records; + *readonly_records = *update_records; } virtual void rollbackUpdateZone() { rollbacked_ = true; @@ -233,13 +245,13 @@ public: virtual void addRecordToZone(const vector& columns) { // Copy the current value to cur_name. If it doesn't exist, // operator[] will create a new one. - cur_name = update_records[columns[DatabaseAccessor::ADD_NAME]]; + cur_name = (*update_records)[columns[DatabaseAccessor::ADD_NAME]]; addRecord(columns[DatabaseAccessor::ADD_TYPE], columns[DatabaseAccessor::ADD_TTL], columns[DatabaseAccessor::ADD_SIGTYPE], columns[DatabaseAccessor::ADD_RDATA]); // copy back the added entry. - update_records[columns[DatabaseAccessor::ADD_NAME]] = cur_name; + (*update_records)[columns[DatabaseAccessor::ADD_NAME]] = cur_name; // remember this one so that test cases can check it. columns_lastadded = columns; @@ -259,16 +271,16 @@ public: virtual void deleteRecordInZone(const vector& params) { vector > records = - update_records[params[DatabaseAccessor::DEL_NAME]]; + (*update_records)[params[DatabaseAccessor::DEL_NAME]]; records.erase(remove_if(records.begin(), records.end(), deleteMatch( params[DatabaseAccessor::DEL_TYPE], params[DatabaseAccessor::DEL_RDATA])), records.end()); if (records.empty()) { - update_records.erase(params[DatabaseAccessor::DEL_NAME]); + (*update_records).erase(params[DatabaseAccessor::DEL_NAME]); } else { - update_records[params[DatabaseAccessor::DEL_NAME]] = records; + (*update_records)[params[DatabaseAccessor::DEL_NAME]] = records; } } @@ -287,10 +299,18 @@ public: const vector& getLastAdded() const { return (columns_lastadded); } + + // This allows the test code to get the accessor used in an update context + shared_ptr getLatestClone() const { + return (latest_clone_); + } private: - RECORDS readonly_records; - RECORDS update_records; - RECORDS empty_records; + RECORDS readonly_records_master; + RECORDS* readonly_records; + RECORDS update_records_master; + RECORDS* update_records; + RECORDS empty_records_master; + RECORDS* empty_records; // used as internal index for getNextRecord() size_t cur_record; @@ -316,13 +336,15 @@ private: const std::string database_name_; + boost::shared_ptr latest_clone_; + const RECORDS& getRecords(int zone_id) const { if (zone_id == READONLY_ZONE_ID) { - return (readonly_records); + return (*readonly_records); } else if (zone_id == WRITABLE_ZONE_ID) { - return (update_records); + return (*update_records); } - return (empty_records); + return (*empty_records); } // Adds one record to the current name in the database @@ -344,8 +366,8 @@ private: // to the actual fake database. This will clear cur_name, // so we can immediately start adding new records. void addCurName(const std::string& name) { - ASSERT_EQ(0, readonly_records.count(name)); - readonly_records[name] = cur_name; + ASSERT_EQ(0, readonly_records->count(name)); + (*readonly_records)[name] = cur_name; cur_name.clear(); } @@ -451,9 +473,9 @@ protected: bool isRollbacked(bool expected = false) const { if (is_mock_) { - const MockAccessor* mock_accessor = - dynamic_cast(current_accessor_); - return (mock_accessor->isRollbacked()); + const MockAccessor& mock_accessor = + dynamic_cast(*update_accessor_); + return (mock_accessor.isRollbacked()); } else { return (expected); } @@ -471,6 +493,14 @@ protected: } } + void setUpdateAccessor() { + if (is_mock_) { + const MockAccessor* mock_accessor = + dynamic_cast(current_accessor_); + update_accessor_ = mock_accessor->getLatestClone(); + } + } + // Some tests only work for MockAccessor. We remember whether our accessor // is of that type. bool is_mock_; @@ -492,6 +522,7 @@ protected: RRsetPtr rrsigset; // for adding/deleting an RRset ZoneUpdaterPtr updater; + shared_ptr update_accessor_; const std::vector empty_rdatas; // for NXRRSET/NXDOMAIN std::vector expected_rdatas; std::vector expected_sig_rdatas; @@ -921,6 +952,7 @@ TYPED_TEST(DatabaseClientTest, flushZone) { // start update in the replace mode. the normal finder should still // be able to see the record, but the updater's finder shouldn't. this->updater = this->client_->startUpdateZone(this->zname, true); + this->setUpdateAccessor(); EXPECT_EQ(ZoneFinder::SUCCESS, this->finder->find(this->qname, this->qtype).code); EXPECT_EQ(ZoneFinder::NXDOMAIN, @@ -944,6 +976,7 @@ TYPED_TEST(DatabaseClientTest, updateCancel) { this->qtype).code); this->updater = this->client_->startUpdateZone(this->zname, true); + this->setUpdateAccessor(); EXPECT_EQ(ZoneFinder::NXDOMAIN, this->updater->getFinder().find(this->qname, this->qtype).code); // DB should not have been rolled back yet. diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 669ac2d081..73c00d9af4 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -248,6 +248,33 @@ TEST_F(SQLite3AccessorTest, getRecords) { "33495 example.com. FAKEFAKEFAKEFAKE"); } +TEST_F(SQLite3AccessorTest, clone) { + shared_ptr cloned = accessor->clone(); + EXPECT_EQ(accessor->getDBName(), cloned->getDBName()); + + // The cloned accessor should have a separate connection and search + // context, so it should be able to perform search in concurrent with + // the original accessor. + string columns1[DatabaseAccessor::COLUMN_COUNT]; + string columns2[DatabaseAccessor::COLUMN_COUNT]; + + const std::pair zone_info1( + accessor->getZone(Name("example.com"))); + const std::pair zone_info2( + accessor->getZone(Name("example.com"))); + + accessor->searchForRecords(zone_info1.second, "foo.example.com."); + ASSERT_TRUE(accessor->getNextRecord(columns1, + DatabaseAccessor::COLUMN_COUNT)); + checkRecordRow(columns1, "CNAME", "3600", "", "cnametest.example.org."); + + + cloned->searchForRecords(zone_info2.second, "foo.example.com."); + ASSERT_TRUE(cloned->getNextRecord(columns2, + DatabaseAccessor::COLUMN_COUNT)); + checkRecordRow(columns2, "CNAME", "3600", "", "cnametest.example.org."); +} + // // Commonly used data for update tests // @@ -274,9 +301,8 @@ protected: TEST_DATA_BUILDDIR "/test.sqlite3.copied")); initAccessor(TEST_DATA_BUILDDIR "/test.sqlite3.copied", RRClass::IN()); zone_id = accessor->getZone(Name("example.com")).second; - another_accessor.reset(new SQLite3Accessor( - TEST_DATA_BUILDDIR "/test.sqlite3.copied", - RRClass::IN())); + another_accessor = boost::dynamic_pointer_cast( + accessor->clone()); expected_stored.push_back(common_expected_data); } @@ -287,7 +313,8 @@ protected: vector expected_stored; // placeholder for checkRecords vector empty_stored; // indicate no corresponding data - // Another accessor, emulating one running on a different process/thread + // Another accessor, emulating one running on a different process/thread. + // It will also confirm clone() works as expected in terms of update. shared_ptr another_accessor; }; From 9a8667331d9a7179331516e7bb1f3aa942bf8218 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 10:29:31 +0200 Subject: [PATCH 585/974] [master] cppcheck fix (uninitialized member var) --- src/lib/datasrc/tests/database_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 76862273fc..7627aaee5b 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -82,7 +82,7 @@ private: */ class MockAccessor : public NopAccessor { public: - MockAccessor() : search_running_(false) + MockAccessor() : cur_record(0), search_running_(false) { fillData(); } From 568a8cc472f3207b44b92428e7ac40338d9ede37 Mon Sep 17 00:00:00 2001 From: zhanglikun Date: Thu, 18 Aug 2011 16:37:15 +0800 Subject: [PATCH 586/974] [519] Add unit test for the change to boss process. Catch the timeout exception when boss receiving the answer from stats. --- src/bin/bind10/bind10_src.py.in | 5 ++++- src/bin/bind10/tests/bind10_test.py.in | 6 ++++++ src/bin/stats/stats.py.in | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 5da78bfd93..81b24f15ff 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -329,7 +329,10 @@ class BoB: 'set', self._get_stats_data()) seq = self.cc_session.group_sendmsg(cmd, 'Stats') # Consume the answer, in case it becomes a orphan message. - self.cc_session.group_recvmsg(False, seq) + try: + self.cc_session.group_recvmsg(False, seq) + except isc.cc.session.SessionTimeout: + pass answer = isc.config.ccsession.create_answer(0) elif command == "ping": answer = isc.config.ccsession.create_answer(0, "pong") diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 077190c865..424a610803 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -147,6 +147,12 @@ class TestBoB(unittest.TestCase): self.assertEqual(bob.command_handler("shutdown", None), isc.config.ccsession.create_answer(0)) self.assertFalse(bob.runnable) + # "getstats" command + self.assertEqual(bob.command_handler("getstats", None), + isc.config.ccsession.create_answer(0, + { "stats_data": { + 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + }})) # "sendstats" command self.assertEqual(bob.command_handler("sendstats", None), isc.config.ccsession.create_answer(0)) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 022900a344..51d712b33d 100644 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -215,7 +215,9 @@ class CCSessionListener(Listener): def _update_stats_data(self, args): # 'args' must be dictionary type - self.stats_data.update(args['stats_data']) + if isinstance(args, dict) and isinstance(args.get('stats_data'), dict): + self.stats_data.update(args['stats_data']) + # overwrite "stats.LastUpdateTime" self.stats_data['stats.last_update_time'] = get_datetime() From 171088e69ff96a2e242cfdf98e8d1f0415d4c172 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 12:01:50 +0200 Subject: [PATCH 587/974] [1183] add initial use of iterator context for getRecords() (to replace searchForRecords->getNextRecord combo) --- src/lib/datasrc/database.cc | 12 +- src/lib/datasrc/database.h | 32 +++++ src/lib/datasrc/sqlite3_accessor.cc | 59 ++++++++- src/lib/datasrc/sqlite3_accessor.h | 7 +- src/lib/datasrc/tests/database_unittest.cc | 75 +++++++++++ .../tests/sqlite3_accessor_unittest.cc | 119 +++++++++--------- 6 files changed, 238 insertions(+), 66 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index f0d97673e0..7359bf5c1f 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -178,12 +178,20 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, bool want_ns) { RRsigStore sig_store; - database_->searchForRecords(zone_id_, name.toText()); bool records_found = false; isc::dns::RRsetPtr result_rrset; + // Request the context + DatabaseAccessor::IteratorContextPtr + context(database_->getRecords(name, zone_id_)); + // It must not return NULL, that's a bug of the implementation + if (context == DatabaseAccessor::IteratorContextPtr()) { + isc_throw(isc::Unexpected, "Iterator context null at " + + name.toText()); + } + std::string columns[DatabaseAccessor::COLUMN_COUNT]; - while (database_->getNextRecord(columns, DatabaseAccessor::COLUMN_COUNT)) { + while (context->getNext(columns, DatabaseAccessor::COLUMN_COUNT)) { if (!records_found) { records_found = true; } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index b0f2b090ef..47e9083621 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -124,6 +124,38 @@ public: typedef boost::shared_ptr IteratorContextPtr; + /** + * \brief Creates an iterator context for a specific name. + * + * This should create a new iterator context to be used by + * DatabaseConnection's ZoneIterator. It can be created based on the name + * or the ID (returned from getZone()), what is more comfortable for the + * database implementation. Both are provided (and are guaranteed to match, + * the DatabaseClient first looks up the zone ID and then calls this). + * + * The default implementation throws isc::NotImplemented, to allow + * "minimal" implementations of the connection not supporting optional + * functionality. + * + * \param name The name to search for. + * \param id The ID of the zone, returned from getZone(). + * \return Newly created iterator context. Must not be NULL. + */ + virtual IteratorContextPtr getRecords(const isc::dns::Name& name, + int id) const + { + /* + * This is a compromise. We need to document the parameters in doxygen, + * so they need a name, but then it complains about unused parameter. + * This is a NOP that "uses" the parameters. + */ + static_cast(name); + static_cast(id); + + isc_throw(isc::NotImplemented, + "This database datasource can't be iterated"); + } + /** * \brief Creates an iterator context for the whole zone. * diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index bab7d2966a..4afb01f1c2 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -368,7 +368,10 @@ convertToPlainChar(const unsigned char* ucp, // it is, just provide data from it. class SQLite3Database::Context : public DatabaseAccessor::IteratorContext { public: + // Construct an iterator for all records. When constructed this + // way, the getNext() call will copy all fields Context(const boost::shared_ptr& database, int id) : + iterator_type_(ITT_ALL), database_(database), statement(NULL) { @@ -379,6 +382,30 @@ public: " to SQL statement (iterate)"); } } + + // Construct an iterator for records with a specific name. When constructed + // this way, the getNext() call will copy all fields except name + Context(const boost::shared_ptr& database, int id, + const isc::dns::Name& name) : + iterator_type_(ITT_NAME), + database_(database), + statement(NULL) + { + // We create the statement now and then just keep getting data from it + // TODO move to private and clean up error + statement = prepare(database->dbparameters_->db_, q_any_str); + if (sqlite3_bind_int(statement, 1, id) != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not bind " << id << + " to SQL statement"); + } + if (sqlite3_bind_text(statement, 2, name.toText().c_str(), -1, + SQLITE_TRANSIENT) != SQLITE_OK) { + sqlite3_finalize(statement); + isc_throw(SQLite3Error, "Could not bind " << id << + " to SQL statement"); + } + } + bool getNext(std::string data[], size_t size) { if (size != COLUMN_COUNT) { isc_throw(DataSourceError, "getNext received size of " << size << @@ -387,9 +414,14 @@ public: // If there's another row, get it int rc(sqlite3_step(statement)); if (rc == SQLITE_ROW) { - for (size_t i(0); i < size; ++ i) { - data[i] = convertToPlainChar(sqlite3_column_text(statement, i), - database_->dbparameters_); + // For both types, we copy the first four columns + copyColumn(data, TYPE_COLUMN); + copyColumn(data, TTL_COLUMN); + copyColumn(data, SIGTYPE_COLUMN); + copyColumn(data, RDATA_COLUMN); + // Only copy Name if we are iterating over every record + if (iterator_type_ == ITT_ALL) { + copyColumn(data, NAME_COLUMN); } return (true); } else if (rc != SQLITE_DONE) { @@ -399,6 +431,7 @@ public: } return (false); } + virtual ~Context() { if (statement) { sqlite3_finalize(statement); @@ -406,10 +439,30 @@ public: } private: + // Depending on which constructor is called, behaviour is slightly + // different. We keep track of what to do with the iterator type + // See description of getNext() and the constructors + enum IteratorType { + ITT_ALL, + ITT_NAME + }; + + void copyColumn(std::string data[], int column) { + data[column] = convertToPlainChar(sqlite3_column_text(statement, + column), + database_->dbparameters_); + } + + IteratorType iterator_type_; boost::shared_ptr database_; sqlite3_stmt *statement; }; +DatabaseAccessor::IteratorContextPtr +SQLite3Database::getRecords(const isc::dns::Name& name, int id) const { + return (IteratorContextPtr(new Context(shared_from_this(), id, name))); +} + DatabaseAccessor::IteratorContextPtr SQLite3Database::getAllRecords(const isc::dns::Name&, int id) const { return (IteratorContextPtr(new Context(shared_from_this(), id))); diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index f75207a6da..6678274f99 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -91,9 +91,14 @@ public: */ virtual std::pair getZone(const isc::dns::Name& name) const; + /// \brief Implementation of DatabaseAbstraction::getRecords + virtual IteratorContextPtr getRecords(const isc::dns::Name& name, + int id) const; + /// \brief Implementation of DatabaseAbstraction::getAllRecords virtual IteratorContextPtr getAllRecords(const isc::dns::Name&, - int id) const; + int id) const; + /** * \brief Start a new search for the given name in the given zone. * diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 7627aaee5b..165d498d84 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -87,6 +87,65 @@ public: fillData(); } private: + class MockNameIteratorContext : public IteratorContext { + public: + MockNameIteratorContext(const MockAccessor& mock_accessor, int zone_id, + const isc::dns::Name& name) : + searched_name_(name.toText()), cur_record_(0) + { + // 'hardcoded' name to trigger exceptions (for testing + // the error handling of find() (the other on is below in + // if the name is "exceptiononsearch" it'll raise an exception here + if (searched_name_ == "dsexception.in.search.") { + isc_throw(DataSourceError, "datasource exception on search"); + } else if (searched_name_ == "iscexception.in.search.") { + isc_throw(isc::Exception, "isc exception on search"); + } else if (searched_name_ == "basicexception.in.search.") { + throw std::exception(); + } + + // we're not aiming for efficiency in this test, simply + // copy the relevant vector from records + if (zone_id == 42) { + if (mock_accessor.records.count(searched_name_) > 0) { + cur_name = mock_accessor.records.find(searched_name_)->second; + } else { + cur_name.clear(); + } + } else { + cur_name.clear(); + } + } + + virtual bool getNext(std::string columns[], size_t column_count) { + if (searched_name_ == "dsexception.in.getnext.") { + isc_throw(DataSourceError, "datasource exception on getnextrecord"); + } else if (searched_name_ == "iscexception.in.getnext.") { + isc_throw(isc::Exception, "isc exception on getnextrecord"); + } else if (searched_name_ == "basicexception.in.getnext.") { + throw std::exception(); + } + + if (column_count != DatabaseAccessor::COLUMN_COUNT) { + isc_throw(DataSourceError, "Wrong column count in getNextRecord"); + } + if (cur_record_ < cur_name.size()) { + for (size_t i = 0; i < column_count; ++i) { + columns[i] = cur_name[cur_record_][i]; + } + cur_record_++; + return (true); + } else { + return (false); + } + } + + private: + const std::string searched_name_; + int cur_record_; + std::vector< std::vector > cur_name; + }; + class MockIteratorContext : public IteratorContext { private: int step; @@ -194,6 +253,20 @@ public: } } + virtual IteratorContextPtr getRecords(const Name& name, int id) const { + if (id == 42) { + return (IteratorContextPtr(new MockNameIteratorContext(*this, id, name))); + } else if (id == 13) { + return (IteratorContextPtr()); + } else if (id == 0) { + return (IteratorContextPtr(new EmptyIteratorContext())); + } else if (id == -1) { + return (IteratorContextPtr(new BadIteratorContext())); + } else { + isc_throw(isc::Unexpected, "Unknown zone ID"); + } + } + virtual void searchForRecords(int zone_id, const std::string& name) { search_running_ = true; @@ -448,6 +521,8 @@ private: // This tests the default getAllRecords behaviour, throwing NotImplemented TEST(DatabaseConnectionTest, getAllRecords) { // The parameters don't matter + EXPECT_THROW(NopAccessor().getRecords(Name("."), 1), + isc::NotImplemented); EXPECT_THROW(NopAccessor().getAllRecords(Name("."), 1), isc::NotImplemented); } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index a6314498f7..27c07e3578 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -181,92 +181,91 @@ TEST_F(SQLite3Access, getRecords) { const size_t column_count = DatabaseAccessor::COLUMN_COUNT; std::string columns[column_count]; + // TODO: can't do this anymore // without search, getNext() should return false - EXPECT_FALSE(db->getNextRecord(columns, column_count)); + //EXPECT_FALSE(context->getNext(columns, column_count)); + //checkRecordRow(columns, "", "", "", "", ""); + + DatabaseAccessor::IteratorContextPtr + context(db->getRecords(Name("foo.bar"), 1)); + ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), + context); + EXPECT_FALSE(context->getNext(columns, column_count)); checkRecordRow(columns, "", "", "", "", ""); - db->searchForRecords(zone_id, "foo.bar."); - EXPECT_FALSE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "", "", "", "", ""); - - db->searchForRecords(zone_id, ""); - EXPECT_FALSE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "", "", "", "", ""); + // TODO can't pass incomplete name anymore + //context = db->getRecords(Name(""), zone_id); + //EXPECT_FALSE(context->getNext(columns, column_count)); + //checkRecordRow(columns, "", "", "", "", ""); // Should error on a bad number of columns - EXPECT_THROW(db->getNextRecord(columns, 4), DataSourceError); - EXPECT_THROW(db->getNextRecord(columns, 6), DataSourceError); + EXPECT_THROW(context->getNext(columns, 4), DataSourceError); + EXPECT_THROW(context->getNext(columns, 6), DataSourceError); // now try some real searches - db->searchForRecords(zone_id, "foo.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + context = db->getRecords(Name("foo.example.com."), zone_id); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "CNAME", "3600", "", - "cnametest.example.org.", "foo.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "cnametest.example.org.", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "CNAME", "CNAME 5 3 3600 20100322084538 20100220084538 33495 " - "example.com. FAKEFAKEFAKEFAKE", "foo.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "example.com. FAKEFAKEFAKEFAKE", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "NSEC", "7200", "", - "mail.example.com. CNAME RRSIG NSEC", "foo.example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "mail.example.com. CNAME RRSIG NSEC", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " - "example.com. FAKEFAKEFAKEFAKE", "foo.example.com."); - EXPECT_FALSE(db->getNextRecord(columns, column_count)); + "example.com. FAKEFAKEFAKEFAKE", ""); + EXPECT_FALSE(context->getNext(columns, column_count)); // with no more records, the array should not have been modified checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " - "example.com. FAKEFAKEFAKEFAKE", "foo.example.com."); + "example.com. FAKEFAKEFAKEFAKE", ""); - db->searchForRecords(zone_id, "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + context = db->getRecords(Name("example.com."), zone_id); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "SOA", "3600", "", "master.example.com. admin.example.com. " - "1234 3600 1800 2419200 7200", "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "1234 3600 1800 2419200 7200", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "SOA", "SOA 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "NS", "1200", "", "dns01.example.com.", - "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "NS", "3600", "", "dns02.example.com.", - "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "NS", "1800", "", "dns03.example.com.", - "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "33495 example.com. FAKEFAKEFAKEFAKE", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); + checkRecordRow(columns, "NS", "1200", "", "dns01.example.com.", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); + checkRecordRow(columns, "NS", "3600", "", "dns02.example.com.", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); + checkRecordRow(columns, "NS", "1800", "", "dns03.example.com.", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "NS", "NS 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE", - "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); - checkRecordRow(columns, "MX", "3600", "", "10 mail.example.com.", - "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "33495 example.com. FAKEFAKEFAKEFAKE", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); + checkRecordRow(columns, "MX", "3600", "", "10 mail.example.com.", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "MX", "3600", "", - "20 mail.subzone.example.com.", "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "20 mail.subzone.example.com.", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "MX", "MX 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "33495 example.com. FAKEFAKEFAKEFAKE", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "NSEC", "7200", "", - "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY", - "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 2 7200 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "33495 example.com. FAKEFAKEFAKEFAKE", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "DNSKEY", "3600", "", "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4q NGV7W" "cTD0WEiuV7IjXgHE36fCmS9QsUxSSOV o1I/FMxI2PJVqTYHkX" "FBS7AzLGsQYMU7UjBZ SotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g" - "0qcbW YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx", "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "0qcbW YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "DNSKEY", "3600", "", "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJ tbvzg" "62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV 4HQZJStJaZ+fHU5AwV" @@ -275,20 +274,20 @@ TEST_F(SQLite3Access, getRecords) { "qiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq 23TaOrVTjB7d1a/h31OD" "fiHAxFHrkY3t3D5J R9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86" "Acv RyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGf oIK/aKwEN" - "rsjcKZZj660b1M=", "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "rsjcKZZj660b1M=", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " - "4456 example.com. FAKEFAKEFAKEFAKE", "example.com."); - ASSERT_TRUE(db->getNextRecord(columns, column_count)); + "4456 example.com. FAKEFAKEFAKEFAKE", ""); + ASSERT_TRUE(context->getNext(columns, column_count)); checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); - EXPECT_FALSE(db->getNextRecord(columns, column_count)); + "33495 example.com. FAKEFAKEFAKEFAKE", ""); + EXPECT_FALSE(context->getNext(columns, column_count)); // getnextrecord returning false should mean array is not altered checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " - "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); + "33495 example.com. FAKEFAKEFAKEFAKE", ""); } } // end anonymous namespace From d88becea33630677dbb5123cd72fa8695512311a Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 12:19:56 +0200 Subject: [PATCH 588/974] [1183] remove searchForRecords(), getNextRecord() and resetSearch() since they can now be done through getRecords() --- src/lib/datasrc/database.cc | 4 +- src/lib/datasrc/database.h | 54 --------- src/lib/datasrc/sqlite3_accessor.cc | 61 ---------- src/lib/datasrc/sqlite3_accessor.h | 46 ------- src/lib/datasrc/tests/database_unittest.cc | 134 +-------------------- 5 files changed, 3 insertions(+), 296 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 7359bf5c1f..48b894608f 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -362,22 +362,20 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_status = CNAME; } } + // TODO: some of these can be removed } catch (const DataSourceError& dse) { logger.error(DATASRC_DATABASE_FIND_ERROR) .arg(database_->getDBName()).arg(dse.what()); // call cleanup and rethrow - database_->resetSearch(); throw; } catch (const isc::Exception& isce) { logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR) .arg(database_->getDBName()).arg(isce.what()); // cleanup, change it to a DataSourceError and rethrow - database_->resetSearch(); isc_throw(DataSourceError, isce.what()); } catch (const std::exception& ex) { logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR) .arg(database_->getDBName()).arg(ex.what()); - database_->resetSearch(); throw; } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 47e9083621..cf7b495540 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -188,60 +188,6 @@ public: "This database datasource can't be iterated"); } - /** - * \brief Starts a new search for records of the given name in the given zone - * - * The data searched by this call can be retrieved with subsequent calls to - * getNextRecord(). - * - * \exception DataSourceError if there is a problem connecting to the - * backend database - * - * \param zone_id The zone to search in, as returned by getZone() - * \param name The name of the records to find - */ - virtual void searchForRecords(int zone_id, const std::string& name) = 0; - - /** - * \brief Retrieves the next record from the search started with searchForRecords() - * - * Returns a boolean specifying whether or not there was more data to read. - * In the case of a database error, a DatasourceError is thrown. - * - * The columns passed is an array of std::strings consisting of - * DatabaseConnection::COLUMN_COUNT elements, the elements of which - * are defined in DatabaseConnection::RecordColumns, in their basic - * string representation. - * - * If you are implementing a derived database connection class, you - * should have this method check the column_count value, and fill the - * array with strings conforming to their description in RecordColumn. - * - * \exception DatasourceError if there was an error reading from the database - * - * \param columns The elements of this array will be filled with the data - * for one record as defined by RecordColumns - * If there was no data, the array is untouched. - * \return true if there was a next record, false if there was not - */ - virtual bool getNextRecord(std::string columns[], size_t column_count) = 0; - - /** - * \brief Resets the current search initiated with searchForRecords() - * - * This method will be called when the called of searchForRecords() and - * getNextRecord() finds bad data, and aborts the current search. - * It should clean up whatever handlers searchForRecords() created, and - * any other state modified or needed by getNextRecord() - * - * Of course, the implementation of getNextRecord may also use it when - * it is done with a search. If it does, the implementation of this - * method should make sure it can handle being called multiple times. - * - * The implementation for this method should make sure it never throws. - */ - virtual void resetSearch() = 0; - /** * Definitions of the fields as they are required to be filled in * by getNextRecord() diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 4afb01f1c2..9cb9ca01b6 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -362,10 +362,6 @@ convertToPlainChar(const unsigned char* ucp, } } -// TODO: Once we want to have iterator returned from searchForRecords, this -// class can be reused. It should be modified to take the sqlite3 statement -// instead of creating it in constructor, it doesn't have to care which one -// it is, just provide data from it. class SQLite3Database::Context : public DatabaseAccessor::IteratorContext { public: // Construct an iterator for all records. When constructed this @@ -468,62 +464,5 @@ SQLite3Database::getAllRecords(const isc::dns::Name&, int id) const { return (IteratorContextPtr(new Context(shared_from_this(), id))); } -void -SQLite3Database::searchForRecords(int zone_id, const std::string& name) { - resetSearch(); - if (sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id) != SQLITE_OK) { - isc_throw(DataSourceError, - "Error in sqlite3_bind_int() for zone_id " << - zone_id << ": " << sqlite3_errmsg(dbparameters_->db_)); - } - // use transient since name is a ref and may disappear - if (sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1, - SQLITE_TRANSIENT) != SQLITE_OK) { - isc_throw(DataSourceError, - "Error in sqlite3_bind_text() for name " << - name << ": " << sqlite3_errmsg(dbparameters_->db_)); - } -} - -bool -SQLite3Database::getNextRecord(std::string columns[], size_t column_count) { - if (column_count != COLUMN_COUNT) { - isc_throw(DataSourceError, - "Datasource backend caller did not pass a column array " - "of size " << COLUMN_COUNT << " to getNextRecord()"); - } - - sqlite3_stmt* current_stmt = dbparameters_->q_any_; - const int rc = sqlite3_step(current_stmt); - - if (rc == SQLITE_ROW) { - for (int column = 0; column < column_count; ++column) { - try { - columns[column] = convertToPlainChar(sqlite3_column_text( - current_stmt, column), - dbparameters_); - } catch (const std::bad_alloc&) { - isc_throw(DataSourceError, - "bad_alloc in Sqlite3Connection::getNextRecord"); - } - } - return (true); - } else if (rc == SQLITE_DONE) { - // reached the end of matching rows - resetSearch(); - return (false); - } - isc_throw(DataSourceError, "Unexpected failure in sqlite3_step: " << - sqlite3_errmsg(dbparameters_->db_)); - // Compilers might not realize isc_throw always throws - return (false); -} - -void -SQLite3Database::resetSearch() { - sqlite3_reset(dbparameters_->q_any_); - sqlite3_clear_bindings(dbparameters_->q_any_); -} - } } diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 6678274f99..32d174a3cd 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -99,52 +99,6 @@ public: virtual IteratorContextPtr getAllRecords(const isc::dns::Name&, int id) const; - /** - * \brief Start a new search for the given name in the given zone. - * - * This implements the searchForRecords from DatabaseConnection. - * This particular implementation does not raise DataSourceError. - * - * \exception DataSourceError when sqlite3_bind_int() or - * sqlite3_bind_text() fails - * - * \param zone_id The zone to seach in, as returned by getZone() - * \param name The name to find records for - */ - virtual void searchForRecords(int zone_id, const std::string& name); - - /** - * \brief Retrieve the next record from the search started with - * searchForRecords - * - * This implements the getNextRecord from DatabaseConnection. - * See the documentation there for more information. - * - * If this method raises an exception, the contents of columns are undefined. - * - * \exception DataSourceError if there is an error returned by sqlite_step() - * When this exception is raised, the current - * search as initialized by searchForRecords() is - * NOT reset, and the caller is expected to take - * care of that. - * \param columns This vector will be cleared, and the fields of the record will - * be appended here as strings (in the order rdtype, ttl, sigtype, - * and rdata). If there was no data (i.e. if this call returns - * false), the vector is untouched. - * \return true if there was a next record, false if there was not - */ - virtual bool getNextRecord(std::string columns[], size_t column_count); - - /** - * \brief Resets any state created by searchForRecords - * - * This implements the resetSearch from DatabaseConnection. - * See the documentation there for more information. - * - * This function never throws. - */ - virtual void resetSearch(); - /// The SQLite3 implementation of this method returns a string starting /// with a fixed prefix of "sqlite3_" followed by the DB file name /// removing any path name. For example, for the DB file diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 165d498d84..5d7663052b 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -62,12 +62,6 @@ public: return (database_name_); } - // These are just to compile, they won't be called - virtual void searchForRecords(int, const std::string&) { } - virtual bool getNextRecord(string*, size_t) { - return (false); - } - virtual void resetSearch() { } private: const std::string database_name_; @@ -82,7 +76,7 @@ private: */ class MockAccessor : public NopAccessor { public: - MockAccessor() : cur_record(0), search_running_(false) + MockAccessor() { fillData(); } @@ -267,83 +261,11 @@ public: } } - virtual void searchForRecords(int zone_id, const std::string& name) { - search_running_ = true; - - // 'hardcoded' name to trigger exceptions (for testing - // the error handling of find() (the other on is below in - // if the name is "exceptiononsearch" it'll raise an exception here - if (name == "dsexception.in.search.") { - isc_throw(DataSourceError, "datasource exception on search"); - } else if (name == "iscexception.in.search.") { - isc_throw(isc::Exception, "isc exception on search"); - } else if (name == "basicexception.in.search.") { - throw std::exception(); - } - searched_name_ = name; - - // we're not aiming for efficiency in this test, simply - // copy the relevant vector from records - cur_record = 0; - if (zone_id == 42) { - if (records.count(name) > 0) { - cur_name = records.find(name)->second; - } else { - cur_name.clear(); - } - } else { - cur_name.clear(); - } - }; - - virtual bool getNextRecord(std::string columns[], size_t column_count) { - if (searched_name_ == "dsexception.in.getnext.") { - isc_throw(DataSourceError, "datasource exception on getnextrecord"); - } else if (searched_name_ == "iscexception.in.getnext.") { - isc_throw(isc::Exception, "isc exception on getnextrecord"); - } else if (searched_name_ == "basicexception.in.getnext.") { - throw std::exception(); - } - - if (column_count != DatabaseAccessor::COLUMN_COUNT) { - isc_throw(DataSourceError, "Wrong column count in getNextRecord"); - } - if (cur_record < cur_name.size()) { - for (size_t i = 0; i < column_count; ++i) { - columns[i] = cur_name[cur_record][i]; - } - cur_record++; - return (true); - } else { - resetSearch(); - return (false); - } - }; - - virtual void resetSearch() { - search_running_ = false; - }; - - bool searchRunning() const { - return (search_running_); - } private: std::map > > records; - // used as internal index for getNextRecord() - size_t cur_record; - // used as temporary storage after searchForRecord() and during - // getNextRecord() calls, as well as during the building of the - // fake data + // used as temporary storageduring the building of the fake data std::vector< std::vector > cur_name; - // This boolean is used to make sure find() calls resetSearch - // when it encounters an error - bool search_running_; - - // We store the name passed to searchForRecords, so we can - // hardcode some exceptions into getNextRecord - std::string searched_name_; - // Adds one record to the current name in the database // The actual data will not be added to 'records' until // addCurName() is called @@ -567,7 +489,6 @@ public: shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); EXPECT_EQ(42, finder->zone_id()); - EXPECT_FALSE(current_database_->searchRunning()); return (finder); } @@ -753,7 +674,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -764,7 +684,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -775,7 +694,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -784,7 +702,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -794,7 +711,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::CNAME, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -804,7 +720,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -813,7 +728,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -825,7 +739,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -837,7 +750,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -846,7 +758,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -857,7 +768,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::CNAME, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -869,7 +779,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -881,7 +790,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -890,7 +798,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -901,7 +808,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::CNAME, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); @@ -913,7 +819,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -924,7 +829,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -935,7 +839,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -946,7 +849,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(360), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_sig_rdatas_.clear(); @@ -957,77 +859,63 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(360), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badcname2.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badcname3.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("badsig.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); // Trigger the hardcoded exceptions and see if find() has cleaned up EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), std::exception); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), std::exception); - EXPECT_FALSE(current_database_->searchRunning()); // This RRSIG has the wrong sigtype field, which should be // an error if we decide to keep using that field @@ -1041,7 +929,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); } TEST_F(DatabaseClientTest, findDelegation) { @@ -1056,7 +943,6 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); expected_rdatas_.push_back("ns.example.com."); @@ -1066,7 +952,6 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRType::NS(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); // Check when we ask for something below delegation point, we get the NS // (Both when the RRset there exists and doesn't) @@ -1081,7 +966,6 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("delegation.example.org.")); - EXPECT_FALSE(current_database_->searchRunning()); doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, @@ -1092,7 +976,6 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("delegation.example.org.")); - EXPECT_FALSE(current_database_->searchRunning()); // Even when we check directly at the delegation point, we should get // the NS @@ -1100,14 +983,12 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); // And when we ask direcly for the NS, we should still get delegation doFindTest(finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); // Now test delegation. If it is below the delegation point, we should get // the DNAME (the one with data under DNAME is invalid zone, but we test @@ -1122,17 +1003,14 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRType::A(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); - EXPECT_FALSE(current_database_->searchRunning()); doFindTest(finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); - EXPECT_FALSE(current_database_->searchRunning()); doFindTest(finder, isc::dns::Name("really.deep.below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); - EXPECT_FALSE(current_database_->searchRunning()); // Asking direcly for DNAME should give SUCCESS doFindTest(finder, isc::dns::Name("dname.example.org."), @@ -1148,33 +1026,27 @@ TEST_F(DatabaseClientTest, findDelegation) { isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); expected_rdatas_.clear(); doFindTest(finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); - EXPECT_FALSE(current_database_->searchRunning()); // This is broken dname, it contains two targets EXPECT_THROW(finder->find(isc::dns::Name("below.baddname.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); // Broken NS - it lives together with something else - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("brokenns1.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); EXPECT_THROW(finder->find(isc::dns::Name("brokenns2.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); - EXPECT_FALSE(current_database_->searchRunning()); } // Glue-OK mode. Just go trough NS delegations down (but not trough @@ -1229,13 +1101,11 @@ TEST_F(DatabaseClientTest, glueOK) { isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org."), ZoneFinder::FIND_GLUE_OK); - EXPECT_FALSE(current_database_->searchRunning()); doFindTest(finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org."), ZoneFinder::FIND_GLUE_OK); - EXPECT_FALSE(current_database_->searchRunning()); } TEST_F(DatabaseClientTest, getOrigin) { From 5ece3fe5e40efbcf7d727650475c35850624cfaf Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 18 Aug 2011 10:42:24 +0000 Subject: [PATCH 589/974] [519] update stats documents --- src/bin/stats/b10-stats.8 | 2 +- src/bin/stats/b10-stats.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8 index f69e4d37fa..ebc9201fd9 100644 --- a/src/bin/stats/b10-stats.8 +++ b/src/bin/stats/b10-stats.8 @@ -45,7 +45,7 @@ with other modules like \fBb10\-auth\fR and so on\&. It waits for coming data from other modules, then other modules send data to stats module periodically\&. Other modules send stats data to stats module independently from implementation of stats module, so the frequency of sending data may not be constant\&. Stats module collects data and aggregates it\&. \fBb10\-stats\fR -invokes "sendstats" command for +invokes an internal command for \fBbind10\fR after its initial starting because it\*(Aqs sure to collect statistics data from \fBbind10\fR\&. diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index f0c472dd29..19f6f46ae7 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -64,7 +64,7 @@ send stats data to stats module independently from implementation of stats module, so the frequency of sending data may not be constant. Stats module collects data and aggregates - it. b10-stats invokes "sendstats" command + it. b10-stats invokes an internal command for bind10 after its initial starting because it's sure to collect statistics data from bind10. From 36c6035855db0ae87a64a0d169e0230d936e3e64 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Thu, 18 Aug 2011 08:44:14 -0400 Subject: [PATCH 590/974] [1138] style fixes and docs in dhcid_49.h and dhcid_49.cc --- src/lib/dns/rdata/in_1/dhcid_49.cc | 44 ++++++++++++++++++++++++++++++ src/lib/dns/rdata/in_1/dhcid_49.h | 19 +++++++++---- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/lib/dns/rdata/in_1/dhcid_49.cc b/src/lib/dns/rdata/in_1/dhcid_49.cc index 937b068bde..3a34d50572 100644 --- a/src/lib/dns/rdata/in_1/dhcid_49.cc +++ b/src/lib/dns/rdata/in_1/dhcid_49.cc @@ -31,6 +31,21 @@ using namespace isc::util; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE +/// \brief Constructor from string. +/// +/// \param dhcid_str A base-64 representation of the DHCID binary data. +/// The data is considered to be opaque, but a sanity check is performed. +/// +/// Exceptions +/// +/// \c dhcid_str must be a valid BASE-64 string, otherwise an exception +/// of class \c isc::BadValue will be thrown; +/// the binary data should consist of at leat of 3 octets as per RFC4701: +/// < 2 octets > Identifier type code +/// < 1 octet > Digest type code +/// < n octets > Digest (length depends on digest type) +/// If the data is less than 3 octets (i.e. it cannot contain id type code and +/// digest type code), an exception of class \c InvalidRdataLength is thrown. DHCID::DHCID(const string& dhcid_str) { istringstream iss(dhcid_str); stringbuf digestbuf; @@ -48,6 +63,13 @@ DHCID::DHCID(const string& dhcid_str) { } } +/// \brief Constructor from wire-format data. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes +/// +/// Exceptions +/// \c InvalidRdataLength is thrown if \c rdata_len is than minimum of 3 octets DHCID::DHCID(InputBuffer& buffer, size_t rdata_len) { if (rdata_len < 3) { isc_throw(InvalidRdataLength, "DHCID length " << rdata_len << @@ -58,24 +80,43 @@ DHCID::DHCID(InputBuffer& buffer, size_t rdata_len) { buffer.readData(&digest_[0], rdata_len); } +/// \brief The copy constructor. +/// +/// This trivial copy constructor never throws an exception. DHCID::DHCID(const DHCID& other) : Rdata(), digest_(other.digest_) {} +/// \brief Render the \c DHCID in the wire format. +/// +/// \param buffer An output buffer to store the wire data. void DHCID::toWire(OutputBuffer& buffer) const { buffer.writeData(&digest_[0], digest_.size()); } +/// \brief Render the \c DHCID in the wire format into a +/// \c MessageRenderer object. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer in which the \c DHCID is to be stored. void DHCID::toWire(AbstractMessageRenderer& renderer) const { renderer.writeData(&digest_[0], digest_.size()); } +/// \brief Convert the \c DHCID to a string. +/// +/// This method returns a \c std::string object representing the \c DHCID. +/// +/// \return A string representation of \c DHCID. string DHCID::toText() const { return (encodeHex(digest_)); } +/// \brief Compare two instances of \c DHCID RDATA. +/// +/// See documentation in \c Rdata. int DHCID::compare(const Rdata& other) const { const DHCID& other_dhcid = dynamic_cast(other); @@ -91,6 +132,9 @@ DHCID::compare(const Rdata& other) const { } } +/// \brief Accessor method to get the DHCID digest +/// +/// \return A reference to the binary DHCID data const std::vector& DHCID::getDigest() const { return (digest_); diff --git a/src/lib/dns/rdata/in_1/dhcid_49.h b/src/lib/dns/rdata/in_1/dhcid_49.h index 666f2837ed..919395fba1 100644 --- a/src/lib/dns/rdata/in_1/dhcid_49.h +++ b/src/lib/dns/rdata/in_1/dhcid_49.h @@ -26,19 +26,28 @@ // BEGIN_RDATA_NAMESPACE +/// \brief \c rdata::DHCID class represents the DHCID RDATA as defined %in +/// RFC4701. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DHCID RDATA. class DHCID : public Rdata { public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS - // subject to change - // DHCID& operator=(const DHCID& source); - // ~DHCID(); - + /// \brief Return the digest. + /// + /// This method never throws an exception. const std::vector& getDigest() const; private: - std::vector digest_; // opaque data at least 3 octets long + /// \brief Private data representation + /// + /// Opaque data at least 3 octets long as per RFC4701. + /// + std::vector digest_; }; // END_RDATA_NAMESPACE // END_ISC_NAMESPACE From 45630ca90e823247c429f82b338244a9bba9baf4 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 14:45:42 +0200 Subject: [PATCH 591/974] [1183] some cleanup --- src/lib/datasrc/database.cc | 3 -- src/lib/datasrc/sqlite3_accessor.cc | 60 ++++++++++++++++------------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 48b894608f..bacfeef36f 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -362,16 +362,13 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_status = CNAME; } } - // TODO: some of these can be removed } catch (const DataSourceError& dse) { logger.error(DATASRC_DATABASE_FIND_ERROR) .arg(database_->getDBName()).arg(dse.what()); - // call cleanup and rethrow throw; } catch (const isc::Exception& isce) { logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR) .arg(database_->getDBName()).arg(isce.what()); - // cleanup, change it to a DataSourceError and rethrow isc_throw(DataSourceError, isce.what()); } catch (const std::exception& ex) { logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 9cb9ca01b6..3ca311d076 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -138,9 +138,13 @@ const char* const SCHEMA_LIST[] = { const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"; -const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata, name " +// note that the order of the SELECT values is specifically chosen to match +// the enum values in RecordColumns +const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata " "FROM records WHERE zone_id=?1 AND name=?2"; +// note that the order of the SELECT values is specifically chosen to match +// the enum values in RecordColumns const char* const q_iterate_str = "SELECT rdtype, ttl, sigtype, rdata, name FROM records " "WHERE zone_id = ?1 " "ORDER BY name, rdtype"; @@ -369,14 +373,11 @@ public: Context(const boost::shared_ptr& database, int id) : iterator_type_(ITT_ALL), database_(database), - statement(NULL) + statement_(NULL) { // We create the statement now and then just keep getting data from it - statement = prepare(database->dbparameters_->db_, q_iterate_str); - if (sqlite3_bind_int(statement, 1, id) != SQLITE_OK) { - isc_throw(SQLite3Error, "Could not bind " << id << - " to SQL statement (iterate)"); - } + statement_ = prepare(database->dbparameters_->db_, q_iterate_str); + bindZoneId(id); } // Construct an iterator for records with a specific name. When constructed @@ -385,21 +386,12 @@ public: const isc::dns::Name& name) : iterator_type_(ITT_NAME), database_(database), - statement(NULL) + statement_(NULL) { // We create the statement now and then just keep getting data from it - // TODO move to private and clean up error - statement = prepare(database->dbparameters_->db_, q_any_str); - if (sqlite3_bind_int(statement, 1, id) != SQLITE_OK) { - isc_throw(SQLite3Error, "Could not bind " << id << - " to SQL statement"); - } - if (sqlite3_bind_text(statement, 2, name.toText().c_str(), -1, - SQLITE_TRANSIENT) != SQLITE_OK) { - sqlite3_finalize(statement); - isc_throw(SQLite3Error, "Could not bind " << id << - " to SQL statement"); - } + statement_ = prepare(database->dbparameters_->db_, q_any_str); + bindZoneId(id); + bindName(name); } bool getNext(std::string data[], size_t size) { @@ -408,7 +400,7 @@ public: ", not " << COLUMN_COUNT); } // If there's another row, get it - int rc(sqlite3_step(statement)); + int rc(sqlite3_step(statement_)); if (rc == SQLITE_ROW) { // For both types, we copy the first four columns copyColumn(data, TYPE_COLUMN); @@ -429,9 +421,7 @@ public: } virtual ~Context() { - if (statement) { - sqlite3_finalize(statement); - } + sqlite3_finalize(statement_); } private: @@ -444,14 +434,32 @@ private: }; void copyColumn(std::string data[], int column) { - data[column] = convertToPlainChar(sqlite3_column_text(statement, + data[column] = convertToPlainChar(sqlite3_column_text(statement_, column), database_->dbparameters_); } + void bindZoneId(const int zone_id) { + if (sqlite3_bind_int(statement_, 1, zone_id) != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not bind int " << zone_id << + " to SQL statement: " << + sqlite3_errmsg(database_->dbparameters_->db_)); + } + } + + void bindName(const isc::dns::Name& name) { + if (sqlite3_bind_text(statement_, 2, name.toText().c_str(), -1, + SQLITE_TRANSIENT) != SQLITE_OK) { + const char* errmsg = sqlite3_errmsg(database_->dbparameters_->db_); + sqlite3_finalize(statement_); + isc_throw(SQLite3Error, "Could not bind text '" << name << + "' to SQL statement: " << errmsg); + } + } + IteratorType iterator_type_; boost::shared_ptr database_; - sqlite3_stmt *statement; + sqlite3_stmt *statement_; }; DatabaseAccessor::IteratorContextPtr From 1b421982a6fcadebc72d3d6ee7a4e34eec61a25d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 15:01:51 +0200 Subject: [PATCH 592/974] [1183] getAllRecords already takes zone_id, does not need name --- src/lib/datasrc/database.cc | 2 +- src/lib/datasrc/database.h | 4 +--- src/lib/datasrc/sqlite3_accessor.cc | 2 +- src/lib/datasrc/sqlite3_accessor.h | 3 +-- src/lib/datasrc/tests/database_unittest.cc | 12 ++++++++---- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 4 ++-- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index bacfeef36f..3ba8fd89cd 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -497,7 +497,7 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const { } // Request the context DatabaseAccessor::IteratorContextPtr - context(database_->getAllRecords(name, zone.second)); + context(database_->getAllRecords(zone.second)); // It must not return NULL, that's a bug of the implementation if (context == DatabaseAccessor::IteratorContextPtr()) { isc_throw(isc::Unexpected, "Iterator context null at " + diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index cf7b495540..4bf5335302 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -173,15 +173,13 @@ public: * \param id The ID of the zone, returned from getZone(). * \return Newly created iterator context. Must not be NULL. */ - virtual IteratorContextPtr getAllRecords(const isc::dns::Name& name, - int id) const + virtual IteratorContextPtr getAllRecords(int id) const { /* * This is a compromise. We need to document the parameters in doxygen, * so they need a name, but then it complains about unused parameter. * This is a NOP that "uses" the parameters. */ - static_cast(name); static_cast(id); isc_throw(isc::NotImplemented, diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 3ca311d076..56e2a1574a 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -468,7 +468,7 @@ SQLite3Database::getRecords(const isc::dns::Name& name, int id) const { } DatabaseAccessor::IteratorContextPtr -SQLite3Database::getAllRecords(const isc::dns::Name&, int id) const { +SQLite3Database::getAllRecords(int id) const { return (IteratorContextPtr(new Context(shared_from_this(), id))); } diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 32d174a3cd..85ed16ea66 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -96,8 +96,7 @@ public: int id) const; /// \brief Implementation of DatabaseAbstraction::getAllRecords - virtual IteratorContextPtr getAllRecords(const isc::dns::Name&, - int id) const; + virtual IteratorContextPtr getAllRecords(int id) const; /// The SQLite3 implementation of this method returns a string starting /// with a fixed prefix of "sqlite3_" followed by the DB file name diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 5d7663052b..bf4a4f74e9 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -233,7 +233,7 @@ private: } }; public: - virtual IteratorContextPtr getAllRecords(const Name&, int id) const { + virtual IteratorContextPtr getAllRecords(int id) const { if (id == 42) { return (IteratorContextPtr(new MockIteratorContext())); } else if (id == 13) { @@ -440,12 +440,16 @@ private: } }; +// This tests the default getRecords behaviour, throwing NotImplemented +TEST(DatabaseConnectionTest, getRecords) { + EXPECT_THROW(NopAccessor().getRecords(Name("."), 1), + isc::NotImplemented); +} + // This tests the default getAllRecords behaviour, throwing NotImplemented TEST(DatabaseConnectionTest, getAllRecords) { // The parameters don't matter - EXPECT_THROW(NopAccessor().getRecords(Name("."), 1), - isc::NotImplemented); - EXPECT_THROW(NopAccessor().getAllRecords(Name("."), 1), + EXPECT_THROW(NopAccessor().getAllRecords(1), isc::NotImplemented); } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 27c07e3578..33390a4e68 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -110,7 +110,7 @@ TEST_F(SQLite3Access, iterator) { // Get the iterator context DatabaseAccessor::IteratorContextPtr - context(db->getAllRecords(Name("example2.com"), 1)); + context(db->getAllRecords(1)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); @@ -133,7 +133,7 @@ TEST_F(SQLite3Access, iteratorColumnCount) { // Get the iterator context DatabaseAccessor::IteratorContextPtr - context(db->getAllRecords(Name("example2.com"), 1)); + context(db->getAllRecords(1)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); From 59add6ec0f7e96ee81a7b9970228b8f795b01997 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 15:34:50 +0200 Subject: [PATCH 593/974] [1183] now it's been unified, we can make compiler enforce array size that makes a number of tests unnecessary (as well as impossible) --- src/lib/datasrc/database.cc | 4 +- src/lib/datasrc/database.h | 8 +-- src/lib/datasrc/sqlite3_accessor.cc | 6 +- src/lib/datasrc/tests/database_unittest.cc | 22 ++----- .../tests/sqlite3_accessor_unittest.cc | 63 +++++++++---------- 5 files changed, 40 insertions(+), 63 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 3ba8fd89cd..809360c165 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -191,7 +191,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, } std::string columns[DatabaseAccessor::COLUMN_COUNT]; - while (context->getNext(columns, DatabaseAccessor::COLUMN_COUNT)) { + while (context->getNext(columns)) { if (!records_found) { records_found = true; } @@ -466,7 +466,7 @@ private: // Load next row of data void getData() { string data[DatabaseAccessor::COLUMN_COUNT]; - data_ready_ = context_->getNext(data, DatabaseAccessor::COLUMN_COUNT); + data_ready_ = context_->getNext(data); name_ = data[DatabaseAccessor::NAME_COLUMN]; rtype_ = data[DatabaseAccessor::TYPE_COLUMN]; ttl_ = data[DatabaseAccessor::TTL_COLUMN]; diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 4bf5335302..1219c5c6ce 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -48,6 +48,9 @@ namespace datasrc { */ class DatabaseAccessor : boost::noncopyable { public: + /// The number of fields the columns array passed to getNext should have + static const size_t COLUMN_COUNT = 5; + /** * \brief Destructor * @@ -119,7 +122,7 @@ public: * exception (or any other in case of derived class) is thrown, * the iterator can't be safely used any more. */ - virtual bool getNext(std::string columns[], size_t column_data) = 0; + virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) = 0; }; typedef boost::shared_ptr IteratorContextPtr; @@ -206,9 +209,6 @@ public: NAME_COLUMN = 4 ///< The domain name of this RR }; - /// The number of fields the columns array passed to getNextRecord should have - static const size_t COLUMN_COUNT = 5; - /** * \brief Returns a string identifying this dabase backend * diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 56e2a1574a..22473ea1da 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -394,11 +394,7 @@ public: bindName(name); } - bool getNext(std::string data[], size_t size) { - if (size != COLUMN_COUNT) { - isc_throw(DataSourceError, "getNext received size of " << size << - ", not " << COLUMN_COUNT); - } + bool getNext(std::string (&data)[COLUMN_COUNT]) { // If there's another row, get it int rc(sqlite3_step(statement_)); if (rc == SQLITE_ROW) { diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index bf4a4f74e9..f558598475 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -111,7 +111,7 @@ private: } } - virtual bool getNext(std::string columns[], size_t column_count) { + virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) { if (searched_name_ == "dsexception.in.getnext.") { isc_throw(DataSourceError, "datasource exception on getnextrecord"); } else if (searched_name_ == "iscexception.in.getnext.") { @@ -120,11 +120,8 @@ private: throw std::exception(); } - if (column_count != DatabaseAccessor::COLUMN_COUNT) { - isc_throw(DataSourceError, "Wrong column count in getNextRecord"); - } if (cur_record_ < cur_name.size()) { - for (size_t i = 0; i < column_count; ++i) { + for (size_t i = 0; i < COLUMN_COUNT; ++i) { columns[i] = cur_name[cur_record_][i]; } cur_record_++; @@ -147,10 +144,7 @@ private: MockIteratorContext() : step(0) { } - virtual bool getNext(string data[], size_t size) { - if (size != DatabaseAccessor::COLUMN_COUNT) { - isc_throw(DataSourceError, "Wrong column count in getNextRecord"); - } + virtual bool getNext(string (&data)[COLUMN_COUNT]) { switch (step ++) { case 0: data[DatabaseAccessor::NAME_COLUMN] = "example.org"; @@ -193,10 +187,7 @@ private: }; class EmptyIteratorContext : public IteratorContext { public: - virtual bool getNext(string[], size_t size) { - if (size != DatabaseAccessor::COLUMN_COUNT) { - isc_throw(DataSourceError, "Wrong column count in getNextRecord"); - } + virtual bool getNext(string(&)[COLUMN_COUNT]) { return (false); } }; @@ -207,10 +198,7 @@ private: BadIteratorContext() : step(0) { } - virtual bool getNext(string data[], size_t size) { - if (size != DatabaseAccessor::COLUMN_COUNT) { - isc_throw(DataSourceError, "Wrong column count in getNextRecord"); - } + virtual bool getNext(string (&data)[COLUMN_COUNT]) { switch (step ++) { case 0: data[DatabaseAccessor::NAME_COLUMN] = "x.example.org"; diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 33390a4e68..628d1b639c 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -117,14 +117,14 @@ TEST_F(SQLite3Access, iterator) { const size_t size(5); std::string data[size]; // Get and check the first and only record - EXPECT_TRUE(context->getNext(data, size)); + EXPECT_TRUE(context->getNext(data)); EXPECT_EQ("example2.com.", data[4]); EXPECT_EQ("SOA", data[0]); EXPECT_EQ("master.example2.com. admin.example2.com. " "1234 3600 1800 2419200 7200", data[3]); EXPECT_EQ("3600", data[1]); // Check there's no other - EXPECT_FALSE(context->getNext(data, size)); + EXPECT_FALSE(context->getNext(data)); } TEST_F(SQLite3Access, iteratorColumnCount) { @@ -137,11 +137,8 @@ TEST_F(SQLite3Access, iteratorColumnCount) { ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); - EXPECT_THROW(context->getNext(NULL, 0), DataSourceError); - std::string data[6]; - EXPECT_THROW(context->getNext(data, 4), DataSourceError); - EXPECT_THROW(context->getNext(data, 6), DataSourceError); - EXPECT_NO_THROW(context->getNext(data, 5)); + std::string data[DatabaseAccessor::COLUMN_COUNT]; + EXPECT_NO_THROW(context->getNext(data)); } TEST(SQLite3Open, getDBNameExample2) { @@ -183,89 +180,85 @@ TEST_F(SQLite3Access, getRecords) { // TODO: can't do this anymore // without search, getNext() should return false - //EXPECT_FALSE(context->getNext(columns, column_count)); + //EXPECT_FALSE(context->getNext(columns)); //checkRecordRow(columns, "", "", "", "", ""); DatabaseAccessor::IteratorContextPtr context(db->getRecords(Name("foo.bar"), 1)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); - EXPECT_FALSE(context->getNext(columns, column_count)); + EXPECT_FALSE(context->getNext(columns)); checkRecordRow(columns, "", "", "", "", ""); // TODO can't pass incomplete name anymore //context = db->getRecords(Name(""), zone_id); - //EXPECT_FALSE(context->getNext(columns, column_count)); + //EXPECT_FALSE(context->getNext(columns)); //checkRecordRow(columns, "", "", "", "", ""); - // Should error on a bad number of columns - EXPECT_THROW(context->getNext(columns, 4), DataSourceError); - EXPECT_THROW(context->getNext(columns, 6), DataSourceError); - // now try some real searches context = db->getRecords(Name("foo.example.com."), zone_id); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "CNAME", "3600", "", "cnametest.example.org.", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "RRSIG", "3600", "CNAME", "CNAME 5 3 3600 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "NSEC", "7200", "", "mail.example.com. CNAME RRSIG NSEC", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE", ""); - EXPECT_FALSE(context->getNext(columns, column_count)); + EXPECT_FALSE(context->getNext(columns)); // with no more records, the array should not have been modified checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE", ""); context = db->getRecords(Name("example.com."), zone_id); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "SOA", "3600", "", "master.example.com. admin.example.com. " "1234 3600 1800 2419200 7200", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "RRSIG", "3600", "SOA", "SOA 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "NS", "1200", "", "dns01.example.com.", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "NS", "3600", "", "dns02.example.com.", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "NS", "1800", "", "dns03.example.com.", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "RRSIG", "3600", "NS", "NS 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "MX", "3600", "", "10 mail.example.com.", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "MX", "3600", "", "20 mail.subzone.example.com.", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "RRSIG", "3600", "MX", "MX 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "NSEC", "7200", "", "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 2 7200 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "DNSKEY", "3600", "", "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4q NGV7W" "cTD0WEiuV7IjXgHE36fCmS9QsUxSSOV o1I/FMxI2PJVqTYHkX" "FBS7AzLGsQYMU7UjBZ SotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g" "0qcbW YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "DNSKEY", "3600", "", "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJ tbvzg" "62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV 4HQZJStJaZ+fHU5AwV" @@ -275,15 +268,15 @@ TEST_F(SQLite3Access, getRecords) { "fiHAxFHrkY3t3D5J R9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86" "Acv RyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGf oIK/aKwEN" "rsjcKZZj660b1M=", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " "4456 example.com. FAKEFAKEFAKEFAKE", ""); - ASSERT_TRUE(context->getNext(columns, column_count)); + ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE", ""); - EXPECT_FALSE(context->getNext(columns, column_count)); + EXPECT_FALSE(context->getNext(columns)); // getnextrecord returning false should mean array is not altered checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " From c0c7b21ab57bb9445329fed9e1451c534aab6a67 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 16:03:35 +0200 Subject: [PATCH 594/974] [1183] documentation --- src/lib/datasrc/database.h | 22 ++++++++++++----- src/lib/datasrc/sqlite3_accessor.h | 24 +++++++++++++++++-- .../tests/sqlite3_accessor_unittest.cc | 10 -------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 1219c5c6ce..cf6ac94092 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -106,16 +106,23 @@ public: * \brief Function to provide next resource record * * This function should provide data about the next resource record - * from the iterated zone. The data are not converted yet. + * from the data that is searched. The data is not converted yet. * + * Depending on how the iterator was constructed, there is a difference + * in behaviour; for a 'full zone iterator', created with + * getAllRecords(), all 5 elements of the array are overwritten. + * for a 'name iterator', created with getRecords(), the fifth column + * (NAME_COLUMN) is untouched, since what would be added here is by + * definition already known to the caller (it already passes it as + * an argument to getRecords()). + * * \note The order of RRs is not strictly set, but the RRs for single * RRset must not be interleaved with any other RRs (eg. RRsets must be * "together"). * * \param columns The data will be returned through here. The order - * is specified by the RecordColumns enum. - * \param Size of the columns array. Must be equal to COLUMN_COUNT, - * otherwise DataSourceError is thrown. + * is specified by the RecordColumns enum, and the size must be + * COLUMN_COUNT * \todo Do we consider databases where it is stored in binary blob * format? * \throw DataSourceError if there's database-related error. If the @@ -140,6 +147,10 @@ public: * "minimal" implementations of the connection not supporting optional * functionality. * + * The implementation of the iterator that is returned may leave the + * fifth column of the array passed to getNext() untouched, as that + * data is already known (it is the same as the name argument here) + * * \param name The name to search for. * \param id The ID of the zone, returned from getZone(). * \return Newly created iterator context. Must not be NULL. @@ -172,7 +183,6 @@ public: * "minimal" implementations of the connection not supporting optional * functionality. * - * \param name The name of the zone. * \param id The ID of the zone, returned from getZone(). * \return Newly created iterator context. Must not be NULL. */ @@ -193,7 +203,7 @@ public: * Definitions of the fields as they are required to be filled in * by getNextRecord() * - * When implementing getNextRecord(), the columns array should + * When implementing getNext(), the columns array should * be filled with the values as described in this enumeration, * in this order, i.e. TYPE_COLUMN should be the first element * (index 0) of the array, TTL_COLUMN should be the second element diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 85ed16ea66..025785ed7a 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -76,6 +76,7 @@ public: * Closes the database. */ ~SQLite3Database(); + /** * \brief Look up a zone * @@ -91,11 +92,30 @@ public: */ virtual std::pair getZone(const isc::dns::Name& name) const; - /// \brief Implementation of DatabaseAbstraction::getRecords + /** \brief Look up all resource records for a name + * + * This implements the getRecords() method from DatabaseAccessor + * + * \exception SQLite3Error if there is an sqlite3 error when performing + * the query + * + * \param name the name to look up + * \param id the zone id, as returned by getZone() + * \return Iterator that contains all records with the given name + */ virtual IteratorContextPtr getRecords(const isc::dns::Name& name, int id) const; - /// \brief Implementation of DatabaseAbstraction::getAllRecords + /** \brief Look up all resource records for a zone + * + * This implements the getRecords() method from DatabaseAccessor + * + * \exception SQLite3Error if there is an sqlite3 error when performing + * the query + * + * \param id the zone id, as returned by getZone() + * \return Iterator that contains all records in the given zone + */ virtual IteratorContextPtr getAllRecords(int id) const; /// The SQLite3 implementation of this method returns a string starting diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 628d1b639c..a42eaad0cb 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -178,11 +178,6 @@ TEST_F(SQLite3Access, getRecords) { const size_t column_count = DatabaseAccessor::COLUMN_COUNT; std::string columns[column_count]; - // TODO: can't do this anymore - // without search, getNext() should return false - //EXPECT_FALSE(context->getNext(columns)); - //checkRecordRow(columns, "", "", "", "", ""); - DatabaseAccessor::IteratorContextPtr context(db->getRecords(Name("foo.bar"), 1)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), @@ -190,11 +185,6 @@ TEST_F(SQLite3Access, getRecords) { EXPECT_FALSE(context->getNext(columns)); checkRecordRow(columns, "", "", "", "", ""); - // TODO can't pass incomplete name anymore - //context = db->getRecords(Name(""), zone_id); - //EXPECT_FALSE(context->getNext(columns)); - //checkRecordRow(columns, "", "", "", "", ""); - // now try some real searches context = db->getRecords(Name("foo.example.com."), zone_id); ASSERT_TRUE(context->getNext(columns)); From 451086b203ef3e4611487630225a7650ad9322e7 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 16:07:06 +0200 Subject: [PATCH 595/974] [1183] remove catches that should be at higher level --- src/lib/datasrc/database.cc | 104 ++++++++++++--------------- src/lib/datasrc/datasrc_messages.mes | 18 ----- 2 files changed, 45 insertions(+), 77 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 809360c165..043567ae13 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -309,71 +309,57 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS) .arg(database_->getDBName()).arg(name).arg(type); - try { - // First, do we have any kind of delegation (NS/DNAME) here? - Name origin(getOrigin()); - size_t origin_label_count(origin.getLabelCount()); - size_t current_label_count(name.getLabelCount()); - // This is how many labels we remove to get origin - size_t remove_labels(current_label_count - origin_label_count); + // First, do we have any kind of delegation (NS/DNAME) here? + Name origin(getOrigin()); + size_t origin_label_count(origin.getLabelCount()); + size_t current_label_count(name.getLabelCount()); + // This is how many labels we remove to get origin + size_t remove_labels(current_label_count - origin_label_count); - // Now go trough all superdomains from origin down - for (int i(remove_labels); i > 0; --i) { - Name superdomain(name.split(i)); - // Look if there's NS or DNAME (but ignore the NS in origin) - found = getRRset(superdomain, NULL, false, true, - i != remove_labels && !glue_ok); - if (found.second) { - // We found something redirecting somewhere else - // (it can be only NS or DNAME here) - result_rrset = found.second; - if (result_rrset->getType() == isc::dns::RRType::NS()) { - LOG_DEBUG(logger, DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_DELEGATION). - arg(database_->getDBName()).arg(superdomain); - result_status = DELEGATION; - } else { - LOG_DEBUG(logger, DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_DNAME). - arg(database_->getDBName()).arg(superdomain); - result_status = DNAME; - } - // Don't search more - break; - } - } - - if (!result_rrset) { // Only if we didn't find a redirect already - // Try getting the final result and extract it - // It is special if there's a CNAME or NS, DNAME is ignored here - // And we don't consider the NS in origin - found = getRRset(name, &type, true, false, - name != origin && !glue_ok); - records_found = found.first; + // Now go trough all superdomains from origin down + for (int i(remove_labels); i > 0; --i) { + Name superdomain(name.split(i)); + // Look if there's NS or DNAME (but ignore the NS in origin) + found = getRRset(superdomain, NULL, false, true, + i != remove_labels && !glue_ok); + if (found.second) { + // We found something redirecting somewhere else + // (it can be only NS or DNAME here) result_rrset = found.second; - if (result_rrset && name != origin && !glue_ok && - result_rrset->getType() == isc::dns::RRType::NS()) { + if (result_rrset->getType() == isc::dns::RRType::NS()) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_DELEGATION_EXACT). - arg(database_->getDBName()).arg(name); + DATASRC_DATABASE_FOUND_DELEGATION). + arg(database_->getDBName()).arg(superdomain); result_status = DELEGATION; - } else if (result_rrset && type != isc::dns::RRType::CNAME() && - result_rrset->getType() == isc::dns::RRType::CNAME()) { - result_status = CNAME; + } else { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_DNAME). + arg(database_->getDBName()).arg(superdomain); + result_status = DNAME; } + // Don't search more + break; + } + } + + if (!result_rrset) { // Only if we didn't find a redirect already + // Try getting the final result and extract it + // It is special if there's a CNAME or NS, DNAME is ignored here + // And we don't consider the NS in origin + found = getRRset(name, &type, true, false, + name != origin && !glue_ok); + records_found = found.first; + result_rrset = found.second; + if (result_rrset && name != origin && !glue_ok && + result_rrset->getType() == isc::dns::RRType::NS()) { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_DELEGATION_EXACT). + arg(database_->getDBName()).arg(name); + result_status = DELEGATION; + } else if (result_rrset && type != isc::dns::RRType::CNAME() && + result_rrset->getType() == isc::dns::RRType::CNAME()) { + result_status = CNAME; } - } catch (const DataSourceError& dse) { - logger.error(DATASRC_DATABASE_FIND_ERROR) - .arg(database_->getDBName()).arg(dse.what()); - throw; - } catch (const isc::Exception& isce) { - logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR) - .arg(database_->getDBName()).arg(isce.what()); - isc_throw(DataSourceError, isce.what()); - } catch (const std::exception& ex) { - logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR) - .arg(database_->getDBName()).arg(ex.what()); - throw; } if (!result_rrset) { diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 89f280639d..659d2bdda0 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -63,12 +63,6 @@ The maximum allowed number of items of the hotspot cache is set to the given number. If there are too many, some of them will be dropped. The size of 0 means no limit. -% DATASRC_DATABASE_FIND_ERROR error retrieving data from datasource %1: %2 -This was an internal error while reading data from a datasource. This can either -mean the specific data source implementation is not behaving correctly, or the -data it provides is invalid. The current search is aborted. -The error message contains specific information about the error. - % DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3 Debug information. The database data source is looking up records with the given name and type in the database. @@ -79,18 +73,6 @@ different TTL values. This isn't allowed on the wire and is considered an error, so we set it to the lowest value we found (but we don't modify the database). The data in database should be checked and fixed. -% DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from datasource %1: %2 -There was an uncaught general exception while reading data from a datasource. -This most likely points to a logic error in the code, and can be considered a -bug. The current search is aborted. Specific information about the exception is -printed in this error message. - -% DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR uncaught error retrieving data from datasource %1: %2 -There was an uncaught ISC exception while reading data from a datasource. This -most likely points to a logic error in the code, and can be considered a bug. -The current search is aborted. Specific information about the exception is -printed in this error message. - % DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %2 in %1 When searching for a domain, the program met a delegation to a different zone at the given domain name. It will return that one instead. From bdebd1afa4bf82120c66d9ee8d8cab500ab0b606 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 16:43:36 +0200 Subject: [PATCH 596/974] [1183] remove some unused code and fix a test --- src/lib/datasrc/sqlite3_accessor.cc | 2 +- src/lib/datasrc/tests/database_unittest.cc | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 22473ea1da..78c5c789df 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -429,7 +429,7 @@ private: ITT_NAME }; - void copyColumn(std::string data[], int column) { + void copyColumn(std::string (&data)[COLUMN_COUNT], int column) { data[column] = convertToPlainChar(sqlite3_column_text(statement_, column), database_->dbparameters_); diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f558598475..e8998a5549 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -238,12 +238,6 @@ public: virtual IteratorContextPtr getRecords(const Name& name, int id) const { if (id == 42) { return (IteratorContextPtr(new MockNameIteratorContext(*this, id, name))); - } else if (id == 13) { - return (IteratorContextPtr()); - } else if (id == 0) { - return (IteratorContextPtr(new EmptyIteratorContext())); - } else if (id == -1) { - return (IteratorContextPtr(new BadIteratorContext())); } else { isc_throw(isc::Unexpected, "Unknown zone ID"); } @@ -890,7 +884,7 @@ TEST_F(DatabaseClientTest, find) { EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), - DataSourceError); + isc::Exception); EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), @@ -903,7 +897,7 @@ TEST_F(DatabaseClientTest, find) { EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), - DataSourceError); + isc::Exception); EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), From 8cfa0f76baf92f82bf2865b3557c0a2094e81cb4 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 18 Aug 2011 16:54:12 +0200 Subject: [PATCH 597/974] [1183] make dbparameters_ a scoped_ptr so that failed constructor does not leak --- src/lib/datasrc/sqlite3_accessor.cc | 7 +++---- src/lib/datasrc/sqlite3_accessor.h | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 78c5c789df..dbef967a00 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -49,7 +49,7 @@ struct SQLite3Parameters { }; SQLite3Database::SQLite3Database(const std::string& filename, - const isc::dns::RRClass& rrclass) : + const isc::dns::RRClass& rrclass) : dbparameters_(new SQLite3Parameters), class_(rrclass.toText()), database_name_("sqlite3_" + @@ -243,7 +243,7 @@ SQLite3Database::open(const std::string& name) { } checkAndSetupSchema(&initializer); - initializer.move(dbparameters_); + initializer.move(dbparameters_.get()); } SQLite3Database::~SQLite3Database() { @@ -251,7 +251,6 @@ SQLite3Database::~SQLite3Database() { if (dbparameters_->db_ != NULL) { close(); } - delete dbparameters_; } void @@ -432,7 +431,7 @@ private: void copyColumn(std::string (&data)[COLUMN_COUNT], int column) { data[column] = convertToPlainChar(sqlite3_column_text(statement_, column), - database_->dbparameters_); + database_->dbparameters_.get()); } void bindZoneId(const int zone_id) { diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 025785ed7a..6737c0b5c4 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -21,6 +21,7 @@ #include #include +#include #include namespace isc { @@ -127,7 +128,7 @@ public: private: /// \brief Private database data - SQLite3Parameters* dbparameters_; + boost::scoped_ptr dbparameters_; /// \brief The class for which the queries are done const std::string class_; /// \brief Opens the database From 954143a2748110c720d28df49159ed4f0bc1a1a2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 18 Aug 2011 15:49:38 -0700 Subject: [PATCH 598/974] [1183] trivial/minor suggested cleanups: they are mainly indentation, constify, typo, removing redundant space, etc. --- src/lib/datasrc/database.cc | 26 ++++++++++------------ src/lib/datasrc/database.h | 10 ++++----- src/lib/datasrc/sqlite3_accessor.cc | 4 ++-- src/lib/datasrc/tests/database_unittest.cc | 2 +- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 043567ae13..c6f4c11fe0 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -185,7 +185,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, DatabaseAccessor::IteratorContextPtr context(database_->getRecords(name, zone_id_)); // It must not return NULL, that's a bug of the implementation - if (context == DatabaseAccessor::IteratorContextPtr()) { + if (!context) { isc_throw(isc::Unexpected, "Iterator context null at " + name.toText()); } @@ -292,7 +292,6 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, return (std::pair(records_found, result_rrset)); } - ZoneFinder::FindResult DatabaseClient::Finder::find(const isc::dns::Name& name, const isc::dns::RRType& type, @@ -310,30 +309,30 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, .arg(database_->getDBName()).arg(name).arg(type); // First, do we have any kind of delegation (NS/DNAME) here? - Name origin(getOrigin()); - size_t origin_label_count(origin.getLabelCount()); - size_t current_label_count(name.getLabelCount()); + const Name origin(getOrigin()); + const size_t origin_label_count(origin.getLabelCount()); + const size_t current_label_count(name.getLabelCount()); // This is how many labels we remove to get origin - size_t remove_labels(current_label_count - origin_label_count); + const size_t remove_labels(current_label_count - origin_label_count); // Now go trough all superdomains from origin down for (int i(remove_labels); i > 0; --i) { - Name superdomain(name.split(i)); + const Name superdomain(name.split(i)); // Look if there's NS or DNAME (but ignore the NS in origin) found = getRRset(superdomain, NULL, false, true, - i != remove_labels && !glue_ok); + i != remove_labels && !glue_ok); if (found.second) { // We found something redirecting somewhere else // (it can be only NS or DNAME here) result_rrset = found.second; if (result_rrset->getType() == isc::dns::RRType::NS()) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_DELEGATION). + DATASRC_DATABASE_FOUND_DELEGATION). arg(database_->getDBName()).arg(superdomain); result_status = DELEGATION; } else { LOG_DEBUG(logger, DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_DNAME). + DATASRC_DATABASE_FOUND_DNAME). arg(database_->getDBName()).arg(superdomain); result_status = DNAME; } @@ -346,18 +345,17 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Try getting the final result and extract it // It is special if there's a CNAME or NS, DNAME is ignored here // And we don't consider the NS in origin - found = getRRset(name, &type, true, false, - name != origin && !glue_ok); + found = getRRset(name, &type, true, false, name != origin && !glue_ok); records_found = found.first; result_rrset = found.second; if (result_rrset && name != origin && !glue_ok && result_rrset->getType() == isc::dns::RRType::NS()) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_DELEGATION_EXACT). + DATASRC_DATABASE_FOUND_DELEGATION_EXACT). arg(database_->getDBName()).arg(name); result_status = DELEGATION; } else if (result_rrset && type != isc::dns::RRType::CNAME() && - result_rrset->getType() == isc::dns::RRType::CNAME()) { + result_rrset->getType() == isc::dns::RRType::CNAME()) { result_status = CNAME; } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index cf6ac94092..68568590f0 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -111,11 +111,11 @@ public: * Depending on how the iterator was constructed, there is a difference * in behaviour; for a 'full zone iterator', created with * getAllRecords(), all 5 elements of the array are overwritten. - * for a 'name iterator', created with getRecords(), the fifth column + * For a 'name iterator', created with getRecords(), the fifth column * (NAME_COLUMN) is untouched, since what would be added here is by * definition already known to the caller (it already passes it as * an argument to getRecords()). - * + * * \note The order of RRs is not strictly set, but the RRs for single * RRset must not be interleaved with any other RRs (eg. RRsets must be * "together"). @@ -138,7 +138,7 @@ public: * \brief Creates an iterator context for a specific name. * * This should create a new iterator context to be used by - * DatabaseConnection's ZoneIterator. It can be created based on the name + * DatabaseAccessor's ZoneIterator. It can be created based on the name * or the ID (returned from getZone()), what is more comfortable for the * database implementation. Both are provided (and are guaranteed to match, * the DatabaseClient first looks up the zone ID and then calls this). @@ -174,7 +174,7 @@ public: * \brief Creates an iterator context for the whole zone. * * This should create a new iterator context to be used by - * DatabaseConnection's ZoneIterator. It can be created based on the name + * DatabaseAccessor's ZoneIterator. It can be created based on the name * or the ID (returned from getZone()), what is more comfortable for the * database implementation. Both are provided (and are guaranteed to match, * the DatabaseClient first looks up the zone ID and then calls this). @@ -201,7 +201,7 @@ public: /** * Definitions of the fields as they are required to be filled in - * by getNextRecord() + * by IteratorContext::getNext() * * When implementing getNext(), the columns array should * be filled with the values as described in this enumeration, diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index dbef967a00..070c6c50a2 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -395,7 +395,7 @@ public: bool getNext(std::string (&data)[COLUMN_COUNT]) { // If there's another row, get it - int rc(sqlite3_step(statement_)); + const int rc(sqlite3_step(statement_)); if (rc == SQLITE_ROW) { // For both types, we copy the first four columns copyColumn(data, TYPE_COLUMN); @@ -452,7 +452,7 @@ private: } } - IteratorType iterator_type_; + const IteratorType iterator_type_; boost::shared_ptr database_; sqlite3_stmt *statement_; }; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index e8998a5549..f3311a83a6 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -245,7 +245,7 @@ public: private: std::map > > records; - // used as temporary storageduring the building of the fake data + // used as temporary storage during the building of the fake data std::vector< std::vector > cur_name; // Adds one record to the current name in the database From dc9318330acbd36e07ad5a4e8a68c9a6e2430543 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 18 Aug 2011 16:18:14 -0700 Subject: [PATCH 599/974] [1068] added new makefile that will be needed for this work. --- src/lib/datasrc/tests/testdata/Makefile.am | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/lib/datasrc/tests/testdata/Makefile.am diff --git a/src/lib/datasrc/tests/testdata/Makefile.am b/src/lib/datasrc/tests/testdata/Makefile.am new file mode 100644 index 0000000000..6a35fe3c63 --- /dev/null +++ b/src/lib/datasrc/tests/testdata/Makefile.am @@ -0,0 +1 @@ +CLEANFILES = *.copied From 353c08576b5c7fae46be834cb815df744ec2ba96 Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Fri, 19 Aug 2011 10:20:45 +0800 Subject: [PATCH 600/974] [trac1153] update variable names to help understand --- src/bin/zonemgr/zonemgr.py.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index 2a5ae1cdbb..dff2e680f3 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -207,16 +207,16 @@ class ZonemgrRefresh: if zone_soa is None: logger.warn(ZONEMGR_NO_SOA, zone_name_class[0], zone_name_class[1]) zone_info["zone_soa_rdata"] = None - zone_reload_jitter = 0 + zone_reload_time = 0.0 else: zone_info["zone_soa_rdata"] = zone_soa[7] - zone_reload_jitter = float(zone_soa[7].split(" ")[RETRY_OFFSET]) + zone_reload_time = float(zone_soa[7].split(" ")[RETRY_OFFSET]) zone_info["zone_state"] = ZONE_OK zone_info["last_refresh_time"] = self._get_current_time() self._zonemgr_refresh_info[zone_name_class] = zone_info # Imposes some random jitters to avoid many zones need to do refresh at the same time. - zone_reload_jitter = max(self._lowerbound_retry, zone_reload_jitter) - self._set_zone_timer(zone_name_class, zone_reload_jitter, self._reload_jitter * zone_reload_jitter) + zone_reload_time = max(self._lowerbound_retry, zone_reload_time) + self._set_zone_timer(zone_name_class, zone_reload_time, self._reload_jitter * zone_reload_time) def _zone_is_expired(self, zone_name_class): """Judge whether a zone is expired or not.""" From 7fe505d131d2a13a6a412789474d92493ade65dd Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Fri, 19 Aug 2011 14:00:45 +0800 Subject: [PATCH 601/974] [trac1130] Remove the added change log entry temporarily --- ChangeLog | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index d89d82880d..b58cb7ac2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,3 @@ -280. [func] ocean - libdns++: Implement the NAPTR rrtype according to RFC2915, - RFC2168 and RFC3403. - (Trac #1130, git a030033e5a53dd18157509c6c101340688d16011) - 279. [func] jerry libdns++: Implement the AFSDB rrtype according to RFC1183. (Trac #1114, git ce052cd92cd128ea3db5a8f154bd151956c2920c) From 9622aed753d953a763a9c0ac25cd7868d257bad7 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Fri, 19 Aug 2011 14:10:33 +0800 Subject: [PATCH 602/974] [master] Update ChangeLog for trac1130. --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 02076a1e02..43c915471e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +282. [func] ocean + libdns++: Implement the NAPTR rrtype according to RFC2915, + RFC2168 and RFC3403. + (Trac #1130, git 01d8d0f13289ecdf9996d6d5d26ac0d43e30549c) + bind10-devel-20110819 released on August 19, 2011 281. [func] jelte From 8216d5dbe1ef23d56ba589fe1de619a601bada4b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 18 Aug 2011 23:49:55 -0700 Subject: [PATCH 603/974] [1068] intermediate editorial cleanup: change database/db, etc to accessor throughout the code (with fixing conflicts with the original implementation) --- src/lib/datasrc/database.cc | 49 ++-- src/lib/datasrc/database.h | 16 +- src/lib/datasrc/sqlite3_accessor.cc | 50 ++-- src/lib/datasrc/sqlite3_accessor.h | 8 +- src/lib/datasrc/tests/database_unittest.cc | 11 +- .../tests/sqlite3_accessor_unittest.cc | 250 +++++++++--------- 6 files changed, 194 insertions(+), 190 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index c6f4c11fe0..1a073ed324 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -38,10 +38,10 @@ namespace isc { namespace datasrc { DatabaseClient::DatabaseClient(boost::shared_ptr - database) : - database_(database) + accessor) : + accessor_(accessor) { - if (database_.get() == NULL) { + if (!accessor_) { isc_throw(isc::InvalidParameter, "No database provided to DatabaseClient"); } @@ -49,21 +49,21 @@ DatabaseClient::DatabaseClient(boost::shared_ptr DataSourceClient::FindResult DatabaseClient::findZone(const Name& name) const { - std::pair zone(database_->getZone(name)); + std::pair zone(accessor_->getZone(name)); // Try exact first if (zone.first) { return (FindResult(result::SUCCESS, - ZoneFinderPtr(new Finder(database_, + ZoneFinderPtr(new Finder(accessor_, zone.second, name)))); } // Then super domains // Start from 1, as 0 is covered above for (size_t i(1); i < name.getLabelCount(); ++i) { isc::dns::Name superdomain(name.split(i)); - zone = database_->getZone(superdomain); + zone = accessor_->getZone(superdomain); if (zone.first) { return (FindResult(result::PARTIALMATCH, - ZoneFinderPtr(new Finder(database_, + ZoneFinderPtr(new Finder(accessor_, zone.second, superdomain)))); } @@ -72,10 +72,9 @@ DatabaseClient::findZone(const Name& name) const { return (FindResult(result::NOTFOUND, ZoneFinderPtr())); } -DatabaseClient::Finder::Finder(boost::shared_ptr - database, int zone_id, - const isc::dns::Name& origin) : - database_(database), +DatabaseClient::Finder::Finder(boost::shared_ptr accessor, + int zone_id, const isc::dns::Name& origin) : + accessor_(accessor), zone_id_(zone_id), origin_(origin) { } @@ -183,7 +182,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, // Request the context DatabaseAccessor::IteratorContextPtr - context(database_->getRecords(name, zone_id_)); + context(accessor_->getRecords(name, zone_id_)); // It must not return NULL, that's a bug of the implementation if (!context) { isc_throw(isc::Unexpected, "Iterator context null at " + @@ -227,7 +226,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, } addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], - *database_); + *accessor_); } else if (type != NULL && cur_type == *type) { if (result_rrset && result_rrset->getType() == isc::dns::RRType::CNAME()) { @@ -240,7 +239,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, } addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], - *database_); + *accessor_); } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) { // There should be no other data, so result_rrset should // be empty. @@ -250,7 +249,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, } addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], - *database_); + *accessor_); } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) { // There should be max one RR of DNAME present if (result_rrset && @@ -260,7 +259,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, } addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], - *database_); + *accessor_); } else if (cur_type == isc::dns::RRType::RRSIG()) { // If we get signatures before we get the actual data, we // can't know which ones to keep and which to drop... @@ -306,7 +305,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, ZoneFinder::Result result_status = SUCCESS; std::pair found; logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS) - .arg(database_->getDBName()).arg(name).arg(type); + .arg(accessor_->getDBName()).arg(name).arg(type); // First, do we have any kind of delegation (NS/DNAME) here? const Name origin(getOrigin()); @@ -328,12 +327,12 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, if (result_rrset->getType() == isc::dns::RRType::NS()) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DELEGATION). - arg(database_->getDBName()).arg(superdomain); + arg(accessor_->getDBName()).arg(superdomain); result_status = DELEGATION; } else { LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DNAME). - arg(database_->getDBName()).arg(superdomain); + arg(accessor_->getDBName()).arg(superdomain); result_status = DNAME; } // Don't search more @@ -352,7 +351,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_rrset->getType() == isc::dns::RRType::NS()) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DELEGATION_EXACT). - arg(database_->getDBName()).arg(name); + arg(accessor_->getDBName()).arg(name); result_status = DELEGATION; } else if (result_rrset && type != isc::dns::RRType::CNAME() && result_rrset->getType() == isc::dns::RRType::CNAME()) { @@ -364,20 +363,20 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, if (records_found) { logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXRRSET) - .arg(database_->getDBName()).arg(name) + .arg(accessor_->getDBName()).arg(name) .arg(getClass()).arg(type); result_status = NXRRSET; } else { logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXDOMAIN) - .arg(database_->getDBName()).arg(name) + .arg(accessor_->getDBName()).arg(name) .arg(getClass()).arg(type); result_status = NXDOMAIN; } } else { logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_RRSET) - .arg(database_->getDBName()).arg(*result_rrset); + .arg(accessor_->getDBName()).arg(*result_rrset); } return (FindResult(result_status, result_rrset)); } @@ -472,7 +471,7 @@ private: ZoneIteratorPtr DatabaseClient::getIterator(const isc::dns::Name& name) const { // Get the zone - std::pair zone(database_->getZone(name)); + std::pair zone(accessor_->getZone(name)); if (!zone.first) { // No such zone, can't continue isc_throw(DataSourceError, "Zone " + name.toText() + @@ -481,7 +480,7 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const { } // Request the context DatabaseAccessor::IteratorContextPtr - context(database_->getAllRecords(zone.second)); + context(accessor_->getAllRecords(zone.second)); // It must not return NULL, that's a bug of the implementation if (context == DatabaseAccessor::IteratorContextPtr()) { isc_throw(isc::Unexpected, "Iterator context null at " + diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 0c277cb086..d54a6cac86 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -361,18 +361,19 @@ public: * applications shouldn't need it. */ int zone_id() const { return (zone_id_); } + /** - * \brief The database. + * \brief The database accessor. * - * This function provides the database stored inside as + * This function provides the database accessor stored inside as * passed to the constructor. This is meant for testing purposes and * normal applications shouldn't need it. */ - const DatabaseAccessor& database() const { - return (*database_); + const DatabaseAccessor& getAccessor() const { + return (*accessor_); } private: - boost::shared_ptr database_; + boost::shared_ptr accessor_; const int zone_id_; const isc::dns::Name origin_; /** @@ -417,6 +418,7 @@ public: bool want_dname, bool want_ns); }; + /** * \brief Find a zone in the database * @@ -453,8 +455,8 @@ public: virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const; private: - /// \brief Our database. - const boost::shared_ptr database_; + /// \brief The accessor to our database. + const boost::shared_ptr accessor_; }; } diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index a65a5276bb..28d40286d9 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -119,7 +119,7 @@ private: const char* const desc_; }; -SQLite3Database::SQLite3Database(const std::string& filename, +SQLite3Accessor::SQLite3Accessor(const std::string& filename, const isc::dns::RRClass& rrclass) : dbparameters_(new SQLite3Parameters), class_(rrclass.toText()), @@ -224,7 +224,7 @@ checkAndSetupSchema(Initializer* initializer) { } void -SQLite3Database::open(const std::string& name) { +SQLite3Accessor::open(const std::string& name) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNOPEN).arg(name); if (dbparameters_->db_ != NULL) { // There shouldn't be a way to trigger this anyway @@ -241,7 +241,7 @@ SQLite3Database::open(const std::string& name) { initializer.move(dbparameters_.get()); } -SQLite3Database::~SQLite3Database() { +SQLite3Accessor::~SQLite3Accessor() { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DROPCONN); if (dbparameters_->db_ != NULL) { close(); @@ -249,7 +249,7 @@ SQLite3Database::~SQLite3Database() { } void -SQLite3Database::close(void) { +SQLite3Accessor::close(void) { LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNCLOSE); if (dbparameters_->db_ == NULL) { isc_throw(DataSourceError, @@ -267,12 +267,12 @@ SQLite3Database::close(void) { } std::pair -SQLite3Database::getZone(const isc::dns::Name& name) const { +SQLite3Accessor::getZone(const isc::dns::Name& name) const { return (getZone(name.toText())); } std::pair -SQLite3Database::getZone(const string& name) const { +SQLite3Accessor::getZone(const string& name) const { int rc; sqlite3_stmt* const stmt = dbparameters_->statements_[ZONE]; @@ -338,31 +338,31 @@ convertToPlainChar(const unsigned char* ucp, } } -class SQLite3Database::Context : public DatabaseAccessor::IteratorContext { +class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext { public: // Construct an iterator for all records. When constructed this // way, the getNext() call will copy all fields - Context(const boost::shared_ptr& database, int id) : + Context(const boost::shared_ptr& accessor, int id) : iterator_type_(ITT_ALL), - database_(database), + accessor_(accessor), statement_(NULL) { // We create the statement now and then just keep getting data from it - statement_ = prepare(database->dbparameters_->db_, + statement_ = prepare(accessor->dbparameters_->db_, text_statements[ITERATE]); bindZoneId(id); } // Construct an iterator for records with a specific name. When constructed // this way, the getNext() call will copy all fields except name - Context(const boost::shared_ptr& database, int id, + Context(const boost::shared_ptr& accessor, int id, const isc::dns::Name& name) : iterator_type_(ITT_NAME), - database_(database), + accessor_(accessor), statement_(NULL) { // We create the statement now and then just keep getting data from it - statement_ = prepare(database->dbparameters_->db_, + statement_ = prepare(accessor->dbparameters_->db_, text_statements[ANY]); bindZoneId(id); bindName(name); @@ -385,7 +385,7 @@ public: } else if (rc != SQLITE_DONE) { isc_throw(DataSourceError, "Unexpected failure in sqlite3_step: " << - sqlite3_errmsg(database_->dbparameters_->db_)); + sqlite3_errmsg(accessor_->dbparameters_->db_)); } return (false); } @@ -406,21 +406,21 @@ private: void copyColumn(std::string (&data)[COLUMN_COUNT], int column) { data[column] = convertToPlainChar(sqlite3_column_text(statement_, column), - database_->dbparameters_.get()); + accessor_->dbparameters_.get()); } void bindZoneId(const int zone_id) { if (sqlite3_bind_int(statement_, 1, zone_id) != SQLITE_OK) { isc_throw(SQLite3Error, "Could not bind int " << zone_id << " to SQL statement: " << - sqlite3_errmsg(database_->dbparameters_->db_)); + sqlite3_errmsg(accessor_->dbparameters_->db_)); } } void bindName(const isc::dns::Name& name) { if (sqlite3_bind_text(statement_, 2, name.toText().c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) { - const char* errmsg = sqlite3_errmsg(database_->dbparameters_->db_); + const char* errmsg = sqlite3_errmsg(accessor_->dbparameters_->db_); sqlite3_finalize(statement_); isc_throw(SQLite3Error, "Could not bind text '" << name << "' to SQL statement: " << errmsg); @@ -428,22 +428,22 @@ private: } const IteratorType iterator_type_; - boost::shared_ptr database_; + boost::shared_ptr accessor_; sqlite3_stmt *statement_; }; DatabaseAccessor::IteratorContextPtr -SQLite3Database::getRecords(const isc::dns::Name& name, int id) const { +SQLite3Accessor::getRecords(const isc::dns::Name& name, int id) const { return (IteratorContextPtr(new Context(shared_from_this(), id, name))); } DatabaseAccessor::IteratorContextPtr -SQLite3Database::getAllRecords(int id) const { +SQLite3Accessor::getAllRecords(int id) const { return (IteratorContextPtr(new Context(shared_from_this(), id))); } pair -SQLite3Database::startUpdateZone(const string& zone_name, const bool replace) { +SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) { if (dbparameters_->updating_zone) { isc_throw(DataSourceError, "duplicate zone update on SQLite3 data source"); @@ -479,7 +479,7 @@ SQLite3Database::startUpdateZone(const string& zone_name, const bool replace) { } void -SQLite3Database::commitUpdateZone() { +SQLite3Accessor::commitUpdateZone() { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "committing zone update on SQLite3 " "data source without transaction"); @@ -492,7 +492,7 @@ SQLite3Database::commitUpdateZone() { } void -SQLite3Database::rollbackUpdateZone() { +SQLite3Accessor::rollbackUpdateZone() { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "rolling back zone update on SQLite3 " "data source without transaction"); @@ -531,7 +531,7 @@ doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id, } void -SQLite3Database::addRecordToZone(const vector& columns) { +SQLite3Accessor::addRecordToZone(const vector& columns) { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "adding record to SQLite3 " "data source without transaction"); @@ -545,7 +545,7 @@ SQLite3Database::addRecordToZone(const vector& columns) { } void -SQLite3Database::deleteRecordInZone(const vector& params) { +SQLite3Accessor::deleteRecordInZone(const vector& params) { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "deleting record in SQLite3 " "data source without transaction"); diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 9ba124b7c3..4d4522cf64 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -53,8 +53,8 @@ struct SQLite3Parameters; * According to the design, it doesn't interpret the data in any way, it just * provides unified access to the DB. */ -class SQLite3Database : public DatabaseAccessor, - public boost::enable_shared_from_this { +class SQLite3Accessor : public DatabaseAccessor, + public boost::enable_shared_from_this { public: /** * \brief Constructor @@ -69,14 +69,14 @@ public: * file can contain multiple classes of data, single database can * provide only one class). */ - SQLite3Database(const std::string& filename, + SQLite3Accessor(const std::string& filename, const isc::dns::RRClass& rrclass); /** * \brief Destructor * * Closes the database. */ - ~SQLite3Database(); + ~SQLite3Accessor(); /** * \brief Look up a zone diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f3311a83a6..d7916937ae 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -445,12 +445,12 @@ public: * times per test. */ void createClient() { - current_database_ = new MockAccessor(); + current_accessor_ = new MockAccessor(); client_.reset(new DatabaseClient(shared_ptr( - current_database_))); + current_accessor_))); } // Will be deleted by client_, just keep the current value for comparison. - MockAccessor* current_database_; + MockAccessor* current_accessor_; shared_ptr client_; const std::string database_name_; @@ -465,7 +465,7 @@ public: ASSERT_NE(shared_ptr(), finder) << "Wrong type of finder"; EXPECT_EQ(42, finder->zone_id()); - EXPECT_EQ(current_database_, &finder->database()); + EXPECT_EQ(current_accessor_, &finder->getAccessor()); } shared_ptr getFinder() { @@ -795,7 +795,6 @@ TEST_F(DatabaseClientTest, find) { ZoneFinder::CNAME, expected_rdatas_, expected_sig_rdatas_); - expected_rdatas_.clear(); expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); @@ -846,7 +845,6 @@ TEST_F(DatabaseClientTest, find) { ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), @@ -889,7 +887,6 @@ TEST_F(DatabaseClientTest, find) { isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), std::exception); - EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."), isc::dns::RRType::A(), NULL, ZoneFinder::FIND_DEFAULT), diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 86a682d857..27496da230 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -49,75 +49,73 @@ std::string SQLITE_DBFILE_NOTEXIST = TEST_DATA_DIR "/nodir/notexist"; // Opening works (the content is tested in different tests) TEST(SQLite3Open, common) { - EXPECT_NO_THROW(SQLite3Database db(SQLITE_DBFILE_EXAMPLE, - RRClass::IN())); + EXPECT_NO_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_EXAMPLE, + RRClass::IN())); } // The file can't be opened TEST(SQLite3Open, notExist) { - EXPECT_THROW(SQLite3Database db(SQLITE_DBFILE_NOTEXIST, - RRClass::IN()), SQLite3Error); + EXPECT_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_NOTEXIST, + RRClass::IN()), SQLite3Error); } // It rejects broken DB TEST(SQLite3Open, brokenDB) { - EXPECT_THROW(SQLite3Database db(SQLITE_DBFILE_BROKENDB, - RRClass::IN()), SQLite3Error); + EXPECT_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_BROKENDB, + RRClass::IN()), SQLite3Error); } // Test we can create the schema on the fly TEST(SQLite3Open, memoryDB) { - EXPECT_NO_THROW(SQLite3Database db(SQLITE_DBFILE_MEMORY, - RRClass::IN())); + EXPECT_NO_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_MEMORY, + RRClass::IN())); } // Test fixture for querying the db -class SQLite3Access : public ::testing::Test { +class SQLite3AccessorTest : public ::testing::Test { public: - SQLite3Access() { + SQLite3AccessorTest() { initAccessor(SQLITE_DBFILE_EXAMPLE, RRClass::IN()); } // So it can be re-created with different data void initAccessor(const std::string& filename, const RRClass& rrclass) { - db.reset(new SQLite3Database(filename, rrclass)); + accessor.reset(new SQLite3Accessor(filename, rrclass)); } - // The tested db - boost::shared_ptr db; + // The tested accessor + boost::shared_ptr accessor; }; // This zone exists in the data, so it should be found -TEST_F(SQLite3Access, getZone) { - std::pair result(db->getZone(Name("example.com"))); +TEST_F(SQLite3AccessorTest, getZone) { + std::pair result(accessor->getZone(Name("example.com"))); EXPECT_TRUE(result.first); EXPECT_EQ(1, result.second); } // But it should find only the zone, nothing below it -TEST_F(SQLite3Access, subZone) { - EXPECT_FALSE(db->getZone(Name("sub.example.com")).first); +TEST_F(SQLite3AccessorTest, subZone) { + EXPECT_FALSE(accessor->getZone(Name("sub.example.com")).first); } // This zone is not there at all -TEST_F(SQLite3Access, noZone) { - EXPECT_FALSE(db->getZone(Name("example.org")).first); +TEST_F(SQLite3AccessorTest, noZone) { + EXPECT_FALSE(accessor->getZone(Name("example.org")).first); } // This zone is there, but in different class -TEST_F(SQLite3Access, noClass) { +TEST_F(SQLite3AccessorTest, noClass) { initAccessor(SQLITE_DBFILE_EXAMPLE, RRClass::CH()); - EXPECT_FALSE(db->getZone(Name("example.com")).first); + EXPECT_FALSE(accessor->getZone(Name("example.com")).first); } // This tests the iterator context -TEST_F(SQLite3Access, iterator) { +TEST_F(SQLite3AccessorTest, iterator) { // Our test zone is conveniently small, but not empty initAccessor(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); // Get the iterator context - DatabaseAccessor::IteratorContextPtr - context(db->getAllRecords(1)); - ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), - context); + DatabaseAccessor::IteratorContextPtr context(accessor->getAllRecords(1)); + ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); const size_t size(5); std::string data[size]; @@ -132,13 +130,13 @@ TEST_F(SQLite3Access, iterator) { EXPECT_FALSE(context->getNext(data)); } -TEST_F(SQLite3Access, iteratorColumnCount) { +TEST_F(SQLite3AccessorTest, iteratorColumnCount) { // Our test zone is conveniently small, but not empty initAccessor(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); // Get the iterator context DatabaseAccessor::IteratorContextPtr - context(db->getAllRecords(1)); + context(accessor->getAllRecords(1)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); @@ -147,13 +145,13 @@ TEST_F(SQLite3Access, iteratorColumnCount) { } TEST(SQLite3Open, getDBNameExample2) { - SQLite3Database db(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); - EXPECT_EQ(SQLITE_DBNAME_EXAMPLE2, db.getDBName()); + SQLite3Accessor accessor(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); + EXPECT_EQ(SQLITE_DBNAME_EXAMPLE2, accessor.getDBName()); } TEST(SQLite3Open, getDBNameExampleROOT) { - SQLite3Database db(SQLITE_DBFILE_EXAMPLE_ROOT, RRClass::IN()); - EXPECT_EQ(SQLITE_DBNAME_EXAMPLE_ROOT, db.getDBName()); + SQLite3Accessor accessor(SQLITE_DBFILE_EXAMPLE_ROOT, RRClass::IN()); + EXPECT_EQ(SQLITE_DBNAME_EXAMPLE_ROOT, accessor.getDBName()); } // Simple function to cound the number of records for @@ -173,8 +171,9 @@ checkRecordRow(const std::string columns[], EXPECT_EQ(field4, columns[4]); } -TEST_F(SQLite3Access, getRecords) { - const std::pair zone_info(db->getZone(Name("example.com"))); +TEST_F(SQLite3AccessorTest, getRecords) { + const std::pair zone_info( + accessor->getZone(Name("example.com"))); ASSERT_TRUE(zone_info.first); const int zone_id = zone_info.second; @@ -184,14 +183,14 @@ TEST_F(SQLite3Access, getRecords) { std::string columns[column_count]; DatabaseAccessor::IteratorContextPtr - context(db->getRecords(Name("foo.bar"), 1)); + context(accessor->getRecords(Name("foo.bar"), 1)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); EXPECT_FALSE(context->getNext(columns)); checkRecordRow(columns, "", "", "", "", ""); // now try some real searches - context = db->getRecords(Name("foo.example.com."), zone_id); + context = accessor->getRecords(Name("foo.example.com."), zone_id); ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "CNAME", "3600", "", "cnametest.example.org.", ""); @@ -207,12 +206,13 @@ TEST_F(SQLite3Access, getRecords) { "NSEC 5 3 7200 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE", ""); EXPECT_FALSE(context->getNext(columns)); + // with no more records, the array should not have been modified checkRecordRow(columns, "RRSIG", "7200", "NSEC", "NSEC 5 3 7200 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE", ""); - context = db->getRecords(Name("example.com."), zone_id); + context = accessor->getRecords(Name("example.com."), zone_id); ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "SOA", "3600", "", "master.example.com. admin.example.com. " @@ -296,17 +296,17 @@ const char* const deleted_data[] = { "foo.bar.example.com.", "A", "192.0.2.1" }; -class SQLite3Update : public SQLite3Access { +class SQLite3Update : public SQLite3AccessorTest { protected: SQLite3Update() { ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR "/test.sqlite3 " TEST_DATA_BUILDDIR "/test.sqlite3.copied")); initAccessor(TEST_DATA_BUILDDIR "/test.sqlite3.copied", RRClass::IN()); - zone_id = db->getZone(Name("example.com")).second; - another_db.reset(new SQLite3Database( - TEST_DATA_BUILDDIR "/test.sqlite3.copied", - RRClass::IN())); + zone_id = accessor->getZone(Name("example.com")).second; + another_accessor.reset(new SQLite3Accessor( + TEST_DATA_BUILDDIR "/test.sqlite3.copied", + RRClass::IN())); expected_stored.push_back(common_expected_data); } @@ -318,16 +318,16 @@ protected: vector empty_stored; // indicate no corresponding data // Another accessor, emulating one running on a different process/thread - shared_ptr another_db; + shared_ptr another_accessor; DatabaseAccessor::IteratorContextPtr iterator; }; void -checkRecords(SQLite3Database& db, int zone_id, const std::string& name, +checkRecords(SQLite3Accessor& accessor, int zone_id, const std::string& name, vector expected_rows) { DatabaseAccessor::IteratorContextPtr iterator = - db.getRecords(Name(name), zone_id); + accessor.getRecords(Name(name), zone_id); std::string columns[DatabaseAccessor::COLUMN_COUNT]; vector::const_iterator it = expected_rows.begin(); while (iterator->getNext(columns)) { @@ -342,45 +342,46 @@ TEST_F(SQLite3Update, emptyUpdate) { // If we do nothing between start and commit, the zone content // should be intact. - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); - zone_id = db->startUpdateZone("example.com.", false).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); - db->commitUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); + zone_id = accessor->startUpdateZone("example.com.", false).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); + accessor->commitUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, flushZone) { // With 'replace' being true startUpdateZone() will flush the existing // zone content. - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); - zone_id = db->startUpdateZone("example.com.", true).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); - db->commitUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); + zone_id = accessor->startUpdateZone("example.com.", true).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); + accessor->commitUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); } TEST_F(SQLite3Update, readWhileUpdate) { - zone_id = db->startUpdateZone("example.com.", true).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + zone_id = accessor->startUpdateZone("example.com.", true).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Until commit is done, the other accessor should see the old data - checkRecords(*another_db, zone_id, "foo.bar.example.com.", + checkRecords(*another_accessor, zone_id, "foo.bar.example.com.", expected_stored); // Once the changes are committed, the other accessor will see the new // data. - db->commitUpdateZone(); - checkRecords(*another_db, zone_id, "foo.bar.example.com.", empty_stored); + accessor->commitUpdateZone(); + checkRecords(*another_accessor, zone_id, "foo.bar.example.com.", + empty_stored); } TEST_F(SQLite3Update, rollback) { - zone_id = db->startUpdateZone("example.com.", true).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + zone_id = accessor->startUpdateZone("example.com.", true).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Rollback will revert the change made by startUpdateZone(, true). - db->rollbackUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + accessor->rollbackUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, rollbackFailure) { @@ -389,27 +390,27 @@ TEST_F(SQLite3Update, rollbackFailure) { // the rollback operation at the end of the test. string columns[DatabaseAccessor::COLUMN_COUNT]; - iterator = db->getRecords(Name("example.com"), zone_id); + iterator = accessor->getRecords(Name("example.com"), zone_id); EXPECT_TRUE(iterator->getNext(columns)); - db->startUpdateZone("example.com.", true); - EXPECT_THROW(db->rollbackUpdateZone(), DataSourceError); + accessor->startUpdateZone("example.com.", true); + EXPECT_THROW(accessor->rollbackUpdateZone(), DataSourceError); } TEST_F(SQLite3Update, commitConflict) { // Start reading the DB by another accessor. We should stop at a single // call to getNextRecord() to keep holding the lock. - iterator = another_db->getRecords(Name("foo.example.com"), zone_id); + iterator = another_accessor->getRecords(Name("foo.example.com"), zone_id); EXPECT_TRUE(iterator->getNext(get_columns)); // Due to getNextRecord() above, the other accessor holds a DB lock, // which will prevent commit. - zone_id = db->startUpdateZone("example.com.", true).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); - EXPECT_THROW(db->commitUpdateZone(), DataSourceError); - db->rollbackUpdateZone(); // rollback should still succeed + zone_id = accessor->startUpdateZone("example.com.", true).second; + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); + EXPECT_THROW(accessor->commitUpdateZone(), DataSourceError); + accessor->rollbackUpdateZone(); // rollback should still succeed - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, updateConflict) { @@ -417,54 +418,57 @@ TEST_F(SQLite3Update, updateConflict) { // update attempt. Note that these two accessors modify disjoint sets // of data; sqlite3 only has a coarse-grained lock so we cannot allow // these updates to run concurrently. - EXPECT_TRUE(another_db->startUpdateZone("sql1.example.com.", true).first); - EXPECT_THROW(db->startUpdateZone("example.com.", true), DataSourceError); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + EXPECT_TRUE(another_accessor->startUpdateZone("sql1.example.com.", + true).first); + EXPECT_THROW(accessor->startUpdateZone("example.com.", true), + DataSourceError); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, duplicateUpdate) { - db->startUpdateZone("example.com.", false); - EXPECT_THROW(db->startUpdateZone("example.com.", false), DataSourceError); + accessor->startUpdateZone("example.com.", false); + EXPECT_THROW(accessor->startUpdateZone("example.com.", false), + DataSourceError); } TEST_F(SQLite3Update, commitWithoutTransaction) { - EXPECT_THROW(db->commitUpdateZone(), DataSourceError); + EXPECT_THROW(accessor->commitUpdateZone(), DataSourceError); } TEST_F(SQLite3Update, rollbackWithoutTransaction) { - EXPECT_THROW(db->rollbackUpdateZone(), DataSourceError); + EXPECT_THROW(accessor->rollbackUpdateZone(), DataSourceError); } TEST_F(SQLite3Update, addRecord) { // Before update, there should be no record for this name - checkRecords(*db, zone_id, "newdata.example.com.", empty_stored); + checkRecords(*accessor, zone_id, "newdata.example.com.", empty_stored); - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; update_columns.assign(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT); - db->addRecordToZone(update_columns); + accessor->addRecordToZone(update_columns); expected_stored.clear(); expected_stored.push_back(new_data); - checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored); // Commit the change, and confirm the new data is still there. - db->commitUpdateZone(); - checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); + accessor->commitUpdateZone(); + checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored); } TEST_F(SQLite3Update, addThenRollback) { - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; update_columns.assign(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT); - db->addRecordToZone(update_columns); + accessor->addRecordToZone(update_columns); expected_stored.clear(); expected_stored.push_back(new_data); - checkRecords(*db, zone_id, "newdata.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored); - db->rollbackUpdateZone(); - checkRecords(*db, zone_id, "newdata.example.com.", empty_stored); + accessor->rollbackUpdateZone(); + checkRecords(*accessor, zone_id, "newdata.example.com.", empty_stored); } TEST_F(SQLite3Update, duplicateAdd) { @@ -474,103 +478,105 @@ TEST_F(SQLite3Update, duplicateAdd) { }; expected_stored.clear(); expected_stored.push_back(dup_data); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); // Adding exactly the same data. As this backend is "dumb", another // row of the same content will be inserted. update_columns.assign(dup_data, dup_data + DatabaseAccessor::ADD_COLUMN_COUNT); - zone_id = db->startUpdateZone("example.com.", false).second; - db->addRecordToZone(update_columns); + zone_id = accessor->startUpdateZone("example.com.", false).second; + accessor->addRecordToZone(update_columns); expected_stored.push_back(dup_data); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, invalidAdd) { // An attempt of add before an explicit start of transaction - EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); // Short column vector update_columns.clear(); - zone_id = db->startUpdateZone("example.com.", false).second; - EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); + zone_id = accessor->startUpdateZone("example.com.", false).second; + EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); // Too many columns for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT + 1; ++i) { update_columns.push_back(""); } - EXPECT_THROW(db->addRecordToZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); } TEST_F(SQLite3Update, deleteRecord) { - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Commit the change, and confirm the deleted data still isn't there. - db->commitUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + accessor->commitUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); } TEST_F(SQLite3Update, deleteThenRollback) { - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "foo.bar.example.com.", empty_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Rollback the change, and confirm the data still exists. - db->rollbackUpdateZone(); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + accessor->rollbackUpdateZone(); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, deleteNonexistent) { - zone_id = db->startUpdateZone("example.com.", false).second; + zone_id = accessor->startUpdateZone("example.com.", false).second; update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); // Replace the name with a non existent one, then try to delete it. // nothing should happen. update_columns[0] = "no-such-name.example.com."; - checkRecords(*db, zone_id, "no-such-name.example.com.", empty_stored); - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "no-such-name.example.com.", empty_stored); + checkRecords(*accessor, zone_id, "no-such-name.example.com.", + empty_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "no-such-name.example.com.", + empty_stored); // Name exists but the RR type is different. Delete attempt shouldn't // delete only by name. update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); update_columns[1] = "AAAA"; - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); // Similar to the previous case, but RDATA is different. update_columns.assign(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT); update_columns[2] = "192.0.2.2"; - db->deleteRecordInZone(update_columns); - checkRecords(*db, zone_id, "foo.bar.example.com.", expected_stored); + accessor->deleteRecordInZone(update_columns); + checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, invalidDelete) { // An attempt of delete before an explicit start of transaction - EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); // Short column vector update_columns.clear(); - zone_id = db->startUpdateZone("example.com.", false).second; - EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); + zone_id = accessor->startUpdateZone("example.com.", false).second; + EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); // Too many parameters for (int i = 0; i < DatabaseAccessor::DEL_PARAM_COUNT + 1; ++i) { update_columns.push_back(""); } - EXPECT_THROW(db->deleteRecordInZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); } } // end anonymous namespace From c5e0ebf85ef50e61457f3b99a05109a92b328573 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 19 Aug 2011 01:16:06 -0700 Subject: [PATCH 604/974] [1068] make sure startUpdateZone() can be called multiple times if the first attempt results in an exception. --- src/lib/datasrc/sqlite3_accessor.cc | 36 ++++++++++++------- .../tests/sqlite3_accessor_unittest.cc | 6 ++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 28d40286d9..ab34c3b94e 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -454,27 +454,37 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) { return (zone_info); } - dbparameters_->updating_zone = true; - dbparameters_->updated_zone_id = zone_info.second; - StatementExecuter(*dbparameters_, BEGIN, "start an SQLite3 transaction").exec(); if (replace) { - StatementExecuter delzone_exec(*dbparameters_, DEL_ZONE_RECORDS, - "delete zone records"); + try { + StatementExecuter delzone_exec(*dbparameters_, DEL_ZONE_RECORDS, + "delete zone records"); - sqlite3_clear_bindings(dbparameters_->statements_[DEL_ZONE_RECORDS]); - if (sqlite3_bind_int(dbparameters_->statements_[DEL_ZONE_RECORDS], - 1, zone_info.second) != SQLITE_OK) { - isc_throw(DataSourceError, - "failed to bind SQLite3 parameter: " << - sqlite3_errmsg(dbparameters_->db_)); + sqlite3_clear_bindings( + dbparameters_->statements_[DEL_ZONE_RECORDS]); + if (sqlite3_bind_int(dbparameters_->statements_[DEL_ZONE_RECORDS], + 1, zone_info.second) != SQLITE_OK) { + isc_throw(DataSourceError, + "failed to bind SQLite3 parameter: " << + sqlite3_errmsg(dbparameters_->db_)); + } + + delzone_exec.exec(); + } catch (const DataSourceError&) { + // Once we start a transaction, if something unexpected happens + // we need to rollback the transaction so that a subsequent update + // is still possible with this accessor. + StatementExecuter(*dbparameters_, ROLLBACK, + "rollback an SQLite3 transaction").exec(); + throw; } - - delzone_exec.exec(); } + dbparameters_->updating_zone = true; + dbparameters_->updated_zone_id = zone_info.second; + return (zone_info); } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 27496da230..c4ff357824 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -423,6 +423,12 @@ TEST_F(SQLite3Update, updateConflict) { EXPECT_THROW(accessor->startUpdateZone("example.com.", true), DataSourceError); checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); + + // Once we rollback the other attempt of change, we should be able to + // start and commit the transaction using the main accessor. + another_accessor->rollbackUpdateZone(); + accessor->startUpdateZone("example.com.", true); + accessor->commitUpdateZone(); } TEST_F(SQLite3Update, duplicateUpdate) { From 9d3e78f0d8075ad62391ed005e1e82f79f05e2ca Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 19 Aug 2011 11:00:50 +0200 Subject: [PATCH 605/974] [1138] include and namespace --- src/lib/dns/rdata/in_1/dhcid_49.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/rdata/in_1/dhcid_49.cc b/src/lib/dns/rdata/in_1/dhcid_49.cc index 3a34d50572..0a9a23c792 100644 --- a/src/lib/dns/rdata/in_1/dhcid_49.cc +++ b/src/lib/dns/rdata/in_1/dhcid_49.cc @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,7 @@ DHCID::DHCID(const string& dhcid_str) { stringbuf digestbuf; iss >> &digestbuf; - decodeHex(digestbuf.str(), digest_); + isc::util::encode::decodeHex(digestbuf.str(), digest_); // RFC4701 states DNS software should consider the RDATA section to // be opaque, but there must be at least three bytes in the data: @@ -111,7 +112,7 @@ DHCID::toWire(AbstractMessageRenderer& renderer) const { /// \return A string representation of \c DHCID. string DHCID::toText() const { - return (encodeHex(digest_)); + return (isc::util::encode::encodeHex(digest_)); } /// \brief Compare two instances of \c DHCID RDATA. From ad36c799ff07d47ebd5c861c63e9feef50408e34 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 19 Aug 2011 02:17:23 -0700 Subject: [PATCH 606/974] [1068] overall comment updates --- src/lib/datasrc/database.h | 260 ++++++++++++++++++--- src/lib/datasrc/sqlite3_accessor.h | 24 +- src/lib/datasrc/tests/database_unittest.cc | 9 + 3 files changed, 252 insertions(+), 41 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index d54a6cac86..f2aa9ed8ff 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -51,14 +51,57 @@ public: /// The number of fields the columns array passed to getNext should have static const size_t COLUMN_COUNT = 5; - /// TBD - /// Compliant database should support the following columns: - /// name, rname, ttl, rdtype, sigtype, rdata - /// (even though their internal representation may be different). - static const size_t ADD_COLUMN_COUNT = 6; + /** + * Definitions of the fields as they are required to be filled in + * by IteratorContext::getNext() + * + * When implementing getNext(), the columns array should + * be filled with the values as described in this enumeration, + * in this order, i.e. TYPE_COLUMN should be the first element + * (index 0) of the array, TTL_COLUMN should be the second element + * (index 1), etc. + */ + enum RecordColumns { + TYPE_COLUMN = 0, ///< The RRType of the record (A/NS/TXT etc.) + TTL_COLUMN = 1, ///< The TTL of the record (a + SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPE + ///< the RRSIG covers. In the current implementation, + ///< this field is ignored. + RDATA_COLUMN = 3, ///< Full text representation of the record's RDATA + NAME_COLUMN = 4 ///< The domain name of this RR + }; - /// TBD - static const size_t DEL_PARAM_COUNT = 3; + /** + * Definitions of the fields to be passed to addRecordToZone(). + * + * Each derived implementation of addRecordToZone() should expect + * the "columns" vector to be filled with the values as described in this + * enumeration, in this order. + */ + enum AddRecordColumns { + ADD_NAME = 0, ///< The owner name of the record (a domain name) + ADD_REV_NAME = 1, ///< Reversed name of NAME (used for DNSSEC) + ADD_TTL = 2, ///< The TTL of the record (an integer) + ADD_TYPE = 3, ///< The RRType of the record (A/NS/TXT etc.) + ADD_SIGTYPE = 4, ///< For RRSIG records, this contains the RRTYPE + ///< the RRSIG covers. + ADD_RDATA = 5, ///< Full text representation of the record's RDATA + ADD_COLUMN_COUNT = 6 ///< Number of columns + }; + + /** + * Definitions of the fields to be passed to deleteRecordInZone(). + * + * Each derived implementation of deleteRecordInZone() should expect + * the "params" vector to be filled with the values as described in this + * enumeration, in this order. + */ + enum DeleteRecordParams { + DEL_NAME = 0, ///< The owner name of the record (a domain name) + DEL_TYPE = 1, ///< The RRType of the record (A/NS/TXT etc.) + DEL_RDATA = 2, ///< Full text representation of the record's RDATA + DEL_PARAM_COUNT = 3 ///< Number of parameters + }; /** * \brief Destructor @@ -208,25 +251,190 @@ public: "This database datasource can't be iterated"); } - /** - * Definitions of the fields as they are required to be filled in - * by IteratorContext::getNext() - * - * When implementing getNext(), the columns array should - * be filled with the values as described in this enumeration, - * in this order, i.e. TYPE_COLUMN should be the first element - * (index 0) of the array, TTL_COLUMN should be the second element - * (index 1), etc. - */ - enum RecordColumns { - TYPE_COLUMN = 0, ///< The RRType of the record (A/NS/TXT etc.) - TTL_COLUMN = 1, ///< The TTL of the record (a - SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPE - ///< the RRSIG covers. In the current implementation, - ///< this field is ignored. - RDATA_COLUMN = 3, ///< Full text representation of the record's RDATA - NAME_COLUMN = 4 ///< The domain name of this RR - }; + /// Start a transaction for updating a zone. + /// + /// Each derived class version of this method starts a database + /// transaction to make updates to the given name of zone (whose class was + /// specified at the construction of the class). + /// + /// If \c replace is true, any existing records of the zone will be + /// deleted on successful completion of updates (after + /// \c commitUpdateZone()); if it's false, the existing records will be + /// intact unless explicitly deleted by \c deleteRecordInZone(). + /// + /// A single \c DatabaseAccessor instance can perform at most one update + /// transaction; a duplicate call to this method before + /// \c commitUpdateZone() or \c rollbackUpdateZone() will result in + /// a \c DataSourceError exception. If multiple update attempts need + /// to be performed concurrently (and if the underlying database allows + /// such operation), separate \c DatabaseAccessor instance must be + /// created. + /// + /// \note The underlying database may not allow concurrent updates to + /// the same database instance even if different "connections" (or + /// something similar specific to the database implementation) are used + /// for different sets of updates. For example, it doesn't seem to be + /// possible for SQLite3 unless different databases are used. MySQL + /// allows concurrent updates to different tables of the same database, + /// but a specific operation may block others. As such, this interface + /// doesn't require derived classes to allow concurrent updates with + /// multiple \c DatabaseAccessor instances; however, the implementation + /// is encouraged to do the best for making it more likely to succeed + /// as long as the underlying database system allows concurrent updates. + /// + /// This method returns a pair of \c bool and \c int. Its first element + /// indicates whether the given name of zone is found. If it's false, + /// the transaction isn't considered to be started; a subsequent call to + /// this method with an existing zone name should succeed. Likewise, + /// if a call to this method results in an exception, the transaction + /// isn't considered to be started. Note also that if the zone is not + /// found this method doesn't try to create a new one in the database. + /// It must have been created by some other means beforehand. + /// + /// The second element is the internal zone ID used for subsequent + /// updates. Depending on implementation details of the actual derived + /// class method, it may be different from the one returned by + /// \c getZone(); for example, a specific implementation may use a + /// completely new zone ID when \c replace is true. + /// + /// \exception DataSourceError Duplicate call to this method, or some + /// internal database related error. + /// + /// \param zone_name A string representation of the zone name to be updated + /// \param replace Whether to replace the entire zone (see above) + /// + /// \return A pair of bool and int, indicating whether the specified zone + /// exists and (if so) the zone ID to be used for the update, respectively. + virtual std::pair startUpdateZone(const std::string& zone_name, + bool replace) = 0; + + /// Add a single record to the zone to be updated. + /// + /// This method provides a simple interface to insert a new record + /// (a database "row") to the zone in the update context started by + /// \c startUpdateZone(). The zone to which the record to be added + /// is the one specified at the time of the \c startUpdateZone() call. + /// + /// A successful call to \c startUpdateZone() must have preceded to + /// this call; otherwise a \c DataSourceError exception will be thrown. + /// + /// The row is defined as a vector of strings that has exactly + /// ADD_COLUMN_COUNT number of elements. See AddRecordColumns for + /// the semantics of each element. + /// + /// Derived class methods are not required to check whether the given + /// values in \c columns are valid in terms of the expected semantics; + /// in general, it's the caller's responsibility. + /// For example, TTLs would normally be expected to be a textual + /// representation of decimal numbers, but this interface doesn't require + /// the implementation to perform this level of validation. It may check + /// the values, however, and in that case if it detects an error it + /// should throw a \c DataSourceError exception. + /// + /// Likewise, derived class methods are not required to detect any + /// duplicate record that is already in the zone. + /// + /// \note The underlying database schema may not have a trivial mapping + /// from this style of definition of rows to actual database records. + /// It's the implementation's responsibility to implement the mapping + /// in the actual derived method. + /// + /// \exception DataSourceError Invalid call without starting a transaction, + /// the columns parameter has an invalid number of elements, or other + /// internal database error. + /// + /// \param columns A vector of strings that defines a record to be added + /// to the zone. + virtual void addRecordToZone(const std::vector& columns) = 0; + + /// Delete a single record from the zone to be updated. + /// + /// This method provides a simple interface to delete a record + /// (a database "row") from the zone in the update context started by + /// \c startUpdateZone(). The zone from which the record to be deleted + /// is the one specified at the time of the \c startUpdateZone() call. + /// + /// A successful call to \c startUpdateZone() must have preceded to + /// this call; otherwise a \c DataSourceError exception will be thrown. + /// + /// The record to be deleted is specified by a vector of strings that has + /// exactly DEL_PARAM_COUNT number of elements. See DeleteRecordParams + /// for the semantics of each element. + /// + /// \note In IXFR, TTL may also be specified, but we intentionally + /// ignore that in this interface, because it's not guaranteed + /// that all records have the same TTL (unlike the RRset + /// assumption) and there can even be multiple records for the + /// same name, type and rdata with different TTLs. If we only + /// delete one of them, subsequent lookup will still return a + /// positive answer, which would be confusing. It's a higher + /// layer's responsibility to check if there is at least one + /// record in the database that has the given TTL. + /// + /// Like \c addRecordToZone, derived class methods are not required to + /// validate the semantics of the given parameters or to check if there + /// is a record that matches the specified parameter; if there isn't + /// it simply ignores the result. + /// + /// \exception DataSourceError Invalid call without starting a transaction, + /// the columns parameter has an invalid number of elements, or other + /// internal database error. + /// + /// \param params A vector of strings that defines a record to be deleted + /// from the zone. + virtual void deleteRecordInZone( + const std::vector& params) = 0; + + /// Commit updates to the zone. + /// + /// This method completes a transaction of making updates to the zone + /// in the context started by startUpdateZone. + /// + /// A successful call to \c startUpdateZone() must have preceded to + /// this call; otherwise a \c DataSourceError exception will be thrown. + /// Once this method successfully completes, the transaction isn't + /// considered to exist any more. So a new transaction can now be + /// started. On the other hand, a duplicate call to this method after + /// a successful completion of it is invalid and should result in + /// a \c DataSourceError exception. + /// + /// If some internal database error happens, a \c DataSourceError + /// exception must be thrown. In that case the transaction is still + /// considered to be valid; the caller must explicitly rollback it + /// or (if it's confident that the error is temporary) try to commit it + /// again. + /// + /// \exception DataSourceError Call without a transaction, duplicate call + /// to the method or internal database error. + virtual void commitUpdateZone() = 0; + + /// Rollback updates to the zone made so far. + /// + /// This method rollbacks a transaction of making updates to the zone + /// in the context started by startUpdateZone. When it succeeds + /// (it normally should, but see below), the underlying database should + /// be reverted to the point before performing the corresponding + /// \c startUpdateZone(). + /// + /// A successful call to \c startUpdateZone() must have preceded to + /// this call; otherwise a \c DataSourceError exception will be thrown. + /// Once this method successfully completes, the transaction isn't + /// considered to exist any more. So a new transaction can now be + /// started. On the other hand, a duplicate call to this method after + /// a successful completion of it is invalid and should result in + /// a \c DataSourceError exception. + /// + /// Normally this method should not fail. But it may not always be + /// possible to guarantee it depending on the characteristics of the + /// underlying database system. So this interface doesn't require the + /// actual implementation for the error free property. But if a specific + /// implementation of this method can fail, it is encouraged to document + /// when that can happen with its implication. + /// + /// \exception DataSourceError Call without a transaction, duplicate call + /// to the method or internal database error. + virtual void rollbackUpdateZone() = 0; + /** * \brief Returns a string identifying this dabase backend diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 4d4522cf64..471f6a1e80 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -119,35 +119,29 @@ public: */ virtual IteratorContextPtr getAllRecords(int id) const; - /// TBD - /// This cannot be nested. virtual std::pair startUpdateZone(const std::string& zone_name, bool replace); - /// TBD - /// Note: we are quite impatient here: it's quite possible that the COMMIT + /// \note we are quite impatient here: it's quite possible that the COMMIT /// fails due to other process performing SELECT on the same database /// (consider the case where COMMIT is done by xfrin or dynamic update /// server while an authoritative server is busy reading the DB). /// In a future version we should probably need to introduce some retry /// attempt and/or increase timeout before giving up the COMMIT, even - /// if it still doesn't guarantee 100% success. + /// if it still doesn't guarantee 100% success. Right now this + /// implementation throws a \c DataSourceError exception in such a case. virtual void commitUpdateZone(); - /// TBD - /// - /// In SQLite3 rollback can fail if there's another unfinished statement - /// is performed for the same database structure. Although it's not - /// expected to happen in our expected usage, it's not guaranteed to be - /// prevented at the API level. If it ever happens, this method throws - /// an \c DataSourceError exception. It should be considered a bug of - /// the higher level application program. + /// \note In SQLite3 rollback can fail if there's another unfinished + /// statement is performed for the same database structure. + /// Although it's not expected to happen in our expected usage, it's not + /// guaranteed to be prevented at the API level. If it ever happens, this + /// method throws a \c DataSourceError exception. It should be + /// considered a bug of the higher level application program. virtual void rollbackUpdateZone(); - /// TBD virtual void addRecordToZone(const std::vector& columns); - /// TBD virtual void deleteRecordInZone(const std::vector& params); /// The SQLite3 implementation of this method returns a string starting diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index d7916937ae..f832b1d652 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -58,6 +58,15 @@ public: } } + virtual std::pair startUpdateZone(const std::string&, bool) { + // return dummy value. unused anyway. + return (pair(true, 0)); + } + virtual void commitUpdateZone() {} + virtual void rollbackUpdateZone() {} + virtual void addRecordToZone(const std::vector&) {} + virtual void deleteRecordInZone(const std::vector&) {} + virtual const std::string& getDBName() const { return (database_name_); } From b97162729a3ad4214e5f6b85452a27904b8f34ca Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 19 Aug 2011 12:02:28 +0200 Subject: [PATCH 607/974] [1183] make name arg of getRecords std::string --- src/lib/datasrc/database.cc | 2 +- src/lib/datasrc/database.h | 2 +- src/lib/datasrc/sqlite3_accessor.cc | 8 ++++---- src/lib/datasrc/sqlite3_accessor.h | 2 +- src/lib/datasrc/tests/database_unittest.cc | 8 ++++---- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index c6f4c11fe0..f0ac19266d 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -183,7 +183,7 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, // Request the context DatabaseAccessor::IteratorContextPtr - context(database_->getRecords(name, zone_id_)); + context(database_->getRecords(name.toText(), zone_id_)); // It must not return NULL, that's a bug of the implementation if (!context) { isc_throw(isc::Unexpected, "Iterator context null at " + diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 68568590f0..5fdf43e5a1 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -155,7 +155,7 @@ public: * \param id The ID of the zone, returned from getZone(). * \return Newly created iterator context. Must not be NULL. */ - virtual IteratorContextPtr getRecords(const isc::dns::Name& name, + virtual IteratorContextPtr getRecords(const std::string& name, int id) const { /* diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 070c6c50a2..158609d3af 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -382,7 +382,7 @@ public: // Construct an iterator for records with a specific name. When constructed // this way, the getNext() call will copy all fields except name Context(const boost::shared_ptr& database, int id, - const isc::dns::Name& name) : + const std::string& name) : iterator_type_(ITT_NAME), database_(database), statement_(NULL) @@ -442,8 +442,8 @@ private: } } - void bindName(const isc::dns::Name& name) { - if (sqlite3_bind_text(statement_, 2, name.toText().c_str(), -1, + void bindName(const std::string& name) { + if (sqlite3_bind_text(statement_, 2, name.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) { const char* errmsg = sqlite3_errmsg(database_->dbparameters_->db_); sqlite3_finalize(statement_); @@ -458,7 +458,7 @@ private: }; DatabaseAccessor::IteratorContextPtr -SQLite3Database::getRecords(const isc::dns::Name& name, int id) const { +SQLite3Database::getRecords(const std::string& name, int id) const { return (IteratorContextPtr(new Context(shared_from_this(), id, name))); } diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 6737c0b5c4..50b15e79b2 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -104,7 +104,7 @@ public: * \param id the zone id, as returned by getZone() * \return Iterator that contains all records with the given name */ - virtual IteratorContextPtr getRecords(const isc::dns::Name& name, + virtual IteratorContextPtr getRecords(const std::string& name, int id) const; /** \brief Look up all resource records for a zone diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f3311a83a6..2936cadbfd 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -84,8 +84,8 @@ private: class MockNameIteratorContext : public IteratorContext { public: MockNameIteratorContext(const MockAccessor& mock_accessor, int zone_id, - const isc::dns::Name& name) : - searched_name_(name.toText()), cur_record_(0) + const std::string& name) : + searched_name_(name), cur_record_(0) { // 'hardcoded' name to trigger exceptions (for testing // the error handling of find() (the other on is below in @@ -235,7 +235,7 @@ public: } } - virtual IteratorContextPtr getRecords(const Name& name, int id) const { + virtual IteratorContextPtr getRecords(const std::string& name, int id) const { if (id == 42) { return (IteratorContextPtr(new MockNameIteratorContext(*this, id, name))); } else { @@ -424,7 +424,7 @@ private: // This tests the default getRecords behaviour, throwing NotImplemented TEST(DatabaseConnectionTest, getRecords) { - EXPECT_THROW(NopAccessor().getRecords(Name("."), 1), + EXPECT_THROW(NopAccessor().getRecords(".", 1), isc::NotImplemented); } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index a42eaad0cb..21e419443d 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -179,14 +179,14 @@ TEST_F(SQLite3Access, getRecords) { std::string columns[column_count]; DatabaseAccessor::IteratorContextPtr - context(db->getRecords(Name("foo.bar"), 1)); + context(db->getRecords("foo.bar", 1)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); EXPECT_FALSE(context->getNext(columns)); checkRecordRow(columns, "", "", "", "", ""); // now try some real searches - context = db->getRecords(Name("foo.example.com."), zone_id); + context = db->getRecords("foo.example.com.", zone_id); ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "CNAME", "3600", "", "cnametest.example.org.", ""); @@ -207,7 +207,7 @@ TEST_F(SQLite3Access, getRecords) { "NSEC 5 3 7200 20100322084538 20100220084538 33495 " "example.com. FAKEFAKEFAKEFAKE", ""); - context = db->getRecords(Name("example.com."), zone_id); + context = db->getRecords("example.com.", zone_id); ASSERT_TRUE(context->getNext(columns)); checkRecordRow(columns, "SOA", "3600", "", "master.example.com. admin.example.com. " From 35a0136d56de7faca280666ba40bb1b87a85fff6 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 19 Aug 2011 15:09:47 +0200 Subject: [PATCH 608/974] [1183] review comments on implementation --- src/lib/datasrc/database.h | 111 ++++++++------------- src/lib/datasrc/sqlite3_accessor.cc | 76 +++++++------- src/lib/datasrc/tests/database_unittest.cc | 9 ++ 3 files changed, 91 insertions(+), 105 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 5fdf43e5a1..95c1c5a8a4 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -48,8 +48,27 @@ namespace datasrc { */ class DatabaseAccessor : boost::noncopyable { public: - /// The number of fields the columns array passed to getNext should have - static const size_t COLUMN_COUNT = 5; + /** + * Definitions of the fields as they are required to be filled in + * by IteratorContext::getNext() + * + * When implementing getNext(), the columns array should + * be filled with the values as described in this enumeration, + * in this order, i.e. TYPE_COLUMN should be the first element + * (index 0) of the array, TTL_COLUMN should be the second element + * (index 1), etc. + */ + enum RecordColumns { + TYPE_COLUMN = 0, ///< The RRType of the record (A/NS/TXT etc.) + TTL_COLUMN = 1, ///< The TTL of the record (a + SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPE + ///< the RRSIG covers. In the current implementation, + ///< this field is ignored. + RDATA_COLUMN = 3, ///< Full text representation of the record's RDATA + NAME_COLUMN = 4, ///< The domain name of this RR + COLUMN_COUNT = 5 ///< The total number of columns, MUST be value of + ///< the largest other element in this enum plus 1. + }; /** * \brief Destructor @@ -110,9 +129,10 @@ public: * * Depending on how the iterator was constructed, there is a difference * in behaviour; for a 'full zone iterator', created with - * getAllRecords(), all 5 elements of the array are overwritten. - * For a 'name iterator', created with getRecords(), the fifth column - * (NAME_COLUMN) is untouched, since what would be added here is by + * getAllRecords(), all COLUMN_COUNT elements of the array are + * overwritten. + * For a 'name iterator', created with getRecords(), the column + * NAME_COLUMN is untouched, since what would be added here is by * definition already known to the caller (it already passes it as * an argument to getRecords()). * @@ -137,87 +157,40 @@ public: /** * \brief Creates an iterator context for a specific name. * - * This should create a new iterator context to be used by - * DatabaseAccessor's ZoneIterator. It can be created based on the name - * or the ID (returned from getZone()), what is more comfortable for the - * database implementation. Both are provided (and are guaranteed to match, - * the DatabaseClient first looks up the zone ID and then calls this). - * - * The default implementation throws isc::NotImplemented, to allow - * "minimal" implementations of the connection not supporting optional - * functionality. + * Returns an IteratorContextPtr that contains all records of the + * given name from the given zone. * * The implementation of the iterator that is returned may leave the - * fifth column of the array passed to getNext() untouched, as that + * NAME_COLUMN column of the array passed to getNext() untouched, as that * data is already known (it is the same as the name argument here) * - * \param name The name to search for. + * \exception any Since any implementation can be used, the caller should + * expect any exception to be thrown. + * + * \param name The name to search for. This should be a FQDN. * \param id The ID of the zone, returned from getZone(). * \return Newly created iterator context. Must not be NULL. */ virtual IteratorContextPtr getRecords(const std::string& name, - int id) const - { - /* - * This is a compromise. We need to document the parameters in doxygen, - * so they need a name, but then it complains about unused parameter. - * This is a NOP that "uses" the parameters. - */ - static_cast(name); - static_cast(id); - - isc_throw(isc::NotImplemented, - "This database datasource can't be iterated"); - } + int id) const = 0; /** * \brief Creates an iterator context for the whole zone. * - * This should create a new iterator context to be used by - * DatabaseAccessor's ZoneIterator. It can be created based on the name - * or the ID (returned from getZone()), what is more comfortable for the - * database implementation. Both are provided (and are guaranteed to match, - * the DatabaseClient first looks up the zone ID and then calls this). + * Returns an IteratorContextPtr that contains all records of the + * zone with the given zone id. * - * The default implementation throws isc::NotImplemented, to allow - * "minimal" implementations of the connection not supporting optional - * functionality. + * Each call to getNext() on the returned iterator should copy all + * column fields of the array that is passed, as defined in the + * RecordColumns enum. + * + * \exception any Since any implementation can be used, the caller should + * expect any exception to be thrown. * * \param id The ID of the zone, returned from getZone(). * \return Newly created iterator context. Must not be NULL. */ - virtual IteratorContextPtr getAllRecords(int id) const - { - /* - * This is a compromise. We need to document the parameters in doxygen, - * so they need a name, but then it complains about unused parameter. - * This is a NOP that "uses" the parameters. - */ - static_cast(id); - - isc_throw(isc::NotImplemented, - "This database datasource can't be iterated"); - } - - /** - * Definitions of the fields as they are required to be filled in - * by IteratorContext::getNext() - * - * When implementing getNext(), the columns array should - * be filled with the values as described in this enumeration, - * in this order, i.e. TYPE_COLUMN should be the first element - * (index 0) of the array, TTL_COLUMN should be the second element - * (index 1), etc. - */ - enum RecordColumns { - TYPE_COLUMN = 0, ///< The RRType of the record (A/NS/TXT etc.) - TTL_COLUMN = 1, ///< The TTL of the record (a - SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPE - ///< the RRSIG covers. In the current implementation, - ///< this field is ignored. - RDATA_COLUMN = 3, ///< Full text representation of the record's RDATA - NAME_COLUMN = 4 ///< The domain name of this RR - }; + virtual IteratorContextPtr getAllRecords(int id) const = 0; /** * \brief Returns a string identifying this dabase backend diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 158609d3af..86487aeda3 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -336,34 +336,6 @@ SQLite3Database::getZone(const isc::dns::Name& name) const { return (std::pair(false, 0)); } -namespace { -// This helper function converts from the unsigned char* type (used by -// sqlite3) to char* (wanted by std::string). Technically these types -// might not be directly convertable -// In case sqlite3_column_text() returns NULL, we just make it an -// empty string. -// The sqlite3parameters value is only used to check the error code if -// ucp == NULL -const char* -convertToPlainChar(const unsigned char* ucp, - SQLite3Parameters* dbparameters) { - if (ucp == NULL) { - // The field can really be NULL, in which case we return an - // empty string, or sqlite may have run out of memory, in - // which case we raise an error - if (dbparameters != NULL && - sqlite3_errcode(dbparameters->db_) == SQLITE_NOMEM) { - isc_throw(DataSourceError, - "Sqlite3 backend encountered a memory allocation " - "error in sqlite3_column_text()"); - } else { - return (""); - } - } - const void* p = ucp; - return (static_cast(p)); -} -} class SQLite3Database::Context : public DatabaseAccessor::IteratorContext { public: @@ -372,7 +344,8 @@ public: Context(const boost::shared_ptr& database, int id) : iterator_type_(ITT_ALL), database_(database), - statement_(NULL) + statement_(NULL), + name_("") { // We create the statement now and then just keep getting data from it statement_ = prepare(database->dbparameters_->db_, q_iterate_str); @@ -385,12 +358,13 @@ public: const std::string& name) : iterator_type_(ITT_NAME), database_(database), - statement_(NULL) + statement_(NULL), + name_(name) { // We create the statement now and then just keep getting data from it statement_ = prepare(database->dbparameters_->db_, q_any_str); bindZoneId(id); - bindName(name); + bindName(name_); } bool getNext(std::string (&data)[COLUMN_COUNT]) { @@ -412,11 +386,12 @@ public: "Unexpected failure in sqlite3_step: " << sqlite3_errmsg(database_->dbparameters_->db_)); } + finalize(); return (false); } virtual ~Context() { - sqlite3_finalize(statement_); + finalize(); } private: @@ -430,12 +405,12 @@ private: void copyColumn(std::string (&data)[COLUMN_COUNT], int column) { data[column] = convertToPlainChar(sqlite3_column_text(statement_, - column), - database_->dbparameters_.get()); + column)); } void bindZoneId(const int zone_id) { if (sqlite3_bind_int(statement_, 1, zone_id) != SQLITE_OK) { + finalize(); isc_throw(SQLite3Error, "Could not bind int " << zone_id << " to SQL statement: " << sqlite3_errmsg(database_->dbparameters_->db_)); @@ -444,17 +419,46 @@ private: void bindName(const std::string& name) { if (sqlite3_bind_text(statement_, 2, name.c_str(), -1, - SQLITE_TRANSIENT) != SQLITE_OK) { + SQLITE_STATIC) != SQLITE_OK) { const char* errmsg = sqlite3_errmsg(database_->dbparameters_->db_); - sqlite3_finalize(statement_); + finalize(); isc_throw(SQLite3Error, "Could not bind text '" << name << "' to SQL statement: " << errmsg); } } + void finalize() { + sqlite3_finalize(statement_); + statement_ = NULL; + } + + // This helper method converts from the unsigned char* type (used by + // sqlite3) to char* (wanted by std::string). Technically these types + // might not be directly convertable + // In case sqlite3_column_text() returns NULL, we just make it an + // empty string, unless it was caused by a memory error + const char* convertToPlainChar(const unsigned char* ucp) { + if (ucp == NULL) { + // The field can really be NULL, in which case we return an + // empty string, or sqlite may have run out of memory, in + // which case we raise an error + if (sqlite3_errcode(database_->dbparameters_->db_) + == SQLITE_NOMEM) { + isc_throw(DataSourceError, + "Sqlite3 backend encountered a memory allocation " + "error in sqlite3_column_text()"); + } else { + return (""); + } + } + const void* p = ucp; + return (static_cast(p)); + } + const IteratorType iterator_type_; boost::shared_ptr database_; sqlite3_stmt *statement_; + const std::string name_; }; DatabaseAccessor::IteratorContextPtr diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 2936cadbfd..529d987d15 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -62,6 +62,15 @@ public: return (database_name_); } + virtual IteratorContextPtr getRecords(const std::string&, int) const { + isc_throw(isc::NotImplemented, + "This database datasource can't be iterated"); + }; + + virtual IteratorContextPtr getAllRecords(int) const { + isc_throw(isc::NotImplemented, + "This database datasource can't be iterated"); + }; private: const std::string database_name_; From 6318db7dc90cb6656cc2a1f8e875f2258f6a4343 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 19 Aug 2011 16:20:00 +0200 Subject: [PATCH 609/974] [1183] address review comments for tests --- src/lib/datasrc/tests/database_unittest.cc | 6 +- .../tests/sqlite3_accessor_unittest.cc | 116 +++++++++++++----- 2 files changed, 86 insertions(+), 36 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 529d987d15..2153f015b2 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -96,9 +96,9 @@ private: const std::string& name) : searched_name_(name), cur_record_(0) { - // 'hardcoded' name to trigger exceptions (for testing - // the error handling of find() (the other on is below in - // if the name is "exceptiononsearch" it'll raise an exception here + // 'hardcoded' names to trigger exceptions + // On these names some exceptions are throws, to test the robustness + // of the find() method. if (searched_name_ == "dsexception.in.search.") { isc_throw(DataSourceError, "datasource exception on search"); } else if (searched_name_ == "iscexception.in.search.") { diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 21e419443d..295bed27c6 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -35,6 +35,7 @@ std::string SQLITE_DBFILE_EXAMPLE_ROOT = TEST_DATA_DIR "/test-root.sqlite3"; std::string SQLITE_DBNAME_EXAMPLE_ROOT = "sqlite3_test-root.sqlite3"; std::string SQLITE_DBFILE_BROKENDB = TEST_DATA_DIR "/brokendb.sqlite3"; std::string SQLITE_DBFILE_MEMORY = ":memory:"; +std::string SQLITE_DBFILE_EXAMPLE_ORG = TEST_DATA_DIR "/example.org.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, @@ -106,39 +107,89 @@ TEST_F(SQLite3Access, noClass) { // This tests the iterator context TEST_F(SQLite3Access, iterator) { // Our test zone is conveniently small, but not empty - initAccessor(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); + initAccessor(SQLITE_DBFILE_EXAMPLE_ORG, RRClass::IN()); + + const std::pair zone_info(db->getZone(Name("example.org"))); + ASSERT_TRUE(zone_info.first); // Get the iterator context DatabaseAccessor::IteratorContextPtr - context(db->getAllRecords(1)); - ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), - context); - - const size_t size(5); - std::string data[size]; - // Get and check the first and only record - EXPECT_TRUE(context->getNext(data)); - EXPECT_EQ("example2.com.", data[4]); - EXPECT_EQ("SOA", data[0]); - EXPECT_EQ("master.example2.com. admin.example2.com. " - "1234 3600 1800 2419200 7200", data[3]); - EXPECT_EQ("3600", data[1]); - // Check there's no other - EXPECT_FALSE(context->getNext(data)); -} - -TEST_F(SQLite3Access, iteratorColumnCount) { - // Our test zone is conveniently small, but not empty - initAccessor(SQLITE_DBFILE_EXAMPLE2, RRClass::IN()); - - // Get the iterator context - DatabaseAccessor::IteratorContextPtr - context(db->getAllRecords(1)); + context(db->getAllRecords(zone_info.second)); ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context); std::string data[DatabaseAccessor::COLUMN_COUNT]; - EXPECT_NO_THROW(context->getNext(data)); + // Get and check the first and only record + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("DNAME", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("dname.example.info.", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("dname.example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("DNAME", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("dname2.example.info.", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("dname2.foo.example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("MX", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("10 mail.example.org.", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("NS", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("ns1.example.org.", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("NS", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("ns2.example.org.", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("NS", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("ns3.example.org.", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("SOA", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("ns1.example.org. admin.example.org. " + "1234 3600 1800 2419200 7200", + data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("A", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("192.0.2.10", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("mail.example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("A", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("192.0.2.101", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("ns.sub.example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("NS", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("ns.sub.example.org.", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("sub.example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + EXPECT_TRUE(context->getNext(data)); + EXPECT_EQ("A", data[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ("3600", data[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ("192.0.2.1", data[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ("www.example.org.", data[DatabaseAccessor::NAME_COLUMN]); + + // Check there's no other + EXPECT_FALSE(context->getNext(data)); } TEST(SQLite3Open, getDBNameExample2) { @@ -161,11 +212,11 @@ checkRecordRow(const std::string columns[], const std::string& field3, const std::string& field4) { - EXPECT_EQ(field0, columns[0]); - EXPECT_EQ(field1, columns[1]); - EXPECT_EQ(field2, columns[2]); - EXPECT_EQ(field3, columns[3]); - EXPECT_EQ(field4, columns[4]); + EXPECT_EQ(field0, columns[DatabaseAccessor::TYPE_COLUMN]); + EXPECT_EQ(field1, columns[DatabaseAccessor::TTL_COLUMN]); + EXPECT_EQ(field2, columns[DatabaseAccessor::SIGTYPE_COLUMN]); + EXPECT_EQ(field3, columns[DatabaseAccessor::RDATA_COLUMN]); + EXPECT_EQ(field4, columns[DatabaseAccessor::NAME_COLUMN]); } TEST_F(SQLite3Access, getRecords) { @@ -175,8 +226,7 @@ TEST_F(SQLite3Access, getRecords) { const int zone_id = zone_info.second; ASSERT_EQ(1, zone_id); - const size_t column_count = DatabaseAccessor::COLUMN_COUNT; - std::string columns[column_count]; + std::string columns[DatabaseAccessor::COLUMN_COUNT]; DatabaseAccessor::IteratorContextPtr context(db->getRecords("foo.bar", 1)); From a9b769b8bf12e2922e385c62ce337fb723731699 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 19 Aug 2011 17:36:01 +0200 Subject: [PATCH 610/974] [326] do table creation in an exclusive transaction (python) --- src/lib/python/isc/datasrc/sqlite3_ds.py | 78 ++++++++++++------- .../isc/datasrc/tests/sqlite3_ds_test.py | 39 +++++++++- 2 files changed, 87 insertions(+), 30 deletions(-) diff --git a/src/lib/python/isc/datasrc/sqlite3_ds.py b/src/lib/python/isc/datasrc/sqlite3_ds.py index a77645a11f..225b1b64ee 100644 --- a/src/lib/python/isc/datasrc/sqlite3_ds.py +++ b/src/lib/python/isc/datasrc/sqlite3_ds.py @@ -33,31 +33,48 @@ def create(cur): Arguments: cur - sqlite3 cursor. """ - cur.execute("CREATE TABLE schema_version (version INTEGER NOT NULL)") - cur.execute("INSERT INTO schema_version VALUES (1)") - cur.execute("""CREATE TABLE zones (id INTEGER PRIMARY KEY, - name STRING NOT NULL COLLATE NOCASE, - rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN', - dnssec BOOLEAN NOT NULL DEFAULT 0)""") - cur.execute("CREATE INDEX zones_byname ON zones (name)") - cur.execute("""CREATE TABLE records (id INTEGER PRIMARY KEY, - zone_id INTEGER NOT NULL, - name STRING NOT NULL COLLATE NOCASE, - rname STRING NOT NULL COLLATE NOCASE, - ttl INTEGER NOT NULL, - rdtype STRING NOT NULL COLLATE NOCASE, - sigtype STRING COLLATE NOCASE, - rdata STRING NOT NULL)""") - cur.execute("CREATE INDEX records_byname ON records (name)") - cur.execute("CREATE INDEX records_byrname ON records (rname)") - cur.execute("""CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, - zone_id INTEGER NOT NULL, - hash STRING NOT NULL COLLATE NOCASE, - owner STRING NOT NULL COLLATE NOCASE, - ttl INTEGER NOT NULL, - rdtype STRING NOT NULL COLLATE NOCASE, - rdata STRING NOT NULL)""") - cur.execute("CREATE INDEX nsec3_byhash ON nsec3 (hash)") + # We are creating the database because it apparently had not been at + # the time we tried to read from it. However, another process may have + # had the same idea, resulting in a potential race condition. + # Therefore, we obtain an exclusive lock before we create anything + # When we have it, we check *again* whether the database has been + # initialized. If not, we do so. + + # If the database is perpetually locked, it'll time out automatically + # and we just let it fail. + cur.execute("BEGIN EXCLUSIVE TRANSACTION") + try: + cur.execute("SELECT version FROM schema_version") + row = cur.fetchone() + except sqlite3.OperationalError: + cur.execute("CREATE TABLE schema_version (version INTEGER NOT NULL)") + cur.execute("INSERT INTO schema_version VALUES (1)") + cur.execute("""CREATE TABLE zones (id INTEGER PRIMARY KEY, + name STRING NOT NULL COLLATE NOCASE, + rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN', + dnssec BOOLEAN NOT NULL DEFAULT 0)""") + cur.execute("CREATE INDEX zones_byname ON zones (name)") + cur.execute("""CREATE TABLE records (id INTEGER PRIMARY KEY, + zone_id INTEGER NOT NULL, + name STRING NOT NULL COLLATE NOCASE, + rname STRING NOT NULL COLLATE NOCASE, + ttl INTEGER NOT NULL, + rdtype STRING NOT NULL COLLATE NOCASE, + sigtype STRING COLLATE NOCASE, + rdata STRING NOT NULL)""") + cur.execute("CREATE INDEX records_byname ON records (name)") + cur.execute("CREATE INDEX records_byrname ON records (rname)") + cur.execute("""CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, + zone_id INTEGER NOT NULL, + hash STRING NOT NULL COLLATE NOCASE, + owner STRING NOT NULL COLLATE NOCASE, + ttl INTEGER NOT NULL, + rdtype STRING NOT NULL COLLATE NOCASE, + rdata STRING NOT NULL)""") + cur.execute("CREATE INDEX nsec3_byhash ON nsec3 (hash)") + row = [1] + cur.execute("COMMIT TRANSACTION") + return row def open(dbfile): """ Open a database, if the database is not yet set up, call create @@ -80,10 +97,13 @@ def open(dbfile): try: cur.execute("SELECT version FROM schema_version") row = cur.fetchone() - except: - create(cur) - conn.commit() - row = [1] + except sqlite3.OperationalError: + # temporarily disable automatic transactions so + # we can do our own + iso_lvl = conn.isolation_level + conn.isolation_level = None + row = create(cur) + conn.isolation_level = iso_lvl if row == None or row[0] != 1: raise Sqlite3DSError("Bad database schema version") diff --git a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py index 707994f6f0..0199c33fa6 100644 --- a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py +++ b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py @@ -23,8 +23,9 @@ TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep READ_ZONE_DB_FILE = TESTDATA_PATH + "example.com.sqlite3" -WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "example.com.out.sqlite3" BROKEN_DB_FILE = TESTDATA_PATH + "brokendb.sqlite3" +WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "example.com.out.sqlite3" +NEW_DB_FILE = TESTDATA_WRITE_PATH + "new_db.sqlite3" def example_reader(): my_zone = [ @@ -91,5 +92,41 @@ class TestSqlite3_ds(unittest.TestCase): # and make sure lock does not stay sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader) +class NewDBFile(unittest.TestCase): + def tearDown(self): + # remove the created database after every test + if (os.path.exists(NEW_DB_FILE)): + os.remove(NEW_DB_FILE) + + def setUp(self): + # remove the created database before every test too, just + # in case a test got aborted half-way, and cleanup didn't occur + if (os.path.exists(NEW_DB_FILE)): + os.remove(NEW_DB_FILE) + + def test_new_db(self): + self.assertFalse(os.path.exists(NEW_DB_FILE)) + sqlite3_ds.load(NEW_DB_FILE, ".", example_reader) + self.assertTrue(os.path.exists(NEW_DB_FILE)) + + def test_new_db_locked(self): + self.assertFalse(os.path.exists(NEW_DB_FILE)) + con = sqlite3.connect(NEW_DB_FILE); + cur = con.cursor() + con.isolation_level = None + cur.execute("BEGIN EXCLUSIVE TRANSACTION") + + # load should now fail, since the database is locked + self.assertRaises(sqlite3.OperationalError, + sqlite3_ds.load, NEW_DB_FILE, ".", example_reader) + + con.rollback() + cur.close() + con.close() + self.assertTrue(os.path.exists(NEW_DB_FILE)) + + # now that we closed our connection, load should work again + sqlite3_ds.load(NEW_DB_FILE, ".", example_reader) + if __name__ == '__main__': unittest.main() From a176724d99c073f8e547dea2675a5b7d1df70515 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 19 Aug 2011 17:54:47 +0200 Subject: [PATCH 611/974] [326] improve test, and add optional shorter timeout to open() --- src/lib/python/isc/datasrc/sqlite3_ds.py | 6 +++-- .../isc/datasrc/tests/sqlite3_ds_test.py | 23 ++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/lib/python/isc/datasrc/sqlite3_ds.py b/src/lib/python/isc/datasrc/sqlite3_ds.py index 225b1b64ee..fd63741ef2 100644 --- a/src/lib/python/isc/datasrc/sqlite3_ds.py +++ b/src/lib/python/isc/datasrc/sqlite3_ds.py @@ -76,18 +76,20 @@ def create(cur): cur.execute("COMMIT TRANSACTION") return row -def open(dbfile): +def open(dbfile, connect_timeout=5.0): """ Open a database, if the database is not yet set up, call create to do so. It may raise Sqlite3DSError if failed to open sqlite3 database file or find bad database schema version in the database. Arguments: dbfile - the filename for the sqlite3 database. + connect_timeout - timeout for opening the database or acquiring locks + defaults to sqlite3 module's default of 5.0 seconds Return sqlite3 connection, sqlite3 cursor. """ try: - conn = sqlite3.connect(dbfile) + conn = sqlite3.connect(dbfile, timeout=connect_timeout) cur = conn.cursor() except Exception as e: fail = "Failed to open " + dbfile + ": " + e.args[0] diff --git a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py index 0199c33fa6..10c61cf2a1 100644 --- a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py +++ b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py @@ -106,19 +106,20 @@ class NewDBFile(unittest.TestCase): def test_new_db(self): self.assertFalse(os.path.exists(NEW_DB_FILE)) - sqlite3_ds.load(NEW_DB_FILE, ".", example_reader) + sqlite3_ds.open(NEW_DB_FILE) self.assertTrue(os.path.exists(NEW_DB_FILE)) def test_new_db_locked(self): self.assertFalse(os.path.exists(NEW_DB_FILE)) con = sqlite3.connect(NEW_DB_FILE); - cur = con.cursor() con.isolation_level = None - cur.execute("BEGIN EXCLUSIVE TRANSACTION") + cur = con.cursor() + cur.execute("BEGIN IMMEDIATE TRANSACTION") - # load should now fail, since the database is locked + # load should now fail, since the database is locked, + # and the open() call needs an exclusive lock self.assertRaises(sqlite3.OperationalError, - sqlite3_ds.load, NEW_DB_FILE, ".", example_reader) + sqlite3_ds.open, NEW_DB_FILE, 0.1) con.rollback() cur.close() @@ -126,7 +127,17 @@ class NewDBFile(unittest.TestCase): self.assertTrue(os.path.exists(NEW_DB_FILE)) # now that we closed our connection, load should work again - sqlite3_ds.load(NEW_DB_FILE, ".", example_reader) + sqlite3_ds.open(NEW_DB_FILE) + + # the database should now have been created, and a new load should + # not require an exclusive lock anymore, so we lock it again + con = sqlite3.connect(NEW_DB_FILE); + cur = con.cursor() + cur.execute("BEGIN IMMEDIATE TRANSACTION") + sqlite3_ds.open(NEW_DB_FILE, 0.1) + con.rollback() + cur.close() + con.close() if __name__ == '__main__': unittest.main() From 08b5add9a6e405342c0c8bc3bdf5d552ed45df0e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 19 Aug 2011 18:01:21 +0200 Subject: [PATCH 612/974] [326] initial cpp version --- src/lib/datasrc/sqlite3_datasrc.cc | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc index 18ee929593..dbe792f625 100644 --- a/src/lib/datasrc/sqlite3_datasrc.cc +++ b/src/lib/datasrc/sqlite3_datasrc.cc @@ -668,13 +668,26 @@ checkAndSetupSchema(Sqlite3Initializer* initializer) { if (prepared != NULL) { sqlite3_finalize(prepared); } - for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) { - if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) != - SQLITE_OK) { - isc_throw(Sqlite3Error, - "Failed to set up schema " << SCHEMA_LIST[i]); + // We need to create the database. However, there is a potential + // race condition with another process that wants to do the same + // So we acquire an exclusive lock, and then check whether the + // tables have been created again. If not, we create them. + sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, NULL); + if (sqlite3_prepare_v2(db, "SELECT version FROM schema_version", -1, + &prepared, NULL) == SQLITE_OK && + sqlite3_step(prepared) == SQLITE_ROW) { + initializer->params_.version_ = sqlite3_column_int(prepared, 0); + sqlite3_finalize(prepared); + } else { + for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) { + if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) != + SQLITE_OK) { + isc_throw(Sqlite3Error, + "Failed to set up schema " << SCHEMA_LIST[i]); + } } } + sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL); } initializer->params_.q_zone_ = prepare(db, q_zone_str); From 5c6391cca55baec236b813b4c2e2b7699595559d Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Fri, 19 Aug 2011 15:22:56 -0500 Subject: [PATCH 613/974] [master] add naptr_35 files to the distribution Noticed on the distcheck builder. I didn't test the distcheck, just the dist target. --- src/lib/dns/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 4a2364190b..61f80b822c 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -31,6 +31,8 @@ EXTRA_DIST += rdata/generic/ds_43.cc EXTRA_DIST += rdata/generic/ds_43.h EXTRA_DIST += rdata/generic/mx_15.cc EXTRA_DIST += rdata/generic/mx_15.h +EXTRA_DIST += rdata/generic/naptr_35.cc +EXTRA_DIST += rdata/generic/naptr_35.h EXTRA_DIST += rdata/generic/ns_2.cc EXTRA_DIST += rdata/generic/ns_2.h EXTRA_DIST += rdata/generic/nsec3_50.cc From 28988a78d3b80c7f1080fce696acf176b74a29fe Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 22 Aug 2011 10:38:47 +0200 Subject: [PATCH 614/974] [1153] Small editorial fixes --- src/bin/zonemgr/tests/zonemgr_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py index 67893c8f7b..4fe468f9ae 100644 --- a/src/bin/zonemgr/tests/zonemgr_test.py +++ b/src/bin/zonemgr/tests/zonemgr_test.py @@ -495,7 +495,7 @@ class TestZonemgrRefresh(unittest.TestCase): name_class = ("doesnotexist.", "IN") self.assertTrue(self.zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"] is None) - # The other configs should be updated successful + # The other configs should be updated successfully self.assertEqual(61, self.zone_refresh._lowerbound_refresh) self.assertEqual(31, self.zone_refresh._lowerbound_retry) self.assertEqual(19801, self.zone_refresh._max_transfer_timeout) @@ -617,8 +617,8 @@ class TestZonemgr(unittest.TestCase): config_data1["secondary_zones"] = [{"name": "nonexistent.example", "class": "IN"}] self.assertEqual(self.zonemgr.config_handler(config_data1), - {"result": [0]}) - # other configs should be updated successful + {"result": [0]}) + # other configs should be updated successfully name_class = ("nonexistent.example.", "IN") self.assertTrue(self.zonemgr._zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"] is None) From 9d48d1964569b49be17afc3e20085a23544a32de Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 22 Aug 2011 11:39:00 +0200 Subject: [PATCH 615/974] [1183] make sure getNext() when done does not error --- src/lib/datasrc/database.h | 3 +++ src/lib/datasrc/sqlite3_accessor.cc | 5 +++++ src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 95c1c5a8a4..d7785e6f14 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -148,6 +148,9 @@ public: * \throw DataSourceError if there's database-related error. If the * exception (or any other in case of derived class) is thrown, * the iterator can't be safely used any more. + * \return true if a record was found, and the columns array was + * updated. false if there was no more data, in which case + * the columns array is untouched. */ virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) = 0; }; diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 86487aeda3..d178f0a91b 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -369,6 +369,11 @@ public: bool getNext(std::string (&data)[COLUMN_COUNT]) { // If there's another row, get it + // If finalize has been called (e.g. when previous getNext() got + // SQLITE_DONE), directly return false + if (statement_ == NULL) { + return false; + } const int rc(sqlite3_step(statement_)); if (rc == SQLITE_ROW) { // For both types, we copy the first four columns diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 295bed27c6..5f7abaf9a3 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -190,6 +190,9 @@ TEST_F(SQLite3Access, iterator) { // Check there's no other EXPECT_FALSE(context->getNext(data)); + + // And make sure calling it again won't cause problems. + EXPECT_FALSE(context->getNext(data)); } TEST(SQLite3Open, getDBNameExample2) { @@ -321,6 +324,9 @@ TEST_F(SQLite3Access, getRecords) { checkRecordRow(columns, "RRSIG", "3600", "DNSKEY", "DNSKEY 5 2 3600 20100322084538 20100220084538 " "33495 example.com. FAKEFAKEFAKEFAKE", ""); + + // check that another getNext does not cause problems + EXPECT_FALSE(context->getNext(columns)); } } // end anonymous namespace From af6328603521584ff62b25a6f86a923bba5a4f5e Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Mon, 22 Aug 2011 19:03:38 +0800 Subject: [PATCH 616/974] [trac1153] consider missing zones as expired --- src/bin/zonemgr/tests/zonemgr_test.py | 9 +++++ src/bin/zonemgr/zonemgr.py.in | 51 ++++++++++++++------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py index 4fe468f9ae..80e41b3194 100644 --- a/src/bin/zonemgr/tests/zonemgr_test.py +++ b/src/bin/zonemgr/tests/zonemgr_test.py @@ -372,6 +372,15 @@ class TestZonemgrRefresh(unittest.TestCase): self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_CH) self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_IN) + old_get_zone_soa = sqlite3_ds.get_zone_soa + def get_zone_soa(zone_name, db_file): + return None + sqlite3_ds.get_zone_soa = get_zone_soa + self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN) + self.assertEqual(self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"], + ZONE_EXPIRED) + sqlite3_ds.get_zone_soa = old_get_zone_soa + def test_find_need_do_refresh_zone(self): time1 = time.time() self.zone_refresh._zonemgr_refresh_info = { diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index dff2e680f3..d4de6a8d14 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -177,7 +177,7 @@ class ZonemgrRefresh: raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't " "belong to zonemgr" % zone_name_class) # Is zone expired? - if ((self._get_zone_soa_rdata(zone_name_class) is not None) and + if ((self._get_zone_soa_rdata(zone_name_class) is None) or self._zone_is_expired(zone_name_class)): self._set_zone_state(zone_name_class, ZONE_EXPIRED) else: @@ -450,29 +450,32 @@ class ZonemgrRefresh: self._reload_jitter = val_or_default( new_config.get('reload_jitter'), self._reload_jitter) - required = {} - secondary_zones = new_config.get('secondary_zones') - if secondary_zones is not None: - # Add new zones - for secondary_zone in new_config.get('secondary_zones'): - name = secondary_zone['name'] - # Be tolerant to sclerotic users who forget the final dot - if name[-1] != '.': - name = name + '.' - name_class = (name, secondary_zone['class']) - required[name_class] = True - # Add it only if it isn't there already - if not name_class in self._zonemgr_refresh_info: - # If we are not able to find it in database, log an warning - self.zonemgr_add_zone(name_class) - # Drop the zones that are no longer there - # Do it in two phases, python doesn't like deleting while iterating - to_drop = [] - for old_zone in self._zonemgr_refresh_info: - if not old_zone in required: - to_drop.append(old_zone) - for drop in to_drop: - del self._zonemgr_refresh_info[drop] + try: + required = {} + secondary_zones = new_config.get('secondary_zones') + if secondary_zones is not None: + # Add new zones + for secondary_zone in new_config.get('secondary_zones'): + name = secondary_zone['name'] + # Be tolerant to sclerotic users who forget the final dot + if name[-1] != '.': + name = name + '.' + name_class = (name, secondary_zone['class']) + required[name_class] = True + # Add it only if it isn't there already + if not name_class in self._zonemgr_refresh_info: + # If we are not able to find it in database, log an warning + self.zonemgr_add_zone(name_class) + # Drop the zones that are no longer there + # Do it in two phases, python doesn't like deleting while iterating + to_drop = [] + for old_zone in self._zonemgr_refresh_info: + if not old_zone in required: + to_drop.append(old_zone) + for drop in to_drop: + del self._zonemgr_refresh_info[drop] + except: + raise class Zonemgr: """Zone manager class.""" From 7f08fc3123ef7d26a2e61dd29455c07510404a7e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 22 Aug 2011 13:11:27 +0200 Subject: [PATCH 617/974] [1183] remove unused member, and fix a typo in comment --- src/lib/datasrc/sqlite3_accessor.cc | 10 +--------- src/lib/datasrc/tests/database_unittest.cc | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index d178f0a91b..e604cf9af0 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -27,7 +27,7 @@ namespace datasrc { struct SQLite3Parameters { SQLite3Parameters() : db_(NULL), version_(-1), - q_zone_(NULL), q_any_(NULL) + q_zone_(NULL) /*q_record_(NULL), q_addrs_(NULL), q_referral_(NULL), q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL), q_prevnsec3_(NULL) */ @@ -35,7 +35,6 @@ struct SQLite3Parameters { sqlite3* db_; int version_; sqlite3_stmt* q_zone_; - sqlite3_stmt* q_any_; /* TODO: Yet unneeded statements sqlite3_stmt* q_record_; @@ -75,9 +74,6 @@ public: if (params_.q_zone_ != NULL) { sqlite3_finalize(params_.q_zone_); } - if (params_.q_any_ != NULL) { - sqlite3_finalize(params_.q_any_); - } /* if (params_.q_record_ != NULL) { sqlite3_finalize(params_.q_record_); @@ -214,7 +210,6 @@ checkAndSetupSchema(Initializer* initializer) { } initializer->params_.q_zone_ = prepare(db, q_zone_str); - initializer->params_.q_any_ = prepare(db, q_any_str); /* TODO: Yet unneeded statements initializer->params_.q_record_ = prepare(db, q_record_str); initializer->params_.q_addrs_ = prepare(db, q_addrs_str); @@ -265,9 +260,6 @@ SQLite3Database::close(void) { sqlite3_finalize(dbparameters_->q_zone_); dbparameters_->q_zone_ = NULL; - sqlite3_finalize(dbparameters_->q_any_); - dbparameters_->q_any_ = NULL; - /* TODO: Once they are needed or not, uncomment or drop sqlite3_finalize(dbparameters->q_record_); dbparameters->q_record_ = NULL; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 2153f015b2..8ff8c5518a 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -97,7 +97,7 @@ private: searched_name_(name), cur_record_(0) { // 'hardcoded' names to trigger exceptions - // On these names some exceptions are throws, to test the robustness + // On these names some exceptions are thrown, to test the robustness // of the find() method. if (searched_name_ == "dsexception.in.search.") { isc_throw(DataSourceError, "datasource exception on search"); From 4579a2a9f43a38144539447bb5076bfcbaf8b6d8 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 22 Aug 2011 15:09:25 +0200 Subject: [PATCH 618/974] [326] cpp version for exclusive locking at creation time --- src/lib/datasrc/sqlite3_datasrc.cc | 118 +++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 33 deletions(-) diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc index dbe792f625..577718fa5d 100644 --- a/src/lib/datasrc/sqlite3_datasrc.cc +++ b/src/lib/datasrc/sqlite3_datasrc.cc @@ -26,6 +26,8 @@ #include #include +#define SQLITE_SCHEMA_VERSION 1 + using namespace std; using namespace isc::dns; using namespace isc::dns::rdata; @@ -77,6 +79,8 @@ const char* const SCHEMA_LIST[] = { NULL }; +const char* const q_version_str = "SELECT version FROM schema_version"; + const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1"; const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata " @@ -254,7 +258,7 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype, } break; } - + sqlite3_reset(query); sqlite3_clear_bindings(query); @@ -295,7 +299,7 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype, // sqlite3_reset(dbparameters->q_count_); sqlite3_clear_bindings(dbparameters->q_count_); - + rc = sqlite3_bind_int(dbparameters->q_count_, 1, zone_id); if (rc != SQLITE_OK) { isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id << @@ -653,42 +657,90 @@ prepare(sqlite3* const db, const char* const statement) { return (prepared); } +// small function to sleep for 0.1 seconds, needed when waiting for +// exclusive database locks (which should only occur on startup, and only +// when the database has not been created yet) +void do_sleep() { + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = 100000000; + nanosleep(&req, NULL); +} + +// returns the schema version if the schema version table exists +// returns -1 if it does not +int check_schema_version(sqlite3* db) { + sqlite3_stmt* prepared = NULL; + // At this point in time, the database might be exclusively locked, in + // which case even prepare() will return BUSY, so we may need to try a + // few times + for (size_t i = 0; i < 50; ++i) { + int rc = sqlite3_prepare_v2(db, q_version_str, -1, &prepared, NULL); + if (rc == SQLITE_ERROR) { + // this is the error that is returned when the table does not + // exist + return -1; + } else if (rc == SQLITE_OK) { + break; + } else if (rc != SQLITE_BUSY || i == 50) { + isc_throw(Sqlite3Error, "Unable to prepare version query: " + << rc << " " << sqlite3_errmsg(db)); + } + do_sleep(); + } + if (sqlite3_step(prepared) != SQLITE_ROW) { + isc_throw(Sqlite3Error, + "Unable to query version: " << sqlite3_errmsg(db)); + } + int version = sqlite3_column_int(prepared, 0); + sqlite3_finalize(prepared); + return version; +} + +// return db version +int create_database(sqlite3* db) { + // try to get an exclusive lock. Once that is obtained, do the version + // check *again*, just in case this process was racing another + // + // try for 5 secs (50*0.1) + int rc; + logger.info(DATASRC_SQLITE_SETUP); + for (size_t i = 0; i < 50; ++i) { + rc = sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, + NULL); + if (rc == SQLITE_OK) { + break; + } else if (rc != SQLITE_BUSY || i == 50) { + isc_throw(Sqlite3Error, "Unable to acquire exclusive lock " + "for database creation: " << sqlite3_errmsg(db)); + } + do_sleep(); + } + int schema_version = check_schema_version(db); + if (schema_version == -1) { + for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) { + if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) != + SQLITE_OK) { + isc_throw(Sqlite3Error, + "Failed to set up schema " << SCHEMA_LIST[i]); + } + } + sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL); + return SQLITE_SCHEMA_VERSION; + } else { + return schema_version; + } +} + void checkAndSetupSchema(Sqlite3Initializer* initializer) { sqlite3* const db = initializer->params_.db_; - sqlite3_stmt* prepared = NULL; - if (sqlite3_prepare_v2(db, "SELECT version FROM schema_version", -1, - &prepared, NULL) == SQLITE_OK && - sqlite3_step(prepared) == SQLITE_ROW) { - initializer->params_.version_ = sqlite3_column_int(prepared, 0); - sqlite3_finalize(prepared); - } else { - logger.info(DATASRC_SQLITE_SETUP); - if (prepared != NULL) { - sqlite3_finalize(prepared); - } - // We need to create the database. However, there is a potential - // race condition with another process that wants to do the same - // So we acquire an exclusive lock, and then check whether the - // tables have been created again. If not, we create them. - sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, NULL); - if (sqlite3_prepare_v2(db, "SELECT version FROM schema_version", -1, - &prepared, NULL) == SQLITE_OK && - sqlite3_step(prepared) == SQLITE_ROW) { - initializer->params_.version_ = sqlite3_column_int(prepared, 0); - sqlite3_finalize(prepared); - } else { - for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) { - if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) != - SQLITE_OK) { - isc_throw(Sqlite3Error, - "Failed to set up schema " << SCHEMA_LIST[i]); - } - } - } - sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL); + int schema_version = check_schema_version(db); + if (schema_version != SQLITE_SCHEMA_VERSION) { + schema_version = create_database(db); } + initializer->params_.version_ = schema_version; initializer->params_.q_zone_ = prepare(db, q_zone_str); initializer->params_.q_record_ = prepare(db, q_record_str); From 38d1a8aa943424e1a0de0503ee8aa961a95d0e14 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 22 Aug 2011 15:40:56 +0200 Subject: [PATCH 619/974] [326] tests and implementation in refactored code --- src/lib/datasrc/sqlite3_accessor.cc | 91 ++++++++++++++++--- src/lib/datasrc/sqlite3_datasrc.cc | 8 +- src/lib/datasrc/tests/Makefile.am | 1 + .../tests/sqlite3_accessor_unittest.cc | 65 +++++++++++++ 4 files changed, 148 insertions(+), 17 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index bab7d2966a..229a7368aa 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -21,6 +21,8 @@ #include +#define SQLITE_SCHEMA_VERSION 1 + namespace isc { namespace datasrc { @@ -136,6 +138,8 @@ const char* const SCHEMA_LIST[] = { NULL }; +const char* const q_version_str = "SELECT version FROM schema_version"; + const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"; const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata, name " @@ -185,29 +189,90 @@ prepare(sqlite3* const db, const char* const statement) { return (prepared); } -void -checkAndSetupSchema(Initializer* initializer) { - sqlite3* const db = initializer->params_.db_; +// small function to sleep for 0.1 seconds, needed when waiting for +// exclusive database locks (which should only occur on startup, and only +// when the database has not been created yet) +void do_sleep() { + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = 100000000; + nanosleep(&req, NULL); +} +// returns the schema version if the schema version table exists +// returns -1 if it does not +int check_schema_version(sqlite3* db) { sqlite3_stmt* prepared = NULL; - if (sqlite3_prepare_v2(db, "SELECT version FROM schema_version", -1, - &prepared, NULL) == SQLITE_OK && - sqlite3_step(prepared) == SQLITE_ROW) { - initializer->params_.version_ = sqlite3_column_int(prepared, 0); - sqlite3_finalize(prepared); - } else { - logger.info(DATASRC_SQLITE_SETUP); - if (prepared != NULL) { - sqlite3_finalize(prepared); + // At this point in time, the database might be exclusively locked, in + // which case even prepare() will return BUSY, so we may need to try a + // few times + for (size_t i = 0; i < 50; ++i) { + int rc = sqlite3_prepare_v2(db, q_version_str, -1, &prepared, NULL); + if (rc == SQLITE_ERROR) { + // this is the error that is returned when the table does not + // exist + return (-1); + } else if (rc == SQLITE_OK) { + break; + } else if (rc != SQLITE_BUSY || i == 50) { + isc_throw(SQLite3Error, "Unable to prepare version query: " + << rc << " " << sqlite3_errmsg(db)); } + do_sleep(); + } + if (sqlite3_step(prepared) != SQLITE_ROW) { + isc_throw(SQLite3Error, + "Unable to query version: " << sqlite3_errmsg(db)); + } + int version = sqlite3_column_int(prepared, 0); + sqlite3_finalize(prepared); + return (version); +} + +// return db version +int create_database(sqlite3* db) { + // try to get an exclusive lock. Once that is obtained, do the version + // check *again*, just in case this process was racing another + // + // try for 5 secs (50*0.1) + int rc; + logger.info(DATASRC_SQLITE_SETUP); + for (size_t i = 0; i < 50; ++i) { + rc = sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, + NULL); + if (rc == SQLITE_OK) { + break; + } else if (rc != SQLITE_BUSY || i == 50) { + isc_throw(SQLite3Error, "Unable to acquire exclusive lock " + "for database creation: " << sqlite3_errmsg(db)); + } + do_sleep(); + } + int schema_version = check_schema_version(db); + if (schema_version == -1) { for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) { if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) != SQLITE_OK) { isc_throw(SQLite3Error, - "Failed to set up schema " << SCHEMA_LIST[i]); + "Failed to set up schema " << SCHEMA_LIST[i]); } } + sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL); + return (SQLITE_SCHEMA_VERSION); + } else { + return (schema_version); } +} + +void +checkAndSetupSchema(Initializer* initializer) { + sqlite3* const db = initializer->params_.db_; + + int schema_version = check_schema_version(db); + if (schema_version != SQLITE_SCHEMA_VERSION) { + schema_version = create_database(db); + } + initializer->params_.version_ = schema_version; initializer->params_.q_zone_ = prepare(db, q_zone_str); initializer->params_.q_any_ = prepare(db, q_any_str); diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc index 577718fa5d..03b057cd49 100644 --- a/src/lib/datasrc/sqlite3_datasrc.cc +++ b/src/lib/datasrc/sqlite3_datasrc.cc @@ -679,7 +679,7 @@ int check_schema_version(sqlite3* db) { if (rc == SQLITE_ERROR) { // this is the error that is returned when the table does not // exist - return -1; + return (-1); } else if (rc == SQLITE_OK) { break; } else if (rc != SQLITE_BUSY || i == 50) { @@ -694,7 +694,7 @@ int check_schema_version(sqlite3* db) { } int version = sqlite3_column_int(prepared, 0); sqlite3_finalize(prepared); - return version; + return (version); } // return db version @@ -726,9 +726,9 @@ int create_database(sqlite3* db) { } } sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL); - return SQLITE_SCHEMA_VERSION; + return (SQLITE_SCHEMA_VERSION); } else { - return schema_version; + return (schema_version); } } diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index a913818930..eb0e3ad5b2 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -3,6 +3,7 @@ AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CPPFLAGS += $(SQLITE_CFLAGS) AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\" +AM_CPPFLAGS += -DTEST_DATA_BUILD_DIR=\"$(builddir)/testdata\" AM_CXXFLAGS = $(B10_CXXFLAGS) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index a6314498f7..92b8558eb6 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -19,6 +19,8 @@ #include #include +#include +#include using namespace isc::datasrc; using isc::data::ConstElementPtr; @@ -42,6 +44,10 @@ std::string SQLITE_DBFILE_MEMORY = ":memory:"; // The "nodir", a non existent directory, is inserted for this purpose. std::string SQLITE_DBFILE_NOTEXIST = TEST_DATA_DIR "/nodir/notexist"; +// new db file, we don't need this to be a std::string, and given the +// raw calls we use it in a const char* is more convenient +const char* SQLITE_NEW_DBFILE = TEST_DATA_BUILD_DIR "/newdb.sqlite3"; + // Opening works (the content is tested in different tests) TEST(SQLite3Open, common) { EXPECT_NO_THROW(SQLite3Database db(SQLITE_DBFILE_EXAMPLE, @@ -291,4 +297,63 @@ TEST_F(SQLite3Access, getRecords) { "33495 example.com. FAKEFAKEFAKEFAKE", "example.com."); } +// Test fixture for creating a db that automatically deletes it before start, +// and when done +class SQLite3Create : public ::testing::Test { +public: + SQLite3Create() { + remove(SQLITE_NEW_DBFILE); + } + + ~SQLite3Create() { + remove(SQLITE_NEW_DBFILE); + } +}; + +bool exists(const char* filename) { + std::ifstream f(filename); + return (f != NULL); +} + +TEST_F(SQLite3Create, creationtest) { + ASSERT_FALSE(exists(SQLITE_NEW_DBFILE)); + // Should simply be created + SQLite3Database db(SQLITE_NEW_DBFILE, RRClass::IN()); + ASSERT_TRUE(exists(SQLITE_NEW_DBFILE)); +} + +TEST_F(SQLite3Create, emptytest) { + ASSERT_FALSE(exists(SQLITE_NEW_DBFILE)); + + // open one manualle + sqlite3* db; + ASSERT_EQ(SQLITE_OK, sqlite3_open(SQLITE_NEW_DBFILE, &db)); + + // empty, but not locked, so creating it now should work + SQLite3Database db2(SQLITE_NEW_DBFILE, RRClass::IN()); + + sqlite3_close(db); + + // should work now that we closed it + SQLite3Database db3(SQLITE_NEW_DBFILE, RRClass::IN()); +} + +TEST_F(SQLite3Create, lockedtest) { + ASSERT_FALSE(exists(SQLITE_NEW_DBFILE)); + + // open one manually + sqlite3* db; + ASSERT_EQ(SQLITE_OK, sqlite3_open(SQLITE_NEW_DBFILE, &db)); + sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, NULL); + + // should not be able to open it + EXPECT_THROW(SQLite3Database db2(SQLITE_NEW_DBFILE, RRClass::IN()), + SQLite3Error); + + sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); + + // should work now that we closed it + SQLite3Database db3(SQLITE_NEW_DBFILE, RRClass::IN()); +} + } // end anonymous namespace From 586d49827ebaa2cf2c70dc030c5830afb1fb89f5 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Mon, 22 Aug 2011 15:32:57 -0500 Subject: [PATCH 620/974] [master] clarify CACHE_RRSET_NOT_FOUND short description mention "in cache" (similar to related log messages) (yes, the ID already mentions it) okayed on jabber --- src/lib/cache/cache_messages.mes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes index 7f593ec6e6..19102aec4a 100644 --- a/src/lib/cache/cache_messages.mes +++ b/src/lib/cache/cache_messages.mes @@ -131,7 +131,7 @@ class is being created. % CACHE_RRSET_LOOKUP looking up %1/%2/%3 in RRset cache Debug message. The resolver is trying to look up data in the RRset cache. -% CACHE_RRSET_NOT_FOUND no RRset found for %1/%2/%3 +% CACHE_RRSET_NOT_FOUND no RRset found for %1/%2/%3 in cache Debug message which can follow CACHE_RRSET_LOOKUP. This means the data is not in the cache. From 5157876271e945703ee699f07442ee1a72bba362 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Tue, 23 Aug 2011 15:33:00 +0800 Subject: [PATCH 621/974] [trac1112]Add basic functions and unit tests to HINFO rrdata --- src/lib/dns/rdata/generic/hinfo_13.cc | 66 +++++++++++++++++++++++ src/lib/dns/rdata/generic/hinfo_13.h | 49 +++++++++++++++++ src/lib/dns/tests/Makefile.am | 1 + src/lib/dns/tests/rdata_hinfo_unittest.cc | 44 +++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 src/lib/dns/rdata/generic/hinfo_13.cc create mode 100644 src/lib/dns/rdata/generic/hinfo_13.h create mode 100644 src/lib/dns/tests/rdata_hinfo_unittest.cc diff --git a/src/lib/dns/rdata/generic/hinfo_13.cc b/src/lib/dns/rdata/generic/hinfo_13.cc new file mode 100644 index 0000000000..232eda3215 --- /dev/null +++ b/src/lib/dns/rdata/generic/hinfo_13.cc @@ -0,0 +1,66 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +HINFO::HINFO(const string& type_str) { +} + +HINFO::HINFO(InputBuffer& buffer, size_t rdata_len) { +} + +HINFO::HINFO(const HINFO& source) { +} + +std::string +HINFO::toText() const { +} + +void +HINFO::toWire(OutputBuffer& buffer) const { +} + +void +HINFO::toWire(AbstractMessageRenderer& renderer) const { +} + +int +HINFO::compare(const Rdata& other) const { + // The compare method normally begins with this dynamic cast. + const HINFO& other_hinfo = dynamic_cast(other); + // ... +} + + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/hinfo_13.h b/src/lib/dns/rdata/generic/hinfo_13.h new file mode 100644 index 0000000000..3922da1912 --- /dev/null +++ b/src/lib/dns/rdata/generic/hinfo_13.h @@ -0,0 +1,49 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// BEGIN_HEADER_GUARD +#include + +#include + +#include +#include + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class HINFO : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// + /// Specialized methods + /// + +private: +}; + + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index 48bce8565f..d1a5025b58 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -46,6 +46,7 @@ run_unittests_SOURCES += rdata_srv_unittest.cc run_unittests_SOURCES += rdata_minfo_unittest.cc run_unittests_SOURCES += rdata_tsig_unittest.cc run_unittests_SOURCES += rdata_naptr_unittest.cc +run_unittests_SOURCES += rdata_hinfo_unittest.cc run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc run_unittests_SOURCES += question_unittest.cc run_unittests_SOURCES += rrparamregistry_unittest.cc diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc new file mode 100644 index 0000000000..96dfda6ff5 --- /dev/null +++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc @@ -0,0 +1,44 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using isc::UnitTestUtil; +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using namespace isc::dns::rdata::generic; + +namespace { +class Rdata_HINFO_Test : public RdataTest { +}; + +static const char *hinfo_str = "\"Pentium\" \"Linux\""; + +TEST_F(Rdata_HINFO_Test, createFromText) { + HINFO hinfo(hinfo_str); +} +} From 4ff5e524a7f79ad7f4513ebed3ca0990392263af Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 23 Aug 2011 13:54:20 +0200 Subject: [PATCH 622/974] [1066] add one test and typo in comment --- src/lib/datasrc/database.cc | 2 +- src/lib/datasrc/tests/database_unittest.cc | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index bafd6a9b8c..71a345b7ac 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -340,7 +340,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // It contains some RRs, so it exists. last_known = superdomain.getLabelCount(); // In case we are in GLUE_OK, we want to store the highest - // encounderet RRset. + // encountered RRset. if (glue_ok && !first_ns && i != remove_labels) { first_ns = getRRset(superdomain, NULL, false, false, true).second; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index a0efd50ff2..06d08fc577 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1037,6 +1037,14 @@ TEST_F(DatabaseClientTest, wildcard) { isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); + // Also make sure that the wildcard doesn't hurt the original data + // below the wildcard + expected_rdatas_.push_back("2001:db8::5"); + doFindTest(finder, isc::dns::Name("cancel.here.wild.example.org"), + isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), + isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, + expected_rdatas_, expected_sig_rdatas_); + expected_rdatas_.clear(); // How wildcard go together with delegation expected_rdatas_.push_back("ns.example.com."); From 872bd5756ba8b5daeeacedfcd4ec38bc50035ec8 Mon Sep 17 00:00:00 2001 From: zhanglikun Date: Tue, 23 Aug 2011 20:15:41 +0800 Subject: [PATCH 623/974] [master] Add one entry to ChangeLog. --- ChangeLog | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 43c915471e..f85fa0bf8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,14 @@ -282. [func] ocean - libdns++: Implement the NAPTR rrtype according to RFC2915, - RFC2168 and RFC3403. - (Trac #1130, git 01d8d0f13289ecdf9996d6d5d26ac0d43e30549c) +283. [bug] zhanglikun + Make stats and boss processes wait for answer messages from each + other in block mode to avoid orphan answer messages, add an internal + command "getstats" to boss process for getting statistics data from + boss. + (Trac #519, git 67d8e93028e014f644868fede3570abb28e5fb43) + +282. [func] ocean + libdns++: Implement the NAPTR rrtype according to RFC2915, + RFC2168 and RFC3403. + (Trac #1130, git 01d8d0f13289ecdf9996d6d5d26ac0d43e30549c) bind10-devel-20110819 released on August 19, 2011 From 1921e1297dfcb878b9417edefe4d87639c827948 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 23 Aug 2011 14:53:54 +0200 Subject: [PATCH 624/974] [1174] use std::string instead of Name for getZone() --- src/lib/datasrc/database.cc | 6 +++--- src/lib/datasrc/database.h | 5 +++-- src/lib/datasrc/sqlite3_accessor.cc | 6 +++--- src/lib/datasrc/sqlite3_accessor.h | 4 ++-- src/lib/datasrc/tests/database_unittest.cc | 10 +++++----- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 12 ++++++------ 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index f0ac19266d..9e5ddb143a 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -49,7 +49,7 @@ DatabaseClient::DatabaseClient(boost::shared_ptr DataSourceClient::FindResult DatabaseClient::findZone(const Name& name) const { - std::pair zone(database_->getZone(name)); + std::pair zone(database_->getZone(name.toText())); // Try exact first if (zone.first) { return (FindResult(result::SUCCESS, @@ -60,7 +60,7 @@ DatabaseClient::findZone(const Name& name) const { // Start from 1, as 0 is covered above for (size_t i(1); i < name.getLabelCount(); ++i) { isc::dns::Name superdomain(name.split(i)); - zone = database_->getZone(superdomain); + zone = database_->getZone(superdomain.toText()); if (zone.first) { return (FindResult(result::PARTIALMATCH, ZoneFinderPtr(new Finder(database_, @@ -472,7 +472,7 @@ private: ZoneIteratorPtr DatabaseClient::getIterator(const isc::dns::Name& name) const { // Get the zone - std::pair zone(database_->getZone(name)); + std::pair zone(database_->getZone(name.toText())); if (!zone.first) { // No such zone, can't continue isc_throw(DataSourceError, "Zone " + name.toText() + diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index d7785e6f14..b34b95e518 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -88,7 +88,8 @@ public: * It is not specified if and what implementation of this method may throw, * so code should expect anything. * - * \param name The name of the zone's apex to be looked up. + * \param name The (fully qualified) domain name of the zone's apex to be + * looked up. * \return The first part of the result indicates if a matching zone * was found. In case it was, the second part is internal zone ID. * This one will be passed to methods finding data in the zone. @@ -96,7 +97,7 @@ public: * be returned - the ID is only passed back to the database as * an opaque handle. */ - virtual std::pair getZone(const isc::dns::Name& name) const = 0; + virtual std::pair getZone(const std::string& name) const = 0; /** * \brief This holds the internal context of ZoneIterator for databases diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index e604cf9af0..f4c22d2174 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -288,14 +288,14 @@ SQLite3Database::close(void) { } std::pair -SQLite3Database::getZone(const isc::dns::Name& name) const { +SQLite3Database::getZone(const std::string& name) const { int rc; // Take the statement (simple SELECT id FROM zones WHERE...) // and prepare it (bind the parameters to it) sqlite3_reset(dbparameters_->q_zone_); - rc = sqlite3_bind_text(dbparameters_->q_zone_, 1, name.toText().c_str(), - -1, SQLITE_TRANSIENT); + rc = sqlite3_bind_text(dbparameters_->q_zone_, 1, name.c_str(), + -1, SQLITE_STATIC); if (rc != SQLITE_OK) { isc_throw(SQLite3Error, "Could not bind " << name << " to SQL statement (zone)"); diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 50b15e79b2..72cb67b35c 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -87,11 +87,11 @@ public: * * \exception SQLite3Error if something about the database is broken. * - * \param name The name of zone to look up + * \param name The (fully qualified) domain name of zone to look up * \return The pair contains if the lookup was successful in the first * element and the zone id in the second if it was. */ - virtual std::pair getZone(const isc::dns::Name& name) const; + virtual std::pair getZone(const std::string& name) const; /** \brief Look up all resource records for a name * diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 8ff8c5518a..e81229516e 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -44,14 +44,14 @@ public: NopAccessor() : database_name_("mock_database") { } - virtual std::pair getZone(const Name& name) const { - if (name == Name("example.org")) { + virtual std::pair getZone(const std::string& name) const { + if (name == "example.org.") { return (std::pair(true, 42)); - } else if (name == Name("null.example.org")) { + } else if (name == "null.example.org.") { return (std::pair(true, 13)); - } else if (name == Name("empty.example.org")) { + } else if (name == "empty.example.org.") { return (std::pair(true, 0)); - } else if (name == Name("bad.example.org")) { + } else if (name == "bad.example.org.") { return (std::pair(true, -1)); } else { return (std::pair(false, 0)); diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 5f7abaf9a3..c2ac81282c 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -83,25 +83,25 @@ public: // This zone exists in the data, so it should be found TEST_F(SQLite3Access, getZone) { - std::pair result(db->getZone(Name("example.com"))); + std::pair result(db->getZone("example.com.")); EXPECT_TRUE(result.first); EXPECT_EQ(1, result.second); } // But it should find only the zone, nothing below it TEST_F(SQLite3Access, subZone) { - EXPECT_FALSE(db->getZone(Name("sub.example.com")).first); + EXPECT_FALSE(db->getZone("sub.example.com.").first); } // This zone is not there at all TEST_F(SQLite3Access, noZone) { - EXPECT_FALSE(db->getZone(Name("example.org")).first); + EXPECT_FALSE(db->getZone("example.org.").first); } // This zone is there, but in different class TEST_F(SQLite3Access, noClass) { initAccessor(SQLITE_DBFILE_EXAMPLE, RRClass::CH()); - EXPECT_FALSE(db->getZone(Name("example.com")).first); + EXPECT_FALSE(db->getZone("example.com.").first); } // This tests the iterator context @@ -109,7 +109,7 @@ TEST_F(SQLite3Access, iterator) { // Our test zone is conveniently small, but not empty initAccessor(SQLITE_DBFILE_EXAMPLE_ORG, RRClass::IN()); - const std::pair zone_info(db->getZone(Name("example.org"))); + const std::pair zone_info(db->getZone("example.org.")); ASSERT_TRUE(zone_info.first); // Get the iterator context @@ -223,7 +223,7 @@ checkRecordRow(const std::string columns[], } TEST_F(SQLite3Access, getRecords) { - const std::pair zone_info(db->getZone(Name("example.com"))); + const std::pair zone_info(db->getZone("example.com.")); ASSERT_TRUE(zone_info.first); const int zone_id = zone_info.second; From 0a39659638fc68f60b95b102968d7d0ad75443ea Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 24 Aug 2011 10:27:40 +0800 Subject: [PATCH 625/974] [master] merge trac1153: zonemgr exits on empty zones --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index f85fa0bf8e..d53ae81f2e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +284. [bug] jerry + b10-zonemgr: zonemgr will not terminate on empty zones, it will + log a warning and try to do zone transfer for them. + (Trac #1153, git TBD) + 283. [bug] zhanglikun Make stats and boss processes wait for answer messages from each other in block mode to avoid orphan answer messages, add an internal From 3b0ccfb2f23961e4cbddb9d0873bab0f4c1d4c3d Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 24 Aug 2011 10:29:25 +0800 Subject: [PATCH 626/974] [master] update ChangeLog entry for #1153 --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d53ae81f2e..c7e6d62a12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ 284. [bug] jerry b10-zonemgr: zonemgr will not terminate on empty zones, it will log a warning and try to do zone transfer for them. - (Trac #1153, git TBD) + (Trac #1153, git 0a39659638fc68f60b95b102968d7d0ad75443ea) 283. [bug] zhanglikun Make stats and boss processes wait for answer messages from each From 087c6def9087019640a437b63c782a5c22de1feb Mon Sep 17 00:00:00 2001 From: chenzhengzhang Date: Wed, 24 Aug 2011 16:02:01 +0800 Subject: [PATCH 627/974] [master] remove useless variable (reviewed on jabber) --- src/lib/datasrc/tests/database_unittest.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index ac6cbf7a74..823c2bc7b0 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -276,8 +276,6 @@ public: private: typedef std::map > > Domains; - // used as internal index for getNextRecord() - size_t cur_record; // used as temporary storage during the building of the fake data Domains records; // used as temporary storage after searchForRecord() and during From fb3d0e1146e9e5a36a9402690a09e7629408c677 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Wed, 24 Aug 2011 07:35:03 -0400 Subject: [PATCH 628/974] [1144] docs for DLV --- src/lib/dns/rdata/generic/detail/ds_like.h | 68 +++++++++++++++++++--- src/lib/dns/rdata/generic/dlv_32769.cc | 30 ++++++++++ src/lib/dns/rdata/generic/dlv_32769.h | 22 ++++++- 3 files changed, 110 insertions(+), 10 deletions(-) diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h index 7e26d275b1..40157e63f6 100644 --- a/src/lib/dns/rdata/generic/detail/ds_like.h +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -20,8 +20,31 @@ #include #include +/// \brief \c rdata::DSLikeImpl class represents the DS-like RDATA for DS +/// and DLV types. +/// +/// This class implements the basic interfaces inherited by the DS and DLV +/// classes from the abstract \c rdata::Rdata class, and provides trivial +/// accessors to DS-like RDATA. templateclass DSLikeImpl { + // Common sequence of toWire() operations used for the two versions of + // toWire(). + template + void + toWireCommon(Output& output) const { + output.writeUint16(tag_); + output.writeUint8(algorithm_); + output.writeUint8(digest_type_); + output.writeData(&digest_[0], digest_.size()); + } + public: + /// \brief Constructor from string. + /// + /// Exceptions + /// + /// \c InvalidRdataText is thrown if the method cannot process the + /// parameter data for any of the number of reasons. DSLikeImpl(const string& ds_str) { istringstream iss(ds_str); @@ -52,6 +75,16 @@ public: decodeHex(digestbuf.str(), digest_); } + /// \brief Constructor from wire-format data. + /// + /// \param buffer A buffer storing the wire format data. + /// \param rdata_len The length of the RDATA in bytes, normally expected + /// to be the value of the RDLENGTH field of the corresponding RR. + /// + /// Exceptions + /// + /// \c InvalidRdataLength is thrown if the input data is too short for the + /// type. DSLikeImpl(InputBuffer& buffer, size_t rdata_len) { if (rdata_len < 4) { isc_throw(InvalidRdataLength, RRType(typeCode) << " too short"); @@ -70,6 +103,9 @@ public: digest_type_ = digest_type; } + /// \brief The copy constructor. + /// + /// Trivial for now, we could've used the default one. DSLikeImpl(const DSLikeImpl& source) { digest_ = source.digest_; @@ -78,6 +114,9 @@ public: digest_type_ = source.digest_type_; } + /// \brief Convert the DS-like data to a string. + /// + /// \return A \c string object that represents the DS-like data. string toText() const { using namespace boost; @@ -87,22 +126,34 @@ public: " " + encodeHex(digest_)); } + /// \brief Render the DS-like data in the wire format to an OutputBuffer + /// object. + /// + /// \param buffer An output buffer to store the wire data. void toWire(OutputBuffer& buffer) const { - buffer.writeUint16(tag_); - buffer.writeUint8(algorithm_); - buffer.writeUint8(digest_type_); - buffer.writeData(&digest_[0], digest_.size()); + toWireCommon(buffer); } + /// \brief Render the DS-like data in the wire format to an + /// AbstractMessageRenderer object. + /// + /// \param renderer A renderer object to send the wire data to. void toWire(AbstractMessageRenderer& renderer) const { - renderer.writeUint16(tag_); - renderer.writeUint8(algorithm_); - renderer.writeUint8(digest_type_); - renderer.writeData(&digest_[0], digest_.size()); + toWireCommon(renderer); } + /// \brief Compare two instances of DS-like RDATA. + /// + /// It is up to the caller to make sure that \c other is an object of the + /// same \c DSLikeImpl class. + /// + /// \param other the right-hand operand to compare against. + /// \return < 0 if \c this would be sorted before \c other. + /// \return 0 if \c this is identical to \c other in terms of sorting + /// order. + /// \return > 0 if \c this would be sorted after \c other. int compare(const DSLikeImpl& other_ds) const { if (tag_ != other_ds.tag_) { @@ -127,6 +178,7 @@ public: } } + /// \brief Accessors uint16_t getTag() const { return (tag_); diff --git a/src/lib/dns/rdata/generic/dlv_32769.cc b/src/lib/dns/rdata/generic/dlv_32769.cc index 70a52fc648..84a0d1603b 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.cc +++ b/src/lib/dns/rdata/generic/dlv_32769.cc @@ -39,18 +39,30 @@ using namespace isc::util::encode; #include +/// \brief Constructor from string. +/// +/// A copy of the implementation object is allocated and constructed. DLV::DLV(const string& ds_str) : impl_(new DLVImpl(ds_str)) {} +/// \brief Constructor from wire-format data. +/// +/// A copy of the implementation object is allocated and constructed. DLV::DLV(InputBuffer& buffer, size_t rdata_len) : impl_(new DLVImpl(buffer, rdata_len)) {} +/// \brief Copy constructor +/// +/// A copy of the implementation object is allocated and constructed. DLV::DLV(const DLV& source) : Rdata(), impl_(new DLVImpl(*source.impl_)) {} +/// \brief Assignment operator +/// +/// PIMPL-induced logic DLV& DLV::operator=(const DLV& source) { if (impl_ == source.impl_) { @@ -64,25 +76,42 @@ DLV::operator=(const DLV& source) { return (*this); } +/// \brief Destructor +/// +/// Deallocates an internal resource. DLV::~DLV() { delete impl_; } +/// \brief Convert the \c DLV to a string. +/// +/// A pass-thru to the corresponding implementation method. string DLV::toText() const { return (impl_->toText()); } +/// \brief Render the \c DLV in the wire format to a OutputBuffer object +/// +/// A pass-thru to the corresponding implementation method. void DLV::toWire(OutputBuffer& buffer) const { impl_->toWire(buffer); } +/// \brief Render the \c DLV in the wire format to a AbstractMessageRenderer +/// object +/// +/// A pass-thru to the corresponding implementation method. void DLV::toWire(AbstractMessageRenderer& renderer) const { impl_->toWire(renderer); } +/// \brief Compare two instances of \c DLV RDATA. +/// +/// The type check is performed here. Otherwise, a pass-thru to the +/// corresponding implementation method. int DLV::compare(const Rdata& other) const { const DLV& other_ds = dynamic_cast(other); @@ -90,6 +119,7 @@ DLV::compare(const Rdata& other) const { return (impl_->compare(*other_ds.impl_)); } +/// \brief Tag accessor uint16_t DLV::getTag() const { return (impl_->getTag()); diff --git a/src/lib/dns/rdata/generic/dlv_32769.h b/src/lib/dns/rdata/generic/dlv_32769.h index 84b1d1c4ee..cdc22ec46f 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.h +++ b/src/lib/dns/rdata/generic/dlv_32769.h @@ -32,16 +32,34 @@ template class DSLikeImpl; +/// \brief \c rdata::DLV class represents the DLV RDATA as defined %in +/// RFC4431. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DLV RDATA. class DLV : public Rdata { public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. DLV& operator=(const DLV& source); + + /// \brief The destructor. ~DLV(); + /// \brief Return the value of the Tag field. /// - /// Specialized methods - /// + /// This method never throws an exception. uint16_t getTag() const; private: typedef DSLikeImpl DLVImpl; From 43da3c6c1cc7cb5fcb1dbe2f983a53e883408d1b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 24 Aug 2011 14:15:10 +0200 Subject: [PATCH 629/974] [1150] Use noinst_SCRIPTS on generated scripts Instead of EXTRA_DIST, this allows make to regenerate them on .in change. --- src/bin/bind10/tests/Makefile.am | 1 + src/bin/cfgmgr/tests/Makefile.am | 3 ++- src/bin/xfrout/tests/Makefile.am | 2 +- src/lib/python/isc/log/tests/Makefile.am | 5 +++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index d9e012fdfe..3f87fb55a1 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -2,6 +2,7 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ #PYTESTS = args_test.py bind10_test.py # NOTE: this has a generated test found in the builddir PYTESTS = bind10_test.py +noinst_SCRIPTS = $(PYTESTS) # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am index bd672419ea..24ff2bc4b0 100644 --- a/src/bin/cfgmgr/tests/Makefile.am +++ b/src/bin/cfgmgr/tests/Makefile.am @@ -1,7 +1,8 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ PYTESTS = b10-cfgmgr_test.py -EXTRA_DIST = $(PYTESTS) testdata/plugins/testplugin.py +noinst_SCRIPTS = $(PYTESTS) +EXTRA_DIST = testdata/plugins/testplugin.py # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index 99f4843f88..cf520b1005 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -1,6 +1,6 @@ PYCOVERAGE_RUN=@PYCOVERAGE_RUN@ PYTESTS = xfrout_test.py -EXTRA_DIST = $(PYTESTS) +noinst_SCRIPTS = $(PYTESTS) # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am index 6bb67de0d3..a518398853 100644 --- a/src/lib/python/isc/log/tests/Makefile.am +++ b/src/lib/python/isc/log/tests/Makefile.am @@ -1,6 +1,7 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ -PYTESTS = log_test.py -EXTRA_DIST = $(PYTESTS) log_console.py.in console.out check_output.sh +PYTESTS_GEN = log_test.py +noinst_SCRIPTS = $(PYTESTS) log_console.py +EXTRA_DIST = console.out check_output.sh # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. From ad91831c938430b6d4a8fd7bfae517a0f1e327c1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 24 Aug 2011 15:13:57 -0700 Subject: [PATCH 630/974] [1068] avoid using ASSERT_EQ in a constructor. ASSERT_EQ is not appropriate since it would have to have a return value, and even any xxx_EQ doesn't make much sense because this is not actually what we are trying to test. I choise to simply let it fail (should it ever happens); we should be able to know by seeing some of the subsequent tests fail. --- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index c4ff357824..11a896d845 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -299,9 +299,10 @@ const char* const deleted_data[] = { class SQLite3Update : public SQLite3AccessorTest { protected: SQLite3Update() { - ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR - "/test.sqlite3 " - TEST_DATA_BUILDDIR "/test.sqlite3.copied")); + // Note: if "installing" the test file fails some of the subsequent + // tests will fail and we should be able to notice that. + system(INSTALL_PROG " " TEST_DATA_DIR + "/test.sqlite3 " TEST_DATA_BUILDDIR "/test.sqlite3.copied"); initAccessor(TEST_DATA_BUILDDIR "/test.sqlite3.copied", RRClass::IN()); zone_id = accessor->getZone(Name("example.com")).second; another_accessor.reset(new SQLite3Accessor( From a7346d50ae5389ce37e35a7131f0f218663b8c68 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 24 Aug 2011 15:24:42 -0700 Subject: [PATCH 631/974] [1068] updated doxygen doc for ADD_TTL enum to be (hopefully) more intuitive --- src/lib/datasrc/database.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index f2aa9ed8ff..712e6026aa 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -81,7 +81,7 @@ public: enum AddRecordColumns { ADD_NAME = 0, ///< The owner name of the record (a domain name) ADD_REV_NAME = 1, ///< Reversed name of NAME (used for DNSSEC) - ADD_TTL = 2, ///< The TTL of the record (an integer) + ADD_TTL = 2, ///< The TTL of the record (in numeric form) ADD_TYPE = 3, ///< The RRType of the record (A/NS/TXT etc.) ADD_SIGTYPE = 4, ///< For RRSIG records, this contains the RRTYPE ///< the RRSIG covers. From df047c5ccb5c81f9a3d36f7fc38a19bc7c8f2ac2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 24 Aug 2011 17:09:00 -0700 Subject: [PATCH 632/974] [1068] use fixed sized arrays for addRecordToZone() and deleteRecordInZone() instead of vector, mainly to be consistent with other interfaces. --- src/lib/datasrc/database.h | 15 ++-- src/lib/datasrc/sqlite3_accessor.cc | 29 +++---- src/lib/datasrc/sqlite3_accessor.h | 6 +- src/lib/datasrc/tests/database_unittest.cc | 4 +- .../tests/sqlite3_accessor_unittest.cc | 84 +++++++------------ 5 files changed, 57 insertions(+), 81 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 712e6026aa..efa370663a 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -340,12 +340,12 @@ public: /// in the actual derived method. /// /// \exception DataSourceError Invalid call without starting a transaction, - /// the columns parameter has an invalid number of elements, or other - /// internal database error. + /// or other internal database error. /// - /// \param columns A vector of strings that defines a record to be added + /// \param columns An array of strings that defines a record to be added /// to the zone. - virtual void addRecordToZone(const std::vector& columns) = 0; + virtual void addRecordToZone( + const std::string (&columns)[ADD_COLUMN_COUNT]) = 0; /// Delete a single record from the zone to be updated. /// @@ -377,13 +377,12 @@ public: /// it simply ignores the result. /// /// \exception DataSourceError Invalid call without starting a transaction, - /// the columns parameter has an invalid number of elements, or other - /// internal database error. + /// or other internal database error. /// - /// \param params A vector of strings that defines a record to be deleted + /// \param params An array of strings that defines a record to be deleted /// from the zone. virtual void deleteRecordInZone( - const std::vector& params) = 0; + const std::string (¶ms)[DEL_PARAM_COUNT]) = 0; /// Commit updates to the zone. /// diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index ab34c3b94e..0e6ffdccef 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -516,9 +516,10 @@ SQLite3Accessor::rollbackUpdateZone() { namespace { // Commonly used code sequence for adding/deleting record +template void doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id, - const vector& update_params, const char* exec_desc) + COLUMNS_TYPE update_params, const char* exec_desc) { sqlite3_stmt* const stmt = dbparams.statements_[stmt_id]; StatementExecuter executer(dbparams, stmt_id, exec_desc); @@ -529,8 +530,10 @@ doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id, isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " << sqlite3_errmsg(dbparams.db_)); } - BOOST_FOREACH(const string& column, update_params) { - if (sqlite3_bind_text(stmt, ++param_id, column.c_str(), -1, + const size_t column_count = + sizeof(update_params) / sizeof(update_params[0]); + for (int i = 0; i < column_count; ++i) { + if (sqlite3_bind_text(stmt, ++param_id, update_params[i].c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) { isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " << sqlite3_errmsg(dbparams.db_)); @@ -541,31 +544,23 @@ doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id, } void -SQLite3Accessor::addRecordToZone(const vector& columns) { +SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "adding record to SQLite3 " "data source without transaction"); } - if (columns.size() != ADD_COLUMN_COUNT) { - isc_throw(DataSourceError, "adding incompatible number of columns " - "to SQLite3 data source: " << columns.size()); - } - - doUpdate(*dbparameters_, ADD_RECORD, columns, "add record to zone"); + doUpdate( + *dbparameters_, ADD_RECORD, columns, "add record to zone"); } void -SQLite3Accessor::deleteRecordInZone(const vector& params) { +SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) { if (!dbparameters_->updating_zone) { isc_throw(DataSourceError, "deleting record in SQLite3 " "data source without transaction"); } - if (params.size() != DEL_PARAM_COUNT) { - isc_throw(DataSourceError, "incompatible # of parameters for " - "deleting in SQLite3 data source: " << params.size()); - } - - doUpdate(*dbparameters_, DEL_RECORD, params, "delete record from zone"); + doUpdate( + *dbparameters_, DEL_RECORD, params, "delete record from zone"); } } diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 471f6a1e80..fd3cdfdf11 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -140,9 +140,11 @@ public: /// considered a bug of the higher level application program. virtual void rollbackUpdateZone(); - virtual void addRecordToZone(const std::vector& columns); + virtual void addRecordToZone( + const std::string (&columns)[ADD_COLUMN_COUNT]); - virtual void deleteRecordInZone(const std::vector& params); + virtual void deleteRecordInZone( + const std::string (¶ms)[DEL_PARAM_COUNT]); /// The SQLite3 implementation of this method returns a string starting /// with a fixed prefix of "sqlite3_" followed by the DB file name diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f832b1d652..c3d7b12cc2 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -64,8 +64,8 @@ public: } virtual void commitUpdateZone() {} virtual void rollbackUpdateZone() {} - virtual void addRecordToZone(const std::vector&) {} - virtual void deleteRecordInZone(const std::vector&) {} + virtual void addRecordToZone(const string (&)[ADD_COLUMN_COUNT]) {} + virtual void deleteRecordInZone(const string (&)[DEL_PARAM_COUNT]) {} virtual const std::string& getDBName() const { return (database_name_); diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 11a896d845..5725c81219 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -12,6 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include #include #include @@ -313,7 +314,8 @@ protected: int zone_id; std::string get_columns[DatabaseAccessor::COLUMN_COUNT]; - std::vector update_columns; + std::string add_columns[DatabaseAccessor::ADD_COLUMN_COUNT]; + std::string del_params[DatabaseAccessor::DEL_PARAM_COUNT]; vector expected_stored; // placeholder for checkRecords vector empty_stored; // indicate no corresponding data @@ -451,9 +453,9 @@ TEST_F(SQLite3Update, addRecord) { checkRecords(*accessor, zone_id, "newdata.example.com.", empty_stored); zone_id = accessor->startUpdateZone("example.com.", false).second; - update_columns.assign(new_data, - new_data + DatabaseAccessor::ADD_COLUMN_COUNT); - accessor->addRecordToZone(update_columns); + copy(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT, + add_columns); + accessor->addRecordToZone(add_columns); expected_stored.clear(); expected_stored.push_back(new_data); @@ -466,9 +468,9 @@ TEST_F(SQLite3Update, addRecord) { TEST_F(SQLite3Update, addThenRollback) { zone_id = accessor->startUpdateZone("example.com.", false).second; - update_columns.assign(new_data, - new_data + DatabaseAccessor::ADD_COLUMN_COUNT); - accessor->addRecordToZone(update_columns); + copy(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT, + add_columns); + accessor->addRecordToZone(add_columns); expected_stored.clear(); expected_stored.push_back(new_data); @@ -489,28 +491,17 @@ TEST_F(SQLite3Update, duplicateAdd) { // Adding exactly the same data. As this backend is "dumb", another // row of the same content will be inserted. - update_columns.assign(dup_data, - dup_data + DatabaseAccessor::ADD_COLUMN_COUNT); + copy(dup_data, dup_data + DatabaseAccessor::ADD_COLUMN_COUNT, + add_columns); zone_id = accessor->startUpdateZone("example.com.", false).second; - accessor->addRecordToZone(update_columns); + accessor->addRecordToZone(add_columns); expected_stored.push_back(dup_data); checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, invalidAdd) { // An attempt of add before an explicit start of transaction - EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); - - // Short column vector - update_columns.clear(); - zone_id = accessor->startUpdateZone("example.com.", false).second; - EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); - - // Too many columns - for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT + 1; ++i) { - update_columns.push_back(""); - } - EXPECT_THROW(accessor->addRecordToZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->addRecordToZone(add_columns), DataSourceError); } TEST_F(SQLite3Update, deleteRecord) { @@ -518,9 +509,9 @@ TEST_F(SQLite3Update, deleteRecord) { checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); - update_columns.assign(deleted_data, deleted_data + - DatabaseAccessor::DEL_PARAM_COUNT); - accessor->deleteRecordInZone(update_columns); + copy(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT, + del_params); + accessor->deleteRecordInZone(del_params); checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Commit the change, and confirm the deleted data still isn't there. @@ -531,9 +522,9 @@ TEST_F(SQLite3Update, deleteRecord) { TEST_F(SQLite3Update, deleteThenRollback) { zone_id = accessor->startUpdateZone("example.com.", false).second; - update_columns.assign(deleted_data, deleted_data + - DatabaseAccessor::DEL_PARAM_COUNT); - accessor->deleteRecordInZone(update_columns); + copy(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT, + del_params); + accessor->deleteRecordInZone(del_params); checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored); // Rollback the change, and confirm the data still exists. @@ -543,47 +534,36 @@ TEST_F(SQLite3Update, deleteThenRollback) { TEST_F(SQLite3Update, deleteNonexistent) { zone_id = accessor->startUpdateZone("example.com.", false).second; - update_columns.assign(deleted_data, deleted_data + - DatabaseAccessor::DEL_PARAM_COUNT); + copy(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT, + del_params); // Replace the name with a non existent one, then try to delete it. // nothing should happen. - update_columns[0] = "no-such-name.example.com."; + del_params[DatabaseAccessor::DEL_NAME] = "no-such-name.example.com."; checkRecords(*accessor, zone_id, "no-such-name.example.com.", empty_stored); - accessor->deleteRecordInZone(update_columns); + accessor->deleteRecordInZone(del_params); checkRecords(*accessor, zone_id, "no-such-name.example.com.", empty_stored); // Name exists but the RR type is different. Delete attempt shouldn't // delete only by name. - update_columns.assign(deleted_data, deleted_data + - DatabaseAccessor::DEL_PARAM_COUNT); - update_columns[1] = "AAAA"; - accessor->deleteRecordInZone(update_columns); + copy(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT, + del_params); + del_params[DatabaseAccessor::DEL_TYPE] = "AAAA"; + accessor->deleteRecordInZone(del_params); checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); // Similar to the previous case, but RDATA is different. - update_columns.assign(deleted_data, deleted_data + - DatabaseAccessor::DEL_PARAM_COUNT); - update_columns[2] = "192.0.2.2"; - accessor->deleteRecordInZone(update_columns); + copy(deleted_data, deleted_data + DatabaseAccessor::DEL_PARAM_COUNT, + del_params); + del_params[DatabaseAccessor::DEL_RDATA] = "192.0.2.2"; + accessor->deleteRecordInZone(del_params); checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored); } TEST_F(SQLite3Update, invalidDelete) { // An attempt of delete before an explicit start of transaction - EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); - - // Short column vector - update_columns.clear(); - zone_id = accessor->startUpdateZone("example.com.", false).second; - EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); - - // Too many parameters - for (int i = 0; i < DatabaseAccessor::DEL_PARAM_COUNT + 1; ++i) { - update_columns.push_back(""); - } - EXPECT_THROW(accessor->deleteRecordInZone(update_columns), DataSourceError); + EXPECT_THROW(accessor->deleteRecordInZone(del_params), DataSourceError); } } // end anonymous namespace From ef64723fe9638f8d56f58fba44a149ac620eadd9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 25 Aug 2011 01:26:42 -0700 Subject: [PATCH 633/974] [master] clarified the expected behavior of DatabaseAccessor::getNext() after it returns false. okayed on jabber, and I don't think we need a separate ticket/branch for this as it's just a small documentation update. --- src/lib/datasrc/database.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index d0237ecab7..e63de75922 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -137,6 +137,11 @@ public: * definition already known to the caller (it already passes it as * an argument to getRecords()). * + * Once this function returns false, any subsequent call to it should + * result in false. The implementation of a derived class must ensure + * it doesn't cause any disruption due to that such as a crash or + * exception. + * * \note The order of RRs is not strictly set, but the RRs for single * RRset must not be interleaved with any other RRs (eg. RRsets must be * "together"). From 4700bada6282f5ad10b53cd8ca7cc03b8fea791d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 25 Aug 2011 11:14:42 +0200 Subject: [PATCH 634/974] [master] changelog entry --- ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index c7e6d62a12..a71c8a8221 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +285. [bug] jelte + sqlite3 data source: fixed a race condition on initial startup, + when the database has not been initialized yet, and multiple + processes are trying to do so, resulting in one of them failing. + (Trac #326, git 5de6f9658f745e05361242042afd518b444d7466) + 284. [bug] jerry b10-zonemgr: zonemgr will not terminate on empty zones, it will log a warning and try to do zone transfer for them. From 10553ed4ebb5b949ae74d277d398d2e8a3909ea5 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 25 Aug 2011 11:54:24 +0200 Subject: [PATCH 635/974] [1150] Fix generated/nongenerated tests The folder contains both generated and directly written files, so we need to handle them separately, because of when we have different builddir than srcdir. --- src/lib/python/isc/log/tests/Makefile.am | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am index a518398853..03c30cdca9 100644 --- a/src/lib/python/isc/log/tests/Makefile.am +++ b/src/lib/python/isc/log/tests/Makefile.am @@ -1,7 +1,8 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ -PYTESTS_GEN = log_test.py -noinst_SCRIPTS = $(PYTESTS) log_console.py -EXTRA_DIST = console.out check_output.sh +PYTESTS_GEN = log_console.py +PYTESTS_NOGEN = log_test.py +noinst_SCRIPTS = $(PYTESTS_GEN) +EXTRA_DIST = console.out check_output.sh $(PYTESTS_NOGEN) # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. @@ -11,6 +12,7 @@ LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/. endif # test using command-line arguments, so use check-local target instead of TESTS +# We need to run the cycle twice, because once the files are in builddir, once in srcdir check-local: $(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 \ @@ -20,10 +22,17 @@ if ENABLE_PYTHON_COVERAGE rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif - for pytest in $(PYTESTS) ; do \ + for pytest in $(PYTESTS_NOGEN) ; do \ 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 ; \ + for pytest in $(PYTESTS_GEN) ; do \ + 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_builddir)/$$pytest || exit ; \ done From 4c0accf0a591b0422c84216150e1b9b4e008609e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 25 Aug 2011 14:09:40 +0200 Subject: [PATCH 636/974] [1150] Make regenerated scripts executable When they are regenerated by make (not during initial configure run), they were not set executable, which was useless. This way they are set executable twice during the initial configure-make-make check, but at last it works. --- src/bin/bind10/tests/Makefile.am | 1 + src/bin/cfgmgr/tests/Makefile.am | 1 + src/bin/tests/Makefile.am | 1 + src/bin/xfrout/tests/Makefile.am | 1 + src/lib/python/isc/log/tests/Makefile.am | 2 ++ 5 files changed, 6 insertions(+) diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index 3f87fb55a1..f388ba1cbe 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -20,6 +20,7 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ + chmod +x $(abs_builddir)/$$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \ diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am index 24ff2bc4b0..99f8cc9471 100644 --- a/src/bin/cfgmgr/tests/Makefile.am +++ b/src/bin/cfgmgr/tests/Makefile.am @@ -20,6 +20,7 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ + chmod +x $(abs_builddir)/$$pytest ; \ env TESTDATA_PATH=$(abs_srcdir)/testdata \ $(LIBRARY_PATH_PLACEHOLDER) \ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/python/isc/config \ diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am index 56ff68b0c7..034152c173 100644 --- a/src/bin/tests/Makefile.am +++ b/src/bin/tests/Makefile.am @@ -20,6 +20,7 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ + chmod +x $(abs_builddir)/$$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index cf520b1005..2f1e2eaef5 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -18,6 +18,7 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ + chmod +x $(abs_builddir)/$$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am index 03c30cdca9..a23887c697 100644 --- a/src/lib/python/isc/log/tests/Makefile.am +++ b/src/lib/python/isc/log/tests/Makefile.am @@ -14,6 +14,7 @@ endif # test using command-line arguments, so use check-local target instead of TESTS # We need to run the cycle twice, because once the files are in builddir, once in srcdir check-local: + chmod +x $(abs_builddir)/log_console.py $(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_srcdir)/check_output.sh $(abs_builddir)/log_console.py $(abs_srcdir)/console.out @@ -31,6 +32,7 @@ endif done ; \ for pytest in $(PYTESTS_GEN) ; do \ echo Running test: $$pytest ; \ + chmod +x $(abs_builddir)/$$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 \ From ecf3f4b962026aa9094ee321b03ee32df2fdf1d2 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 25 Aug 2011 16:36:56 +0200 Subject: [PATCH 637/974] [1041] check for space in path --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index 6e129b6093..c569096e94 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,12 @@ AC_PROG_CXX # Libtool configuration # + +# libtool cannot handle spaces in paths, so exit early if there is one +if [ test `echo $PWD | grep -c ' '` != "0" ]; then + AC_MSG_ERROR([BIND 10 cannot be built in a directory that contains spaces, because of libtool limitations. Please change the directory name, or use a symbolic link that does not contain spaces.]) +fi + # On FreeBSD (and probably some others), clang++ does not meet an autoconf # assumption in identifying libtool configuration regarding shared library: # the configure script will execute "$CC -shared $CFLAGS/$CXXFLAGS -v" and From 77ba8639c274865c762eee688383c321f18ef889 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 25 Aug 2011 16:42:09 +0200 Subject: [PATCH 638/974] [1176] Test that wildcards don't break RRSIGs It works, but just making sure it works even with wildcards. --- src/lib/datasrc/tests/database_unittest.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 13bda7080f..bd0c5841d3 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -461,6 +461,7 @@ private: // Something for wildcards addRecord("A", "3600", "", "192.0.2.5"); + addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); addCurName("*.wild.example.org."); addRecord("AAAA", "3600", "", "2001:db8::5"); addCurName("cancel.here.wild.example.org."); @@ -1161,7 +1162,10 @@ TEST_F(DatabaseClientTest, wildcard) { shared_ptr finder(getFinder()); // First, simple wildcard match + // Check also that the RRSIG is added from the wildcard (not modified) expected_rdatas_.push_back("192.0.2.5"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 " + "12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("a.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, @@ -1171,6 +1175,7 @@ TEST_F(DatabaseClientTest, wildcard) { isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); doFindTest(finder, isc::dns::Name("a.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, @@ -1182,11 +1187,14 @@ TEST_F(DatabaseClientTest, wildcard) { // Direct request for thi wildcard expected_rdatas_.push_back("192.0.2.5"); + expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 " + "12345 example.org. FAKEFAKEFAKE"); doFindTest(finder, isc::dns::Name("*.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); expected_rdatas_.clear(); + expected_sig_rdatas_.clear(); doFindTest(finder, isc::dns::Name("*.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, From 8d380bb47dd24c7fd2c4880a4106835d871bf4d5 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 25 Aug 2011 17:02:56 +0200 Subject: [PATCH 639/974] [1176] Add flag to enforce inclusion of DNSSEC data Not used for anything yet. --- src/lib/datasrc/zone.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 0dacc5da55..c5e1a06eea 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -107,7 +107,11 @@ public: /// performed on these values to express compound options. enum FindOptions { FIND_DEFAULT = 0, ///< The default options - FIND_GLUE_OK = 1 ///< Allow search under a zone cut + FIND_GLUE_OK = 1, ///< Allow search under a zone cut + FIND_DNSSEC = 2 ///< Require DNSSEC data in the answer + ///< (RRSIG, NSEC, etc.). The implementation + ///< is allowed to include it even if it is + ///< not set. }; /// From 0953b3f5d7ed1b4a25362f9a2d1a41eeeda8efa6 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 25 Aug 2011 12:17:13 -0700 Subject: [PATCH 640/974] [1068] renamed StatementExecuter to StatementProcessor as the latter would sound less odd. --- src/lib/datasrc/sqlite3_accessor.cc | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 0e6ffdccef..b87ab2dc0e 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -89,18 +89,18 @@ struct SQLite3Parameters { // statement, which is completed with a single "step" (normally within a // single call to an SQLite3Database method). In particular, it cannot be // used for "SELECT" variants, which generally expect multiple matching rows. -class StatementExecuter { +class StatementProcessor { public: // desc will be used on failure in the what() message of the resulting // DataSourceError exception. - StatementExecuter(SQLite3Parameters& dbparameters, StatementID stmt_id, - const char* desc) : + StatementProcessor(SQLite3Parameters& dbparameters, StatementID stmt_id, + const char* desc) : dbparameters_(dbparameters), stmt_id_(stmt_id), desc_(desc) { sqlite3_clear_bindings(dbparameters_.statements_[stmt_id_]); } - ~StatementExecuter() { + ~StatementProcessor() { sqlite3_reset(dbparameters_.statements_[stmt_id_]); } @@ -454,13 +454,13 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) { return (zone_info); } - StatementExecuter(*dbparameters_, BEGIN, - "start an SQLite3 transaction").exec(); + StatementProcessor(*dbparameters_, BEGIN, + "start an SQLite3 transaction").exec(); if (replace) { try { - StatementExecuter delzone_exec(*dbparameters_, DEL_ZONE_RECORDS, - "delete zone records"); + StatementProcessor delzone_exec(*dbparameters_, DEL_ZONE_RECORDS, + "delete zone records"); sqlite3_clear_bindings( dbparameters_->statements_[DEL_ZONE_RECORDS]); @@ -476,8 +476,8 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) { // Once we start a transaction, if something unexpected happens // we need to rollback the transaction so that a subsequent update // is still possible with this accessor. - StatementExecuter(*dbparameters_, ROLLBACK, - "rollback an SQLite3 transaction").exec(); + StatementProcessor(*dbparameters_, ROLLBACK, + "rollback an SQLite3 transaction").exec(); throw; } } @@ -495,8 +495,8 @@ SQLite3Accessor::commitUpdateZone() { "data source without transaction"); } - StatementExecuter(*dbparameters_, COMMIT, - "commit an SQLite3 transaction").exec(); + StatementProcessor(*dbparameters_, COMMIT, + "commit an SQLite3 transaction").exec(); dbparameters_->updating_zone = false; dbparameters_->updated_zone_id = -1; } @@ -508,8 +508,8 @@ SQLite3Accessor::rollbackUpdateZone() { "data source without transaction"); } - StatementExecuter(*dbparameters_, ROLLBACK, - "rollback an SQLite3 transaction").exec(); + StatementProcessor(*dbparameters_, ROLLBACK, + "rollback an SQLite3 transaction").exec(); dbparameters_->updating_zone = false; dbparameters_->updated_zone_id = -1; } @@ -522,7 +522,7 @@ doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id, COLUMNS_TYPE update_params, const char* exec_desc) { sqlite3_stmt* const stmt = dbparams.statements_[stmt_id]; - StatementExecuter executer(dbparams, stmt_id, exec_desc); + StatementProcessor executer(dbparams, stmt_id, exec_desc); int param_id = 0; if (sqlite3_bind_int(stmt, ++param_id, dbparams.updated_zone_id) From 255bf5b18e2b0e28a65062e87dc2d1212376bfc2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 25 Aug 2011 13:16:09 -0700 Subject: [PATCH 641/974] [master] rename exists() to isReadable() to better represent the intent. also use is_open() instead of != NULL (it's strange that the original code did even compile, but in any case this one should be better in clarity and possibly in portability). okayed on jabber, should be minor enough, directly pushing. --- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index c9db3f146f..427ee7167e 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -364,20 +364,19 @@ public: } }; -bool exists(const char* filename) { - std::ifstream f(filename); - return (f != NULL); +bool isReadable(const char* filename) { + return (std::ifstream(filename).is_open()); } TEST_F(SQLite3Create, creationtest) { - ASSERT_FALSE(exists(SQLITE_NEW_DBFILE)); + ASSERT_FALSE(isReadable(SQLITE_NEW_DBFILE)); // Should simply be created SQLite3Accessor accessor(SQLITE_NEW_DBFILE, RRClass::IN()); - ASSERT_TRUE(exists(SQLITE_NEW_DBFILE)); + ASSERT_TRUE(isReadable(SQLITE_NEW_DBFILE)); } TEST_F(SQLite3Create, emptytest) { - ASSERT_FALSE(exists(SQLITE_NEW_DBFILE)); + ASSERT_FALSE(isReadable(SQLITE_NEW_DBFILE)); // open one manualle sqlite3* db; @@ -393,7 +392,7 @@ TEST_F(SQLite3Create, emptytest) { } TEST_F(SQLite3Create, lockedtest) { - ASSERT_FALSE(exists(SQLITE_NEW_DBFILE)); + ASSERT_FALSE(isReadable(SQLITE_NEW_DBFILE)); // open one manually sqlite3* db; From 84d83c1d8979e2906971af79f2e41083299beb7e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 25 Aug 2011 22:54:20 +0200 Subject: [PATCH 642/974] [1176] Interface to do dnssec query --- src/bin/auth/query.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h index 13523e8b58..47d52477f1 100644 --- a/src/bin/auth/query.h +++ b/src/bin/auth/query.h @@ -139,11 +139,13 @@ public: /// \param qname The query name /// \param qtype The RR type of the query /// \param response The response message to store the answer to the query. + /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if + /// possible. Query(const isc::datasrc::DataSourceClient& datasrc_client, const isc::dns::Name& qname, const isc::dns::RRType& qtype, - isc::dns::Message& response) : + isc::dns::Message& response, bool dnssec = false) : datasrc_client_(datasrc_client), qname_(qname), qtype_(qtype), - response_(response) + response_(response), dnssec_(dnssec) {} /// Process the query. @@ -211,6 +213,7 @@ private: const isc::dns::Name& qname_; const isc::dns::RRType& qtype_; isc::dns::Message& response_; + const bool dnssec_; }; } From b131dd71ce147b4efcece9dd8fba16c51fefa492 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 25 Aug 2011 23:35:41 +0200 Subject: [PATCH 643/974] [1176] Test for query dnssec positive support --- src/bin/auth/tests/query_unittest.cc | 54 +++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index 68f0a1d57a..c389caa902 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -141,7 +141,7 @@ private: typedef map RRsetStore; typedef map Domains; Domains domains_; - void loadRRset(ConstRRsetPtr rrset) { + void loadRRset(RRsetPtr rrset) { domains_[rrset->getName()][rrset->getType()] = rrset; if (rrset->getName() == delegation_name_ && rrset->getType() == RRType::NS()) { @@ -149,6 +149,26 @@ private: } else if (rrset->getName() == dname_name_ && rrset->getType() == RRType::DNAME()) { dname_rrset_ = rrset; + // Add some signatures + } else if (rrset->getName() == Name("example.com.") && + rrset->getType() == RRType::NS()) { + rrset->addRRsig(RdataPtr(new generic::RRSIG("NS 5 3 3600 " + "20000101000000 " + "20000201000000 " + "12345 example.com. " + "FAKEFAKEFAKE"))); + } else if (rrset->getType() == RRType::A()) { + rrset->addRRsig(RdataPtr(new generic::RRSIG("A 5 3 3600 " + "20000101000000 " + "20000201000000 " + "12345 example.com. " + "FAKEFAKEFAKE"))); + } else if (rrset->getType() == RRType::AAAA()) { + rrset->addRRsig(RdataPtr(new generic::RRSIG("AAAA 5 3 3600 " + "20000101000000 " + "20000201000000 " + "12345 example.com. " + "FAKEFAKEFAKE"))); } } @@ -304,6 +324,38 @@ TEST_F(QueryTest, exactMatch) { www_a_txt, zone_ns_txt, ns_addrs_txt); } +TEST_F(QueryTest, dnssecPositive) { + // Just like exactMatch, but the signatures should be included as well + Query query(memory_client, qname, qtype, response, true); + EXPECT_NO_THROW(query.process()); + // find match rrset + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, + (www_a_txt + std::string("www.example.com. 3600 IN RRSIG " + "AAAA 5 3 3600 20000101000000 " + "20000201000000 12345 example.com. " + "FAKEFAKEFAKE\n")).c_str(), + (zone_ns_txt + std::string("example.com. 3600 IN RRSIG NS 5 " + "3 3600 20000101000000 " + "20000201000000 12345 " + "example.com. FAKEFAKEFAKE\n")). + c_str(), + (ns_addrs_txt + std::string("glue.delegation.example.com. " + "3600 IN RRSIG A 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. " + "FAKEFAKEFAKE\n" + "glue.delegation.example.com. " + "3600 IN RRSIG AAAA 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. " + "FAKEFAKEFAKE\n" + "noglue.example.com. 3600 IN " + "RRSIG A 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. " + "FAKEFAKEFAKE\n")).c_str()); +} + TEST_F(QueryTest, exactAddrMatch) { // find match rrset, omit additional data which has already been provided // in the answer section from the additional. From 8fe581570c2ef4f881762f4f22ef4f66c1063491 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 25 Aug 2011 20:32:29 -0700 Subject: [PATCH 644/974] [1068] refactoring for the test code so that it will be easily used by update tests. the semantics should be preserved at the moment. making a commit at this point mainly as a checkpoint. --- src/lib/datasrc/tests/database_unittest.cc | 214 ++++++++++++++++----- 1 file changed, 165 insertions(+), 49 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 1c19c93bbd..3f8764487c 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -12,6 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + #include #include @@ -35,6 +37,10 @@ using namespace isc::dns; namespace { +// Imaginary zone IDs used in the mock accessor below. +const int READONLY_ZONE_ID = 42; +const int WRITABLE_ZONE_ID = 4200; + /* * An accessor with minimum implementation, keeping the original * "NotImplemented" methods. @@ -46,7 +52,7 @@ public: virtual std::pair getZone(const std::string& name) const { if (name == "example.org.") { - return (std::pair(true, 42)); + return (std::pair(true, READONLY_ZONE_ID)); } else if (name == "null.example.org.") { return (std::pair(true, 13)); } else if (name == "empty.example.org.") { @@ -99,9 +105,15 @@ private: * implementation of the optional functionality. */ class MockAccessor : public NopAccessor { + // Type of mock database "row"s + typedef std::map > > + Domains; + public: - MockAccessor() - { + MockAccessor() { + readonly_records_ = &readonly_records_master_; + update_records_ = &update_records_master_; + empty_records_ = &empty_records_master_; fillData(); } private: @@ -122,32 +134,27 @@ private: throw std::exception(); } - if (zone_id == 42) { - if (subdomains) { - cur_name.clear(); - // Just walk everything and check if it is a subdomain. - // If it is, just copy all data from there. - for (Domains::const_iterator - i(mock_accessor.records.begin()); - i != mock_accessor.records.end(); ++ i) { - Name local(i->first); - if (local.compare(isc::dns::Name(name)). - getRelation() == - isc::dns::NameComparisonResult::SUBDOMAIN) { - cur_name.insert(cur_name.end(), i->second.begin(), - i->second.end()); - } - } - } else { + cur_record_ = 0; + const Domains& cur_records = mock_accessor.getMockRecords(zone_id); + if (cur_records.count(name) > 0) { // we're not aiming for efficiency in this test, simply // copy the relevant vector from records - if (mock_accessor.records.count(searched_name_) > 0) { - cur_name = mock_accessor.records.find(searched_name_)-> - second; - } else { - cur_name.clear(); + cur_name = cur_records.find(name)->second; + } else if (subdomains) { + cur_name.clear(); + // Just walk everything and check if it is a subdomain. + // If it is, just copy all data from there. + for (Domains::const_iterator i(cur_records.begin()); + i != cur_records.end(); ++i) { + const Name local(i->first); + if (local.compare(Name(name)).getRelation() == + isc::dns::NameComparisonResult::SUBDOMAIN) { + cur_name.insert(cur_name.end(), i->second.begin(), + i->second.end()); } } + } else { + cur_name.clear(); } } @@ -262,7 +269,7 @@ private: }; public: virtual IteratorContextPtr getAllRecords(int id) const { - if (id == 42) { + if (id == READONLY_ZONE_ID) { return (IteratorContextPtr(new MockIteratorContext())); } else if (id == 13) { return (IteratorContextPtr()); @@ -278,7 +285,7 @@ public: virtual IteratorContextPtr getRecords(const std::string& name, int id, bool subdomains) const { - if (id == 42) { + if (id == READONLY_ZONE_ID) { return (IteratorContextPtr(new MockNameIteratorContext(*this, id, name, subdomains))); } else { @@ -286,15 +293,64 @@ public: } } + // + // Helper methods to keep track of some update related activities + // + bool isRollbacked() const { + return (rollbacked_); + } + + const vector& getLastAdded() const { + return (columns_lastadded_); + } + + // This allows the test code to get the accessor used in an update context + shared_ptr getLatestClone() const { + return (latest_clone_); + } + private: - typedef std::map > > - Domains; + // The following member variables are storage and/or update work space + // of the test zone. The "master"s are the real objects that contain + // the data, and they are shared among by all accessors cloned from + // an initially created one. The pointer members allow the sharing. + // "readonly" is for normal lookups. "update" is the workspace for + // updates. When update starts it will be initialized either as an + // empty set (when replacing the entire zone) or as a copy of the + // "readonly" one. "empty" is a sentinel to produce negative results. + Domains readonly_records_master_; + Domains* readonly_records_; + Domains update_records_master_; + Domains* update_records_; + const Domains empty_records_master_; + const Domains* empty_records_; + // used as temporary storage during the building of the fake data - Domains records; + //Domains records; + // used as temporary storage after searchForRecord() and during // getNextRecord() calls, as well as during the building of the // fake data - std::vector< std::vector > cur_name; + std::vector< std::vector > cur_name_; + + // The columns that were most recently added via addRecordToZone() + vector columns_lastadded_; + + // Whether rollback operation has been performed for the database. + // Not useful except for purely testing purpose. + bool rollbacked_; + + // Remember the mock accessor that was last cloned + boost::shared_ptr latest_clone_; + + const Domains& getMockRecords(int zone_id) const { + if (zone_id == READONLY_ZONE_ID) { + return (*readonly_records_); + } else if (zone_id == WRITABLE_ZONE_ID) { + return (*update_records_); + } + return (*empty_records_); + } // Adds one record to the current name in the database // The actual data will not be added to 'records' until @@ -308,21 +364,21 @@ private: columns.push_back(type); columns.push_back(sigtype); columns.push_back(rdata); - cur_name.push_back(columns); + cur_name_.push_back(columns); } // Adds all records we just built with calls to addRecords - // to the actual fake database. This will clear cur_name, + // to the actual fake database. This will clear cur_name_, // so we can immediately start adding new records. void addCurName(const std::string& name) { - ASSERT_EQ(0, records.count(name)); + ASSERT_EQ(0, readonly_records_->count(name)); // Append the name to all of them for (std::vector >::iterator - i(cur_name.begin()); i != cur_name.end(); ++ i) { + i(cur_name_.begin()); i != cur_name_.end(); ++ i) { i->push_back(name); } - records[name] = cur_name; - cur_name.clear(); + (*readonly_records_)[name] = cur_name_; + cur_name_.clear(); } // Fills the database with zone data. @@ -503,23 +559,44 @@ TEST(DatabaseConnectionTest, getAllRecords) { class DatabaseClientTest : public ::testing::Test { public: - DatabaseClientTest() { + DatabaseClientTest() : zname_("example.org"), qname_("www.example.org"), + qclass_(RRClass::IN()), qtype_(RRType::A()), + rrttl_(3600) + { createClient(); + + // set up the commonly used finder. + DataSourceClient::FindResult zone(client_->findZone(zname_)); + assert(zone.code == result::SUCCESS); + finder_ = dynamic_pointer_cast( + zone.zone_finder); + + // Test IN/A RDATA to be added in update tests. Intentionally using + // different data than the initial data configured in the MockAccessor. + rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), + rrset_->getClass(), "192.0.2.2")); + + // And its RRSIG. Also different from the configured one. + rrsigset_.reset(new RRset(qname_, qclass_, RRType::RRSIG(), + rrttl_)); + rrsigset_->addRdata(rdata::createRdata(rrsigset_->getType(), + rrsigset_->getClass(), + "A 5 3 0 20000101000000 " + "20000201000000 0 example.org. " + "FAKEFAKEFAKE")); } + /* * We initialize the client from a function, so we can call it multiple * times per test. */ void createClient() { current_accessor_ = new MockAccessor(); - client_.reset(new DatabaseClient(RRClass::IN(), + client_.reset(new DatabaseClient(qclass_, shared_ptr( current_accessor_))); } - // Will be deleted by client_, just keep the current value for comparison. - MockAccessor* current_accessor_; - shared_ptr client_; - const std::string database_name_; /** * Check the zone finder is a valid one and references the zone ID and @@ -531,21 +608,61 @@ public: dynamic_pointer_cast(zone.zone_finder)); ASSERT_NE(shared_ptr(), finder) << "Wrong type of finder"; - EXPECT_EQ(42, finder->zone_id()); + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); EXPECT_EQ(current_accessor_, &finder->getAccessor()); } shared_ptr getFinder() { - DataSourceClient::FindResult zone( - client_->findZone(Name("example.org"))); + DataSourceClient::FindResult zone(client_->findZone(zname_)); EXPECT_EQ(result::SUCCESS, zone.code); shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); - EXPECT_EQ(42, finder->zone_id()); + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); return (finder); } + // Helper methods for update tests + //bool isRollbacked(bool expected = false) const { + bool isRollbacked() const { + return (update_accessor_->isRollbacked()); + } + + void checkLastAdded(const char* const expected[]) const { + int i = 0; + BOOST_FOREACH(const string& column, + current_accessor_->getLastAdded()) { + EXPECT_EQ(expected[i++], column); + } + } + + void setUpdateAccessor() { + update_accessor_ = current_accessor_->getLatestClone(); + } + + // Will be deleted by client_, just keep the current value for comparison. + MockAccessor* current_accessor_; + shared_ptr client_; + const std::string database_name_; + + // The zone finder of the test zone commonly used in various tests. + shared_ptr finder_; + + // Some shortcut variables for commonly used test parameters + const Name zname_; // the zone name stored in the test data source + const Name qname_; // commonly used name to be found + const RRClass qclass_; // commonly used RR class used with qname + const RRType qtype_; // commonly used RR type used with qname + const RRTTL rrttl_; // commonly used RR TTL + RRsetPtr rrset_; // for adding/deleting an RRset + RRsetPtr rrsigset_; // for adding/deleting an RRset + + // update related objects to be tested + ZoneUpdaterPtr updater_; + shared_ptr update_accessor_; + + // placeholders + const std::vector empty_rdatas_; // for NXRRSET/NXDOMAIN std::vector expected_rdatas_; std::vector expected_sig_rdatas_; }; @@ -1299,8 +1416,7 @@ TEST_F(DatabaseClientTest, getOrigin) { ASSERT_EQ(result::SUCCESS, zone.code); shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); - EXPECT_EQ(42, finder->zone_id()); + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); EXPECT_EQ(isc::dns::Name("example.org"), finder->getOrigin()); } - } From ddf9da5175b1182810838861f1464fb05fe00104 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 26 Aug 2011 11:14:30 +0200 Subject: [PATCH 645/974] [1176] Implement the DNSSEC support in query Only positive matches for now. This looks like someone already prepared for it, it only needs to passing the intent to few functions and everything handles itself well. Also updated tests, since the utility function gets confused by the RRSIGs. Not fixing the function for now, it looks complicated, because the problem is in it's design - if it happens to be just this one case, it will be easier. --- src/bin/auth/query.cc | 40 +++++++++++++++--------- src/bin/auth/query.h | 5 ++- src/bin/auth/tests/query_unittest.cc | 46 +++++++++++++++++----------- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc index 3fe03c802a..803123d947 100644 --- a/src/bin/auth/query.cc +++ b/src/bin/auth/query.cc @@ -67,20 +67,23 @@ Query::findAddrs(ZoneFinder& zone, const Name& qname, // Find A rrset if (qname_ != qname || qtype_ != RRType::A()) { ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL, - options); + static_cast(options | dnssec_opt_)); if (a_result.code == ZoneFinder::SUCCESS) { response_.addRRset(Message::SECTION_ADDITIONAL, - boost::const_pointer_cast(a_result.rrset)); + boost::const_pointer_cast(a_result.rrset), dnssec_); } } // Find AAAA rrset if (qname_ != qname || qtype_ != RRType::AAAA()) { ZoneFinder::FindResult aaaa_result = - zone.find(qname, RRType::AAAA(), NULL, options); + zone.find(qname, RRType::AAAA(), NULL, + static_cast(options | + dnssec_opt_)); if (aaaa_result.code == ZoneFinder::SUCCESS) { response_.addRRset(Message::SECTION_ADDITIONAL, - boost::const_pointer_cast(aaaa_result.rrset)); + boost::const_pointer_cast(aaaa_result.rrset), + dnssec_); } } } @@ -88,7 +91,7 @@ Query::findAddrs(ZoneFinder& zone, const Name& qname, void Query::putSOA(ZoneFinder& zone) const { ZoneFinder::FindResult soa_result(zone.find(zone.getOrigin(), - RRType::SOA())); + RRType::SOA(), NULL, dnssec_opt_)); if (soa_result.code != ZoneFinder::SUCCESS) { isc_throw(NoSOA, "There's no SOA record in zone " << zone.getOrigin().toText()); @@ -99,7 +102,7 @@ Query::putSOA(ZoneFinder& zone) const { * to insist. */ response_.addRRset(Message::SECTION_AUTHORITY, - boost::const_pointer_cast(soa_result.rrset)); + boost::const_pointer_cast(soa_result.rrset), dnssec_); } } @@ -107,14 +110,15 @@ void Query::getAuthAdditional(ZoneFinder& zone) const { // Fill in authority and addtional sections. ZoneFinder::FindResult ns_result = zone.find(zone.getOrigin(), - RRType::NS()); + RRType::NS(), NULL, + dnssec_opt_); // zone origin name should have NS records if (ns_result.code != ZoneFinder::SUCCESS) { isc_throw(NoApexNS, "There's no apex NS records in zone " << zone.getOrigin().toText()); } else { response_.addRRset(Message::SECTION_AUTHORITY, - boost::const_pointer_cast(ns_result.rrset)); + boost::const_pointer_cast(ns_result.rrset), dnssec_); // Handle additional for authority section getAdditional(zone, *ns_result.rrset); } @@ -147,12 +151,14 @@ Query::process() const { keep_doing = false; std::auto_ptr target(qtype_is_any ? new RRsetList : NULL); const ZoneFinder::FindResult db_result( - result.zone_finder->find(qname_, qtype_, target.get())); + result.zone_finder->find(qname_, qtype_, target.get(), + dnssec_opt_)); switch (db_result.code) { case ZoneFinder::DNAME: { // First, put the dname into the answer response_.addRRset(Message::SECTION_ANSWER, - boost::const_pointer_cast(db_result.rrset)); + boost::const_pointer_cast(db_result.rrset), + dnssec_); /* * Empty DNAME should never get in, as it is impossible to * create one in master file. @@ -188,7 +194,7 @@ Query::process() const { qname_.getLabelCount() - db_result.rrset->getName().getLabelCount()). concatenate(dname.getDname()))); - response_.addRRset(Message::SECTION_ANSWER, cname); + response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_); break; } case ZoneFinder::CNAME: @@ -202,20 +208,23 @@ Query::process() const { * So, just put it there. */ response_.addRRset(Message::SECTION_ANSWER, - boost::const_pointer_cast(db_result.rrset)); + boost::const_pointer_cast(db_result.rrset), + dnssec_); break; case ZoneFinder::SUCCESS: if (qtype_is_any) { // If quety type is ANY, insert all RRs under the domain // into answer section. BOOST_FOREACH(RRsetPtr rrset, *target) { - response_.addRRset(Message::SECTION_ANSWER, rrset); + response_.addRRset(Message::SECTION_ANSWER, rrset, + dnssec_); // Handle additional for answer section getAdditional(*result.zone_finder, *rrset.get()); } } else { response_.addRRset(Message::SECTION_ANSWER, - boost::const_pointer_cast(db_result.rrset)); + boost::const_pointer_cast(db_result.rrset), + dnssec_); // Handle additional for answer section getAdditional(*result.zone_finder, *db_result.rrset); } @@ -233,7 +242,8 @@ Query::process() const { case ZoneFinder::DELEGATION: response_.setHeaderFlag(Message::HEADERFLAG_AA, false); response_.addRRset(Message::SECTION_AUTHORITY, - boost::const_pointer_cast(db_result.rrset)); + boost::const_pointer_cast(db_result.rrset), + dnssec_); getAdditional(*result.zone_finder, *db_result.rrset); break; case ZoneFinder::NXDOMAIN: diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h index 47d52477f1..0ebbed8a15 100644 --- a/src/bin/auth/query.h +++ b/src/bin/auth/query.h @@ -145,7 +145,9 @@ public: const isc::dns::Name& qname, const isc::dns::RRType& qtype, isc::dns::Message& response, bool dnssec = false) : datasrc_client_(datasrc_client), qname_(qname), qtype_(qtype), - response_(response), dnssec_(dnssec) + response_(response), dnssec_(dnssec), + dnssec_opt_(dnssec ? isc::datasrc::ZoneFinder::FIND_DNSSEC : + isc::datasrc::ZoneFinder::FIND_DEFAULT) {} /// Process the query. @@ -214,6 +216,7 @@ private: const isc::dns::RRType& qtype_; isc::dns::Message& response_; const bool dnssec_; + const isc::datasrc::ZoneFinder::FindOptions dnssec_opt_; }; } diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index c389caa902..77753bc2c8 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -215,6 +215,7 @@ MockZoneFinder::find(const Name& name, const RRType& type, RRsetStore::const_iterator found_rrset = found_domain->second.find(type); if (found_rrset != found_domain->second.end()) { + // TODO: Drop whatever rrsig is there if options doesn't have the dnssec return (FindResult(SUCCESS, found_rrset->second)); } @@ -329,31 +330,40 @@ TEST_F(QueryTest, dnssecPositive) { Query query(memory_client, qname, qtype, response, true); EXPECT_NO_THROW(query.process()); // find match rrset - responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, + // We can't let responseCheck to check the additional section as well, + // it gets confused by the two RRs for glue.delegation.../RRSIG due + // to it's design and fixing it would be hard. Therefore we simply + // check manually this one time. + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6, (www_a_txt + std::string("www.example.com. 3600 IN RRSIG " - "AAAA 5 3 3600 20000101000000 " + "A 5 3 3600 20000101000000 " "20000201000000 12345 example.com. " "FAKEFAKEFAKE\n")).c_str(), (zone_ns_txt + std::string("example.com. 3600 IN RRSIG NS 5 " "3 3600 20000101000000 " "20000201000000 12345 " "example.com. FAKEFAKEFAKE\n")). - c_str(), - (ns_addrs_txt + std::string("glue.delegation.example.com. " - "3600 IN RRSIG A 5 3 3600 " - "20000101000000 20000201000000 " - "12345 example.com. " - "FAKEFAKEFAKE\n" - "glue.delegation.example.com. " - "3600 IN RRSIG AAAA 5 3 3600 " - "20000101000000 20000201000000 " - "12345 example.com. " - "FAKEFAKEFAKE\n" - "noglue.example.com. 3600 IN " - "RRSIG A 5 3 3600 " - "20000101000000 20000201000000 " - "12345 example.com. " - "FAKEFAKEFAKE\n")).c_str()); + c_str(), NULL); + RRsetIterator iterator(response.beginSection(Message::SECTION_ADDITIONAL)); + const char* additional[] = { + "glue.delegation.example.com. 3600 IN A 192.0.2.153\n", + "glue.delegation.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 " + "20000201000000 12345 example.com. FAKEFAKEFAKE\n", + "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n", + "glue.delegation.example.com. 3600 IN RRSIG AAAA 5 3 3600 " + "20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE\n", + "noglue.example.com. 3600 IN A 192.0.2.53\n", + "noglue.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 " + "20000201000000 12345 example.com. FAKEFAKEFAKE\n", + NULL + }; + for (const char** rr(additional); *rr != NULL; ++ rr) { + ASSERT_FALSE(iterator == + response.endSection(Message::SECTION_ADDITIONAL)); + EXPECT_EQ(*rr, (*iterator)->toText()); + iterator ++; + } + EXPECT_TRUE(iterator == response.endSection(Message::SECTION_ADDITIONAL)); } TEST_F(QueryTest, exactAddrMatch) { From 38c8e9a9ccfd7fd57bc5fa5090c86cf7b7920d28 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 26 Aug 2011 11:26:22 +0200 Subject: [PATCH 646/974] [1176] Test it asks for DNSSEC data While backends are allowed to pass DNSSEC data when not asked to, they don't have to, so we need to ask for it. Test backend therefore doesn't return the DNSSEC data when not asked to, to check it really asks. --- src/bin/auth/tests/query_unittest.cc | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index 77753bc2c8..d65cc1029f 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -215,8 +215,25 @@ MockZoneFinder::find(const Name& name, const RRType& type, RRsetStore::const_iterator found_rrset = found_domain->second.find(type); if (found_rrset != found_domain->second.end()) { - // TODO: Drop whatever rrsig is there if options doesn't have the dnssec - return (FindResult(SUCCESS, found_rrset->second)); + ConstRRsetPtr rrset; + // Strip whatever signature there is in case DNSSEC is not required + // Just to make sure the Query asks for it when it is needed + if (options & ZoneFinder::FIND_DNSSEC || + !found_rrset->second->getRRsig()) { + rrset = found_rrset->second; + } else { + RRsetPtr noconst(new RRset(found_rrset->second->getName(), + found_rrset->second->getClass(), + found_rrset->second->getType(), + found_rrset->second->getTTL())); + for (RdataIteratorPtr + i(found_rrset->second->getRdataIterator()); + !i->isLast(); i->next()) { + noconst->addRdata(i->getCurrent()); + } + rrset = noconst; + } + return (FindResult(SUCCESS, rrset)); } // If not found but we have a target, fill it with all RRsets here From 778bd1be6ced7f4a135e2a6bcc7414c4e4bdc27d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 26 Aug 2011 17:14:57 +0200 Subject: [PATCH 647/974] [master] fix compiler warn error in test result value of system() was unused. And if we're catching it anyway, might as well error on it if the command failed. reviewed on jabber --- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 427ee7167e..022f68e87b 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -431,9 +431,16 @@ class SQLite3Update : public SQLite3AccessorTest { protected: SQLite3Update() { // Note: if "installing" the test file fails some of the subsequent - // tests will fail and we should be able to notice that. - system(INSTALL_PROG " " TEST_DATA_DIR - "/test.sqlite3 " TEST_DATA_BUILDDIR "/test.sqlite3.copied"); + // tests would fail. + const char *install_cmd = INSTALL_PROG " " TEST_DATA_DIR + "/test.sqlite3 " TEST_DATA_BUILDDIR + "/test.sqlite3.copied"; + if (system(install_cmd) != 0) { + // any exception will do, this is failure in test setup, but nice + // to show the command that fails, and shouldn't be caught + isc_throw(isc::Exception, + "Error setting up; command failed: " << install_cmd); + }; initAccessor(TEST_DATA_BUILDDIR "/test.sqlite3.copied", RRClass::IN()); zone_id = accessor->getZone("example.com.").second; another_accessor.reset(new SQLite3Accessor( From 93327a85ea63f7043c49a0af2384a1e274ab1dda Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 26 Aug 2011 10:59:58 -0700 Subject: [PATCH 648/974] [1068] cleanup and simplification --- src/lib/datasrc/tests/database_unittest.cc | 665 ++++++++++++++++++--- 1 file changed, 589 insertions(+), 76 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 3f8764487c..5a6563838c 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -86,19 +86,20 @@ public: { isc_throw(isc::NotImplemented, "This database datasource can't be iterated"); - }; + } virtual IteratorContextPtr getAllRecords(int) const { isc_throw(isc::NotImplemented, "This database datasource can't be iterated"); - }; + } + private: const std::string database_name_; }; /* - * A virtual database connection that pretends it contains single zone -- + * A virtual database accessor that pretends it contains single zone -- * example.org. * * It has the same getZone method as NopConnection, but it provides @@ -116,6 +117,16 @@ public: empty_records_ = &empty_records_master_; fillData(); } + + virtual shared_ptr clone() { + shared_ptr cloned_accessor(new MockAccessor()); + cloned_accessor->readonly_records_ = &readonly_records_master_; + cloned_accessor->update_records_ = &update_records_master_; + cloned_accessor->empty_records_ = &empty_records_master_; + latest_clone_ = cloned_accessor; + return (cloned_accessor); + } + private: class MockNameIteratorContext : public IteratorContext { public: @@ -285,14 +296,86 @@ public: virtual IteratorContextPtr getRecords(const std::string& name, int id, bool subdomains) const { - if (id == READONLY_ZONE_ID) { - return (IteratorContextPtr(new MockNameIteratorContext(*this, id, - name, subdomains))); + if (id == READONLY_ZONE_ID || id == WRITABLE_ZONE_ID) { + return (IteratorContextPtr( + new MockNameIteratorContext(*this, id, name, + subdomains))); } else { isc_throw(isc::Unexpected, "Unknown zone ID"); } } + virtual pair startUpdateZone(const std::string& zone_name, + bool replace) + { + const pair zone_info = getZone(zone_name); + if (!zone_info.first) { + return (pair(false, 0)); + } + + // Prepare the record set for update. If replacing the existing one, + // we use an empty set; otherwise we use a writable copy of the + // original. + if (replace) { + update_records_->clear(); + } else { + *update_records_ = *readonly_records_; + } + + return (pair(true, WRITABLE_ZONE_ID)); + } + virtual void commitUpdateZone() { + *readonly_records_ = *update_records_; + } + virtual void rollbackUpdateZone() { + rollbacked_ = true; + } + virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) { + // Copy the current value to cur_name. If it doesn't exist, + // operator[] will create a new one. + cur_name_ = (*update_records_)[columns[DatabaseAccessor::ADD_NAME]]; + + vector record_columns; + record_columns.push_back(columns[DatabaseAccessor::ADD_TYPE]); + record_columns.push_back(columns[DatabaseAccessor::ADD_TTL]); + record_columns.push_back(columns[DatabaseAccessor::ADD_SIGTYPE]); + record_columns.push_back(columns[DatabaseAccessor::ADD_RDATA]); + record_columns.push_back(columns[DatabaseAccessor::ADD_NAME]); + + // copy back the added entry + cur_name_.push_back(record_columns); + (*update_records_)[columns[DatabaseAccessor::ADD_NAME]] = cur_name_; + + // remember this one so that test cases can check it. + copy(columns, columns + DatabaseAccessor::ADD_COLUMN_COUNT, + columns_lastadded_); + } + + // Helper predicate class used in deleteRecordInZone(). + struct deleteMatch { + deleteMatch(const string& type, const string& rdata) : + type_(type), rdata_(rdata) + {} + bool operator()(const vector& row) const { + return (row[0] == type_ && row[3] == rdata_); + } + const string& type_; + const string& rdata_; + }; + + virtual void deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) { + vector >& records = + (*update_records_)[params[DatabaseAccessor::DEL_NAME]]; + records.erase(remove_if(records.begin(), records.end(), + deleteMatch( + params[DatabaseAccessor::DEL_TYPE], + params[DatabaseAccessor::DEL_RDATA])), + records.end()); + if (records.empty()) { + (*update_records_).erase(params[DatabaseAccessor::DEL_NAME]); + } + } + // // Helper methods to keep track of some update related activities // @@ -300,7 +383,7 @@ public: return (rollbacked_); } - const vector& getLastAdded() const { + const string* getLastAdded() const { return (columns_lastadded_); } @@ -334,7 +417,7 @@ private: std::vector< std::vector > cur_name_; // The columns that were most recently added via addRecordToZone() - vector columns_lastadded_; + string columns_lastadded_[ADD_COLUMN_COUNT]; // Whether rollback operation has been performed for the database. // Not useful except for purely testing purpose. @@ -355,13 +438,13 @@ private: // Adds one record to the current name in the database // The actual data will not be added to 'records' until // addCurName() is called - void addRecord(const std::string& name, - const std::string& type, + void addRecord(const std::string& type, + const std::string& ttl, const std::string& sigtype, const std::string& rdata) { std::vector columns; - columns.push_back(name); columns.push_back(type); + columns.push_back(ttl); columns.push_back(sigtype); columns.push_back(rdata); cur_name_.push_back(columns); @@ -629,10 +712,9 @@ public: } void checkLastAdded(const char* const expected[]) const { - int i = 0; - BOOST_FOREACH(const string& column, - current_accessor_->getLastAdded()) { - EXPECT_EQ(expected[i++], column); + for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT; ++i) { + EXPECT_EQ(expected[i], + current_accessor_->getLatestClone()->getLastAdded()[i]); } } @@ -803,7 +885,7 @@ checkRRset(isc::dns::ConstRRsetPtr rrset, } void -doFindTest(shared_ptr finder, +doFindTest(ZoneFinder& finder, const isc::dns::Name& name, const isc::dns::RRType& type, const isc::dns::RRType& expected_type, @@ -816,16 +898,16 @@ doFindTest(shared_ptr finder, { SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText()); ZoneFinder::FindResult result = - finder->find(name, type, NULL, options); + finder.find(name, type, NULL, options); ASSERT_EQ(expected_result, result.code) << name << " " << type; if (!expected_rdatas.empty()) { checkRRset(result.rrset, expected_name != Name(".") ? expected_name : - name, finder->getClass(), expected_type, expected_ttl, + name, finder.getClass(), expected_type, expected_ttl, expected_rdatas); if (!expected_sig_rdatas.empty()) { checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ? - expected_name : name, finder->getClass(), + expected_name : name, finder.getClass(), isc::dns::RRType::RRSIG(), expected_ttl, expected_sig_rdatas); } else { @@ -842,7 +924,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); - doFindTest(finder, isc::dns::Name("www.example.org."), + doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -852,7 +934,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); expected_rdatas_.push_back("192.0.2.2"); - doFindTest(finder, isc::dns::Name("www2.example.org."), + doFindTest(*finder, isc::dns::Name("www2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -862,7 +944,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("2001:db8::1"); expected_rdatas_.push_back("2001:db8::2"); - doFindTest(finder, isc::dns::Name("www.example.org."), + doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -870,7 +952,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("www.example.org."), + doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, @@ -879,7 +961,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); expected_rdatas_.push_back("www.example.org."); - doFindTest(finder, isc::dns::Name("cname.example.org."), + doFindTest(*finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, @@ -888,7 +970,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); expected_rdatas_.push_back("www.example.org."); - doFindTest(finder, isc::dns::Name("cname.example.org."), + doFindTest(*finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -896,7 +978,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("doesnotexist.example.org."), + doFindTest(*finder, isc::dns::Name("doesnotexist.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, @@ -907,7 +989,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.push_back("192.0.2.1"); expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signed1.example.org."), + doFindTest(*finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -918,7 +1000,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.push_back("2001:db8::1"); expected_rdatas_.push_back("2001:db8::2"); expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signed1.example.org."), + doFindTest(*finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -926,7 +1008,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("signed1.example.org."), + doFindTest(*finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, @@ -936,7 +1018,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("www.example.org."); expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signedcname1.example.org."), + doFindTest(*finder, isc::dns::Name("signedcname1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, @@ -947,7 +1029,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.push_back("192.0.2.1"); expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signed2.example.org."), + doFindTest(*finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -958,7 +1040,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.push_back("2001:db8::2"); expected_rdatas_.push_back("2001:db8::1"); expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signed2.example.org."), + doFindTest(*finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -966,7 +1048,7 @@ TEST_F(DatabaseClientTest, find) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("signed2.example.org."), + doFindTest(*finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, @@ -976,7 +1058,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("www.example.org."); expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("signedcname2.example.org."), + doFindTest(*finder, isc::dns::Name("signedcname2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::CNAME(), isc::dns::RRTTL(3600), ZoneFinder::CNAME, @@ -986,7 +1068,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("acnamesig1.example.org."), + doFindTest(*finder, isc::dns::Name("acnamesig1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -996,7 +1078,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("acnamesig2.example.org."), + doFindTest(*finder, isc::dns::Name("acnamesig2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -1006,7 +1088,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("acnamesig3.example.org."), + doFindTest(*finder, isc::dns::Name("acnamesig3.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -1016,7 +1098,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); expected_rdatas_.push_back("192.0.2.2"); - doFindTest(finder, isc::dns::Name("ttldiff1.example.org."), + doFindTest(*finder, isc::dns::Name("ttldiff1.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(360), ZoneFinder::SUCCESS, @@ -1026,7 +1108,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); expected_rdatas_.push_back("192.0.2.2"); - doFindTest(finder, isc::dns::Name("ttldiff2.example.org."), + doFindTest(*finder, isc::dns::Name("ttldiff2.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(360), ZoneFinder::SUCCESS, @@ -1094,7 +1176,7 @@ TEST_F(DatabaseClientTest, find) { expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("badsigtype.example.org."), + doFindTest(*finder, isc::dns::Name("badsigtype.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, @@ -1109,7 +1191,7 @@ TEST_F(DatabaseClientTest, findDelegation) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); - doFindTest(finder, isc::dns::Name("example.org."), + doFindTest(*finder, isc::dns::Name("example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); @@ -1118,7 +1200,7 @@ TEST_F(DatabaseClientTest, findDelegation) { expected_rdatas_.push_back("ns.example.com."); expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 " "12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("example.org."), + doFindTest(*finder, isc::dns::Name("example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); @@ -1131,17 +1213,17 @@ TEST_F(DatabaseClientTest, findDelegation) { expected_rdatas_.push_back("ns.delegation.example.org."); expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 " "12345 example.org. FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), + doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::A(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("delegation.example.org.")); - doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), + doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("delegation.example.org.")); - doFindTest(finder, isc::dns::Name("deep.below.delegation.example.org."), + doFindTest(*finder, isc::dns::Name("deep.below.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_, @@ -1149,13 +1231,13 @@ TEST_F(DatabaseClientTest, findDelegation) { // Even when we check directly at the delegation point, we should get // the NS - doFindTest(finder, isc::dns::Name("delegation.example.org."), + doFindTest(*finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_); // And when we ask direcly for the NS, we should still get delegation - doFindTest(finder, isc::dns::Name("delegation.example.org."), + doFindTest(*finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_); @@ -1169,21 +1251,21 @@ TEST_F(DatabaseClientTest, findDelegation) { expected_sig_rdatas_.push_back("DNAME 5 3 3600 20000101000000 " "20000201000000 12345 example.org. " "FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("below.dname.example.org."), + doFindTest(*finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); - doFindTest(finder, isc::dns::Name("below.dname.example.org."), + doFindTest(*finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); - doFindTest(finder, isc::dns::Name("really.deep.below.dname.example.org."), + doFindTest(*finder, isc::dns::Name("really.deep.below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); // Asking direcly for DNAME should give SUCCESS - doFindTest(finder, isc::dns::Name("dname.example.org."), + doFindTest(*finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::DNAME(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); @@ -1192,12 +1274,12 @@ TEST_F(DatabaseClientTest, findDelegation) { expected_rdatas_.clear(); expected_rdatas_.push_back("192.0.2.1"); expected_sig_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("dname.example.org."), + doFindTest(*finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); expected_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("dname.example.org."), + doFindTest(*finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); @@ -1224,7 +1306,7 @@ TEST_F(DatabaseClientTest, emptyDomain) { // This domain doesn't exist, but a subdomain of it does. // Therefore we should pretend the domain is there, but contains no RRsets - doFindTest(finder, isc::dns::Name("b.example.org."), isc::dns::RRType::A(), + doFindTest(*finder, isc::dns::Name("b.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); } @@ -1236,20 +1318,20 @@ TEST_F(DatabaseClientTest, glueOK) { expected_rdatas_.clear(); expected_sig_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), + doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("ns.delegation.example.org."), ZoneFinder::FIND_GLUE_OK); - doFindTest(finder, isc::dns::Name("nothere.delegation.example.org."), + doFindTest(*finder, isc::dns::Name("nothere.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("nothere.delegation.example.org."), ZoneFinder::FIND_GLUE_OK); expected_rdatas_.push_back("192.0.2.1"); - doFindTest(finder, isc::dns::Name("ns.delegation.example.org."), + doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_, @@ -1264,7 +1346,7 @@ TEST_F(DatabaseClientTest, glueOK) { "FAKEFAKEFAKE"); // When we request the NS, it should be SUCCESS, not DELEGATION // (different in GLUE_OK) - doFindTest(finder, isc::dns::Name("delegation.example.org."), + doFindTest(*finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_, @@ -1276,12 +1358,12 @@ TEST_F(DatabaseClientTest, glueOK) { expected_sig_rdatas_.push_back("DNAME 5 3 3600 20000101000000 " "20000201000000 12345 example.org. " "FAKEFAKEFAKE"); - doFindTest(finder, isc::dns::Name("below.dname.example.org."), + doFindTest(*finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::A(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org."), ZoneFinder::FIND_GLUE_OK); - doFindTest(finder, isc::dns::Name("below.dname.example.org."), + doFindTest(*finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("dname.example.org."), @@ -1293,64 +1375,64 @@ TEST_F(DatabaseClientTest, wildcard) { // First, simple wildcard match expected_rdatas_.push_back("192.0.2.5"); - doFindTest(finder, isc::dns::Name("a.wild.example.org"), + doFindTest(*finder, isc::dns::Name("a.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); - doFindTest(finder, isc::dns::Name("b.a.wild.example.org"), + doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); expected_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("a.wild.example.org"), + doFindTest(*finder, isc::dns::Name("a.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); - doFindTest(finder, isc::dns::Name("b.a.wild.example.org"), + doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); // Direct request for thi wildcard expected_rdatas_.push_back("192.0.2.5"); - doFindTest(finder, isc::dns::Name("*.wild.example.org"), + doFindTest(*finder, isc::dns::Name("*.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); expected_rdatas_.clear(); - doFindTest(finder, isc::dns::Name("*.wild.example.org"), + doFindTest(*finder, isc::dns::Name("*.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); // This is nonsense, but check it doesn't match by some stupid accident - doFindTest(finder, isc::dns::Name("a.*.wild.example.org"), + doFindTest(*finder, isc::dns::Name("a.*.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, expected_rdatas_, expected_sig_rdatas_); // These should be canceled, since it is below a domain which exitsts - doFindTest(finder, isc::dns::Name("nothing.here.wild.example.org"), + doFindTest(*finder, isc::dns::Name("nothing.here.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, expected_rdatas_, expected_sig_rdatas_); - doFindTest(finder, isc::dns::Name("cancel.here.wild.example.org"), + doFindTest(*finder, isc::dns::Name("cancel.here.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); - doFindTest(finder, + doFindTest(*finder, isc::dns::Name("below.cancel.here.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, expected_rdatas_, expected_sig_rdatas_); // And this should be just plain empty non-terminal domain, check // the wildcard doesn't hurt it - doFindTest(finder, isc::dns::Name("here.wild.example.org"), + doFindTest(*finder, isc::dns::Name("here.wild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); // Also make sure that the wildcard doesn't hurt the original data // below the wildcard expected_rdatas_.push_back("2001:db8::5"); - doFindTest(finder, isc::dns::Name("cancel.here.wild.example.org"), + doFindTest(*finder, isc::dns::Name("cancel.here.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); @@ -1358,14 +1440,14 @@ TEST_F(DatabaseClientTest, wildcard) { // How wildcard go together with delegation expected_rdatas_.push_back("ns.example.com."); - doFindTest(finder, isc::dns::Name("below.delegatedwild.example.org"), + doFindTest(*finder, isc::dns::Name("below.delegatedwild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_, isc::dns::Name("delegatedwild.example.org")); // FIXME: This doesn't look logically OK, GLUE_OK should make it transparent, // so the match should either work or be canceled, but return NXDOMAIN - doFindTest(finder, isc::dns::Name("below.delegatedwild.example.org"), + doFindTest(*finder, isc::dns::Name("below.delegatedwild.example.org"), isc::dns::RRType::A(), isc::dns::RRType::NS(), isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, expected_sig_rdatas_, @@ -1381,7 +1463,7 @@ TEST_F(DatabaseClientTest, wildcard) { NULL }; for (const char** name(positive_names); *name != NULL; ++ name) { - doFindTest(finder, isc::dns::Name(*name), isc::dns::RRType::A(), + doFindTest(*finder, isc::dns::Name(*name), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); @@ -1404,7 +1486,7 @@ TEST_F(DatabaseClientTest, wildcard) { NULL }; for (const char** name(negative_names); *name != NULL; ++ name) { - doFindTest(finder, isc::dns::Name(*name), isc::dns::RRType::A(), + doFindTest(*finder, isc::dns::Name(*name), isc::dns::RRType::A(), isc::dns::RRType::A(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); @@ -1419,4 +1501,435 @@ TEST_F(DatabaseClientTest, getOrigin) { EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); EXPECT_EQ(isc::dns::Name("example.org"), finder->getOrigin()); } + +TEST_F(DatabaseClientTest, updaterFinder) { + updater_ = client_->startUpdateZone(zname_, false); + ASSERT_TRUE(updater_); + + // If this update isn't replacing the zone, the finder should work + // just like the normal find() case. + EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( + updater_->getFinder()).zone_id()); + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + doFindTest(updater_->getFinder(), qname_, + qtype_, qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + + // When replacing the zone, the updater's finder shouldn't see anything + // in the zone until something is added. + updater_.reset(); + updater_ = client_->startUpdateZone(zname_, true); + ASSERT_TRUE(updater_); + EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( + updater_->getFinder()).zone_id()); + doFindTest(updater_->getFinder(), qname_, qtype_, qtype_, rrttl_, + ZoneFinder::NXDOMAIN, empty_rdatas_, empty_rdatas_); +} + +TEST_F(DatabaseClientTest, flushZone) { + // A simple update case: flush the entire zone + shared_ptr finder(getFinder()); + + // Before update, the name exists. + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); + + // start update in the replace mode. the normal finder should still + // be able to see the record, but the updater's finder shouldn't. + updater_ = client_->startUpdateZone(zname_, true); + setUpdateAccessor(); + EXPECT_EQ(ZoneFinder::SUCCESS, + finder->find(qname_, qtype_).code); + EXPECT_EQ(ZoneFinder::NXDOMAIN, + updater_->getFinder().find(qname_, qtype_).code); + + // commit the update. now the normal finder shouldn't see it. + updater_->commit(); + EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(qname_, qtype_).code); + + // Check rollback wasn't accidentally performed. + EXPECT_FALSE(isRollbacked()); +} + +TEST_F(DatabaseClientTest, updateCancel) { + // similar to the previous test, but destruct the updater before commit. + + ZoneFinderPtr finder = client_->findZone(zname_).zone_finder; + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); + + updater_ = client_->startUpdateZone(zname_, true); + setUpdateAccessor(); + EXPECT_EQ(ZoneFinder::NXDOMAIN, + updater_->getFinder().find(qname_, qtype_).code); + // DB should not have been rolled back yet. + EXPECT_FALSE(isRollbacked()); + updater_.reset(); // destruct without commit + + // reset() should have triggered rollback (although it doesn't affect + // anything to the mock accessor implementation except for the result of + // isRollbacked()) + EXPECT_TRUE(isRollbacked()); + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); +} + +TEST_F(DatabaseClientTest, duplicateCommit) { + // duplicate commit. should result in exception. + updater_ = client_->startUpdateZone(zname_, true); + updater_->commit(); + EXPECT_THROW(updater_->commit(), DataSourceError); +} + +TEST_F(DatabaseClientTest, addRRsetToNewZone) { + // Add a single RRset to a fresh empty zone + updater_ = client_->startUpdateZone(zname_, true); + updater_->addRRset(*rrset_); + + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.2"); + { + SCOPED_TRACE("add RRset"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } + + // Similar to the previous case, but with RRSIG + updater_.reset(); + updater_ = client_->startUpdateZone(zname_, true); + updater_->addRRset(*rrset_); + updater_->addRRset(*rrsigset_); + + // confirm the expected columns were passed to the accessor (if checkable). + const char* const rrsig_added[] = { + "www.example.org.", "org.example.www.", "3600", "RRSIG", "A", + "A 5 3 0 20000101000000 20000201000000 0 example.org. FAKEFAKEFAKE" + }; + checkLastAdded(rrsig_added); + + expected_sig_rdatas_.clear(); + expected_sig_rdatas_.push_back(rrsig_added[DatabaseAccessor::ADD_RDATA]); + { + SCOPED_TRACE("add RRset with RRSIG"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, expected_sig_rdatas_); + } +} + +TEST_F(DatabaseClientTest, addRRsetToCurrentZone) { + // Similar to the previous test, but not replacing the existing data. + shared_ptr finder(getFinder()); + + updater_ = client_->startUpdateZone(zname_, false); + updater_->addRRset(*rrset_); + + // We should see both old and new data. + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.2"); + { + SCOPED_TRACE("add RRset"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } + updater_->commit(); + { + SCOPED_TRACE("add RRset after commit"); + doFindTest(*finder, qname_, qtype_, qtype_, + rrttl_, ZoneFinder::SUCCESS, expected_rdatas_, + empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, addMultipleRRs) { + // Similar to the previous case, but the added RRset contains multiple + // RRs. + updater_ = client_->startUpdateZone(zname_, false); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "192.0.2.3")); + updater_->addRRset(*rrset_); + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.2"); + expected_rdatas_.push_back("192.0.2.3"); + { + SCOPED_TRACE("add multiple RRs"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, addRRsetOfLargerTTL) { + // Similar to the previous one, but the TTL of the added RRset is larger + // than that of the existing record. The finder should use the smaller + // one. + updater_ = client_->startUpdateZone(zname_, false); + rrset_->setTTL(RRTTL(7200)); + updater_->addRRset(*rrset_); + + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.2"); + { + SCOPED_TRACE("add RRset of larger TTL"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, addRRsetOfSmallerTTL) { + // Similar to the previous one, but the added RRset has a smaller TTL. + // The added TTL should be used by the finder. + updater_ = client_->startUpdateZone(zname_, false); + rrset_->setTTL(RRTTL(1800)); + updater_->addRRset(*rrset_); + + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.2"); + { + SCOPED_TRACE("add RRset of smaller TTL"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, RRTTL(1800), ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, addSameRR) { + // Add the same RR as that is already in the data source. + // Currently the add interface doesn't try to suppress the duplicate, + // neither does the finder. We may want to revisit it in future versions. + + updater_ = client_->startUpdateZone(zname_, false); + rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "192.0.2.1")); + updater_->addRRset(*rrset_); + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.1"); + { + SCOPED_TRACE("add same RR"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, addDeviantRR) { + updater_ = client_->startUpdateZone(zname_, false); + + // RR class mismatch. This should be detected and rejected. + rrset_.reset(new RRset(qname_, RRClass::CH(), RRType::TXT(), rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "test text")); + EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); + + // Out-of-zone owner name. At a higher level this should be rejected, + // but it doesn't happen in this interface. + rrset_.reset(new RRset(Name("example.com"), qclass_, qtype_, rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "192.0.2.100")); + updater_->addRRset(*rrset_); + + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.100"); + { + // Note: with the find() implementation being more strict about + // zone cuts, this test may fail. Then the test should be updated. + SCOPED_TRACE("add out-of-zone RR"); + doFindTest(updater_->getFinder(), Name("example.com"), + qtype_, qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, addEmptyRRset) { + updater_ = client_->startUpdateZone(zname_, false); + rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); + EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); +} + +TEST_F(DatabaseClientTest, addAfterCommit) { + updater_ = client_->startUpdateZone(zname_, false); + updater_->commit(); + EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); +} + +TEST_F(DatabaseClientTest, deleteRRset) { + shared_ptr finder(getFinder()); + + rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "192.0.2.1")); + + // Delete one RR from an RRset + updater_ = client_->startUpdateZone(zname_, false); + updater_->deleteRRset(*rrset_); + + // Delete the only RR of a name + rrset_.reset(new RRset(Name("cname.example.org"), qclass_, + RRType::CNAME(), rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "www.example.org")); + updater_->deleteRRset(*rrset_); + + // The updater_ finder should immediately see the deleted results. + { + SCOPED_TRACE("delete RRset"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::NXRRSET, + empty_rdatas_, empty_rdatas_); + doFindTest(updater_->getFinder(), Name("cname.example.org"), + qtype_, qtype_, rrttl_, ZoneFinder::NXDOMAIN, + empty_rdatas_, empty_rdatas_); + } + + // before committing the change, the original finder should see the + // original record. + { + SCOPED_TRACE("delete RRset before commit"); + expected_rdatas_.push_back("192.0.2.1"); + doFindTest(*finder, qname_, qtype_, qtype_, + rrttl_, ZoneFinder::SUCCESS, expected_rdatas_, + empty_rdatas_); + + expected_rdatas_.clear(); + expected_rdatas_.push_back("www.example.org."); + doFindTest(*finder, Name("cname.example.org"), qtype_, + RRType::CNAME(), rrttl_, ZoneFinder::CNAME, + expected_rdatas_, empty_rdatas_); + } + + // once committed, the record should be removed from the original finder's + // view, too. + updater_->commit(); + { + SCOPED_TRACE("delete RRset after commit"); + doFindTest(*finder, qname_, qtype_, qtype_, + rrttl_, ZoneFinder::NXRRSET, empty_rdatas_, + empty_rdatas_); + doFindTest(*finder, Name("cname.example.org"), + qtype_, qtype_, rrttl_, ZoneFinder::NXDOMAIN, + empty_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, deleteRRsetToNXDOMAIN) { + // similar to the previous case, but it removes the only record of the + // given name. a subsequent find() should result in NXDOMAIN. + rrset_.reset(new RRset(Name("cname.example.org"), qclass_, + RRType::CNAME(), rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "www.example.org")); + + updater_ = client_->startUpdateZone(zname_, false); + updater_->deleteRRset(*rrset_); + { + SCOPED_TRACE("delete RRset to NXDOMAIN"); + doFindTest(updater_->getFinder(), Name("cname.example.org"), + qtype_, qtype_, rrttl_, ZoneFinder::NXDOMAIN, + empty_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, deleteMultipleRRs) { + rrset_.reset(new RRset(qname_, qclass_, RRType::AAAA(), rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "2001:db8::1")); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "2001:db8::2")); + + updater_ = client_->startUpdateZone(zname_, false); + updater_->deleteRRset(*rrset_); + + { + SCOPED_TRACE("delete multiple RRs"); + doFindTest(updater_->getFinder(), qname_, RRType::AAAA(), + qtype_, rrttl_, ZoneFinder::NXRRSET, + empty_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, partialDelete) { + rrset_.reset(new RRset(qname_, qclass_, RRType::AAAA(), rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "2001:db8::1")); + // This does not exist in the test data source: + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "2001:db8::3")); + + // deleteRRset should succeed "silently", and subsequent find() should + // find the remaining RR. + updater_ = client_->startUpdateZone(zname_, false); + updater_->deleteRRset(*rrset_); + { + SCOPED_TRACE("partial delete"); + expected_rdatas_.push_back("2001:db8::2"); + doFindTest(updater_->getFinder(), qname_, RRType::AAAA(), + RRType::AAAA(), rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, deleteNoMatch) { + // similar to the previous test, but there's not even a match in the + // specified RRset. Essentially there's no difference in the result. + updater_ = client_->startUpdateZone(zname_, false); + updater_->deleteRRset(*rrset_); + { + SCOPED_TRACE("delete no match"); + expected_rdatas_.push_back("192.0.2.1"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::SUCCESS, + expected_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, deleteWithDifferentTTL) { + // Our delete interface simply ignores TTL (may change in a future version) + rrset_.reset(new RRset(qname_, qclass_, qtype_, RRTTL(1800))); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "192.0.2.1")); + updater_ = client_->startUpdateZone(zname_, false); + updater_->deleteRRset(*rrset_); + { + SCOPED_TRACE("delete RRset with a different TTL"); + doFindTest(updater_->getFinder(), qname_, qtype_, + qtype_, rrttl_, ZoneFinder::NXRRSET, + empty_rdatas_, empty_rdatas_); + } +} + +TEST_F(DatabaseClientTest, deleteDeviantRR) { + updater_ = client_->startUpdateZone(zname_, false); + + // RR class mismatch. This should be detected and rejected. + rrset_.reset(new RRset(qname_, RRClass::CH(), RRType::TXT(), rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "test text")); + EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); + + // Out-of-zone owner name. At a higher level this should be rejected, + // but it doesn't happen in this interface. + rrset_.reset(new RRset(Name("example.com"), qclass_, qtype_, rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), + "192.0.2.100")); + EXPECT_NO_THROW(updater_->deleteRRset(*rrset_)); +} + +TEST_F(DatabaseClientTest, deleteAfterCommit) { + updater_ = client_->startUpdateZone(zname_, false); + updater_->commit(); + EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); +} + +TEST_F(DatabaseClientTest, deleteEmptyRRset) { + updater_ = client_->startUpdateZone(zname_, false); + rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); + EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); +} } From 1d907966f7f0fe7089efe46d8b808d9115f0d167 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 26 Aug 2011 16:06:49 -0700 Subject: [PATCH 649/974] [1068] documented DataSourceClient::getUpdater() (renamed from startUpdateZone() to be more consistent with getIterator()). --- doc/Doxyfile | 4 +- src/lib/datasrc/client.h | 69 +++++++++++++++---- src/lib/datasrc/database.cc | 4 +- src/lib/datasrc/database.h | 4 +- src/lib/datasrc/memory_datasrc.cc | 2 +- src/lib/datasrc/memory_datasrc.h | 4 +- src/lib/datasrc/tests/client_unittest.cc | 2 +- src/lib/datasrc/tests/database_unittest.cc | 48 ++++++------- .../datasrc/tests/memory_datasrc_unittest.cc | 2 +- 9 files changed, 91 insertions(+), 48 deletions(-) diff --git a/doc/Doxyfile b/doc/Doxyfile index ceb806ff5c..71b0738fcc 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -568,8 +568,8 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../src/lib/cc ../src/lib/config \ - ../src/lib/cryptolink ../src/lib/dns ../src/lib/datasrc \ +INPUT = ../src/lib/exceptions ../src/lib/cc \ + ../src/lib/config ../src/lib/cryptolink ../src/lib/dns ../src/lib/datasrc \ ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \ ../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \ ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \ diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index 54694815a1..6a7ae046bd 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -80,8 +80,8 @@ typedef boost::shared_ptr ZoneIteratorPtr; /// disruption with a naive copy it's prohibited explicitly. For the expected /// usage of the client classes the restriction should be acceptable. /// -/// \todo This class is not complete. It needs more factory methods, for -/// accessing the whole zone, updating it, loading it, etc. +/// \todo This class is still not complete. It will need more factory methods, +/// e.g. for (re)loading a zone. class DataSourceClient : boost::noncopyable { public: /// \brief A helper structure to represent the search result of @@ -181,19 +181,64 @@ public: "Data source doesn't support iteration"); } - /// TBD + /// Return an updater to make updates to a specific zone. /// - /// We allow having a read-only data source. For such data source - /// this method will result in a NotImplemented exception. + /// The RR class of the zone is the one that the client is expected to + /// handle (see the detailed description of this class). /// - /// To avoid throwing the exception accidentally with a lazy + /// If the specified zone is not found via the client, a NULL pointer + /// will be returned; in other words a completely new zone cannot be + /// created using an updater. It must be created beforehand (even if + /// it's an empty placeholder) in a way specific to the underlying data + /// source. + /// + /// Conceptually, the updater will trigger a separate transaction for + /// subsequent updates to the zone within the context of the updater + /// (the actual implementation of the "transaction" may vary for the + /// specific underlying data source). Until \c commit() is performed + /// on the updater, the intermediate updates won't affect the results + /// of other methods (and the result of the object's methods created + /// by other factory methods). Likewise, if the updater is destructed + /// without performing \c commit(), the intermediate updates will be + /// effectively canceled and will never affect other methods. + /// + /// If the underlying data source allows concurrent updates, this method + /// can be called multiple times while the previously returned updater(s) + /// are still active. In this case each updater triggers a different + /// "transaction". Normally it would be for different zones for such a + /// case as handling multiple incoming AXFR streams concurrently, but + /// this interface does not even prohibit an attempt of getting more than + /// one updater for the same zone, as long as the underlying data source + /// allows such an operation (and any conflict resolution is left to the + /// specific derived class implementation). + /// + /// If \c replace is true, any existing RRs of the zone will be + /// deleted on successful completion of updates (after \c commit() on + /// the updater); if it's false, the existing RRs will be + /// intact unless explicitly deleted by \c deleteRRset() on the updater. + /// + /// A data source can be "read only" or can prohibit partial updates. + /// In such cases this method will result in an \c isc::NotImplemented + /// exception unconditionally or when \c replace is false). + /// + /// \note To avoid throwing the exception accidentally with a lazy /// implementation, we still keep this method pure virtual without - /// an implementation. All derived classes must explicitly write the - /// definition of this method, even if it simply throws the NotImplemented - /// exception. - virtual ZoneUpdaterPtr startUpdateZone(const isc::dns::Name& name, - bool replace) - const = 0; + /// an implementation. All derived classes must explicitly define this + /// method, even if it simply throws the NotImplemented exception. + /// + /// \exception NotImplemented The underlying data source does not support + /// updates. + /// \exception DataSourceError Internal error in the underlying data + /// source. + /// \exception std::bad_alloc Resource allocation failure. + /// + /// \param name The zone name to be updated + /// \param replace Whether to delete existing RRs before making updates + /// + /// \return A pointer to the updater; it will be NULL if the specified + /// zone isn't found. + virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name, + bool replace) const = 0; }; } } diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 4bf20ad981..0cff8c0dc6 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -609,9 +609,7 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const { } ZoneUpdaterPtr -DatabaseClient::startUpdateZone(const isc::dns::Name& name, - bool replace) const -{ +DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace) const { shared_ptr update_accessor(accessor_->clone()); const std::pair zone(update_accessor->startUpdateZone( name.toText(), replace)); diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index bc87fd6240..1eed27b26f 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -701,8 +701,8 @@ public: virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const; /// TBD - virtual ZoneUpdaterPtr startUpdateZone(const isc::dns::Name& name, - bool replace) const; + virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name, + bool replace) const; private: /// \brief The RR class that this client handles. diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 5db31639ec..630b1c005e 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -796,7 +796,7 @@ InMemoryClient::getIterator(const Name& name) const { } ZoneUpdaterPtr -InMemoryClient::startUpdateZone(const isc::dns::Name&, bool) const { +InMemoryClient::getUpdater(const isc::dns::Name&, bool) const { isc_throw(isc::NotImplemented, "Update attempt on in memory data source"); } } // end of namespace datasrc diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index b5ae859acb..be8cb8f793 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -268,8 +268,8 @@ public: /// In-memory data source is read-only, so this derived method will /// result in a NotImplemented (once merged) exception. - virtual ZoneUpdaterPtr startUpdateZone(const isc::dns::Name& name, - bool replace) const; + virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name, + bool replace) const; private: // TODO: Do we still need the PImpl if nobody should manipulate this class diff --git a/src/lib/datasrc/tests/client_unittest.cc b/src/lib/datasrc/tests/client_unittest.cc index afa78de048..5b2c91ab51 100644 --- a/src/lib/datasrc/tests/client_unittest.cc +++ b/src/lib/datasrc/tests/client_unittest.cc @@ -32,7 +32,7 @@ public: virtual FindResult findZone(const isc::dns::Name&) const { return (FindResult(result::NOTFOUND, ZoneFinderPtr())); } - virtual ZoneUpdaterPtr startUpdateZone(const isc::dns::Name&, bool) const { + virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name&, bool) const { return (ZoneUpdaterPtr()); } }; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 5a6563838c..83a54f0917 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1503,7 +1503,7 @@ TEST_F(DatabaseClientTest, getOrigin) { } TEST_F(DatabaseClientTest, updaterFinder) { - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); ASSERT_TRUE(updater_); // If this update isn't replacing the zone, the finder should work @@ -1519,7 +1519,7 @@ TEST_F(DatabaseClientTest, updaterFinder) { // When replacing the zone, the updater's finder shouldn't see anything // in the zone until something is added. updater_.reset(); - updater_ = client_->startUpdateZone(zname_, true); + updater_ = client_->getUpdater(zname_, true); ASSERT_TRUE(updater_); EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( updater_->getFinder()).zone_id()); @@ -1536,7 +1536,7 @@ TEST_F(DatabaseClientTest, flushZone) { // start update in the replace mode. the normal finder should still // be able to see the record, but the updater's finder shouldn't. - updater_ = client_->startUpdateZone(zname_, true); + updater_ = client_->getUpdater(zname_, true); setUpdateAccessor(); EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); @@ -1557,7 +1557,7 @@ TEST_F(DatabaseClientTest, updateCancel) { ZoneFinderPtr finder = client_->findZone(zname_).zone_finder; EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); - updater_ = client_->startUpdateZone(zname_, true); + updater_ = client_->getUpdater(zname_, true); setUpdateAccessor(); EXPECT_EQ(ZoneFinder::NXDOMAIN, updater_->getFinder().find(qname_, qtype_).code); @@ -1574,14 +1574,14 @@ TEST_F(DatabaseClientTest, updateCancel) { TEST_F(DatabaseClientTest, duplicateCommit) { // duplicate commit. should result in exception. - updater_ = client_->startUpdateZone(zname_, true); + updater_ = client_->getUpdater(zname_, true); updater_->commit(); EXPECT_THROW(updater_->commit(), DataSourceError); } TEST_F(DatabaseClientTest, addRRsetToNewZone) { // Add a single RRset to a fresh empty zone - updater_ = client_->startUpdateZone(zname_, true); + updater_ = client_->getUpdater(zname_, true); updater_->addRRset(*rrset_); expected_rdatas_.clear(); @@ -1595,7 +1595,7 @@ TEST_F(DatabaseClientTest, addRRsetToNewZone) { // Similar to the previous case, but with RRSIG updater_.reset(); - updater_ = client_->startUpdateZone(zname_, true); + updater_ = client_->getUpdater(zname_, true); updater_->addRRset(*rrset_); updater_->addRRset(*rrsigset_); @@ -1620,7 +1620,7 @@ TEST_F(DatabaseClientTest, addRRsetToCurrentZone) { // Similar to the previous test, but not replacing the existing data. shared_ptr finder(getFinder()); - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->addRRset(*rrset_); // We should see both old and new data. @@ -1645,7 +1645,7 @@ TEST_F(DatabaseClientTest, addRRsetToCurrentZone) { TEST_F(DatabaseClientTest, addMultipleRRs) { // Similar to the previous case, but the added RRset contains multiple // RRs. - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), "192.0.2.3")); updater_->addRRset(*rrset_); @@ -1665,7 +1665,7 @@ TEST_F(DatabaseClientTest, addRRsetOfLargerTTL) { // Similar to the previous one, but the TTL of the added RRset is larger // than that of the existing record. The finder should use the smaller // one. - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); rrset_->setTTL(RRTTL(7200)); updater_->addRRset(*rrset_); @@ -1683,7 +1683,7 @@ TEST_F(DatabaseClientTest, addRRsetOfLargerTTL) { TEST_F(DatabaseClientTest, addRRsetOfSmallerTTL) { // Similar to the previous one, but the added RRset has a smaller TTL. // The added TTL should be used by the finder. - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); rrset_->setTTL(RRTTL(1800)); updater_->addRRset(*rrset_); @@ -1703,7 +1703,7 @@ TEST_F(DatabaseClientTest, addSameRR) { // Currently the add interface doesn't try to suppress the duplicate, // neither does the finder. We may want to revisit it in future versions. - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), "192.0.2.1")); @@ -1720,7 +1720,7 @@ TEST_F(DatabaseClientTest, addSameRR) { } TEST_F(DatabaseClientTest, addDeviantRR) { - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); // RR class mismatch. This should be detected and rejected. rrset_.reset(new RRset(qname_, RRClass::CH(), RRType::TXT(), rrttl_)); @@ -1748,13 +1748,13 @@ TEST_F(DatabaseClientTest, addDeviantRR) { } TEST_F(DatabaseClientTest, addEmptyRRset) { - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); } TEST_F(DatabaseClientTest, addAfterCommit) { - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->commit(); EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); } @@ -1767,7 +1767,7 @@ TEST_F(DatabaseClientTest, deleteRRset) { "192.0.2.1")); // Delete one RR from an RRset - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->deleteRRset(*rrset_); // Delete the only RR of a name @@ -1826,7 +1826,7 @@ TEST_F(DatabaseClientTest, deleteRRsetToNXDOMAIN) { rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), "www.example.org")); - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->deleteRRset(*rrset_); { SCOPED_TRACE("delete RRset to NXDOMAIN"); @@ -1843,7 +1843,7 @@ TEST_F(DatabaseClientTest, deleteMultipleRRs) { rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), "2001:db8::2")); - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->deleteRRset(*rrset_); { @@ -1864,7 +1864,7 @@ TEST_F(DatabaseClientTest, partialDelete) { // deleteRRset should succeed "silently", and subsequent find() should // find the remaining RR. - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->deleteRRset(*rrset_); { SCOPED_TRACE("partial delete"); @@ -1878,7 +1878,7 @@ TEST_F(DatabaseClientTest, partialDelete) { TEST_F(DatabaseClientTest, deleteNoMatch) { // similar to the previous test, but there's not even a match in the // specified RRset. Essentially there's no difference in the result. - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->deleteRRset(*rrset_); { SCOPED_TRACE("delete no match"); @@ -1894,7 +1894,7 @@ TEST_F(DatabaseClientTest, deleteWithDifferentTTL) { rrset_.reset(new RRset(qname_, qclass_, qtype_, RRTTL(1800))); rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), "192.0.2.1")); - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->deleteRRset(*rrset_); { SCOPED_TRACE("delete RRset with a different TTL"); @@ -1905,7 +1905,7 @@ TEST_F(DatabaseClientTest, deleteWithDifferentTTL) { } TEST_F(DatabaseClientTest, deleteDeviantRR) { - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); // RR class mismatch. This should be detected and rejected. rrset_.reset(new RRset(qname_, RRClass::CH(), RRType::TXT(), rrttl_)); @@ -1922,13 +1922,13 @@ TEST_F(DatabaseClientTest, deleteDeviantRR) { } TEST_F(DatabaseClientTest, deleteAfterCommit) { - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); updater_->commit(); EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); } TEST_F(DatabaseClientTest, deleteEmptyRRset) { - updater_ = client_->startUpdateZone(zname_, false); + updater_ = client_->getUpdater(zname_, false); rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); } diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index cefa33e987..a926935198 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -198,7 +198,7 @@ TEST_F(InMemoryClientTest, getZoneCount) { } TEST_F(InMemoryClientTest, startUpdateZone) { - EXPECT_THROW(memory_client.startUpdateZone(Name("example.org"), false), + EXPECT_THROW(memory_client.getUpdater(Name("example.org"), false), isc::NotImplemented); } From 2ec9338d84714ea670ee888f1edf5a4ad220ea9a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 27 Aug 2011 13:19:06 -0700 Subject: [PATCH 650/974] [1068] overall documentation updates; also hid the database updater implementation completely within .cc as even the class name doesn't have to be visible. --- src/lib/datasrc/database.cc | 97 +++++++++------ src/lib/datasrc/database.h | 68 +++++----- src/lib/datasrc/sqlite3_accessor.h | 3 +- src/lib/datasrc/zone.h | 192 ++++++++++++++++++++++++++--- 4 files changed, 275 insertions(+), 85 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 0cff8c0dc6..c7564f4d55 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -608,49 +608,54 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const { return (ZoneIteratorPtr(new DatabaseIterator(context, RRClass::IN()))); } -ZoneUpdaterPtr -DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace) const { - shared_ptr update_accessor(accessor_->clone()); - const std::pair zone(update_accessor->startUpdateZone( - name.toText(), replace)); - if (!zone.first) { - return (ZoneUpdaterPtr()); - } - - return (ZoneUpdaterPtr(new Updater(update_accessor, zone.second, - name, rrclass_))); -} - -DatabaseClient::Updater::Updater(shared_ptr accessor, - int zone_id, const Name& zone_name, - const RRClass& zone_class) : - committed_(false), accessor_(accessor), zone_id_(zone_id), - db_name_(accessor->getDBName()), zone_name_(zone_name.toText()), - zone_class_(zone_class), - finder_(new Finder(accessor_, zone_id_, zone_name)) -{ - logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED) - .arg(zone_name_).arg(zone_class_).arg(db_name_); -} - -DatabaseClient::Updater::~Updater() { - if (!committed_) { - accessor_->rollbackUpdateZone(); - logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK) +// +// Zone updater using some database system as the underlying data source. +// +class DatabaseUpdater : public ZoneUpdater { +public: + DatabaseUpdater(shared_ptr accessor, int zone_id, + const Name& zone_name, const RRClass& zone_class) : + committed_(false), accessor_(accessor), zone_id_(zone_id), + db_name_(accessor->getDBName()), zone_name_(zone_name.toText()), + zone_class_(zone_class), + finder_(new DatabaseClient::Finder::Finder(accessor_, zone_id_, + zone_name)) + { + logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED) .arg(zone_name_).arg(zone_class_).arg(db_name_); } - logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED) - .arg(zone_name_).arg(zone_class_).arg(db_name_); -} + virtual ~DatabaseUpdater() { + if (!committed_) { + accessor_->rollbackUpdateZone(); + logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK) + .arg(zone_name_).arg(zone_class_).arg(db_name_); + } -ZoneFinder& -DatabaseClient::Updater::getFinder() { - return (*finder_); -} + logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED) + .arg(zone_name_).arg(zone_class_).arg(db_name_); + } + + virtual ZoneFinder& getFinder() { return (*finder_); } + + virtual void addRRset(const RRset& rrset); + virtual void deleteRRset(const RRset& rrset); + virtual void commit(); + +private: + bool committed_; + shared_ptr accessor_; + const int zone_id_; + const string db_name_; + const string zone_name_; + const RRClass zone_class_; + boost::scoped_ptr finder_; + string add_columns_[DatabaseAccessor::ADD_COLUMN_COUNT]; + string del_params_[DatabaseAccessor::DEL_PARAM_COUNT]; +}; void -DatabaseClient::Updater::addRRset(const RRset& rrset) { +DatabaseUpdater::addRRset(const RRset& rrset) { if (committed_) { isc_throw(DataSourceError, "Add attempt after commit to zone: " << zone_name_ << "/" << zone_class_); @@ -691,7 +696,7 @@ DatabaseClient::Updater::addRRset(const RRset& rrset) { } void -DatabaseClient::Updater::deleteRRset(const RRset& rrset) { +DatabaseUpdater::deleteRRset(const RRset& rrset) { if (committed_) { isc_throw(DataSourceError, "Delete attempt after commit on zone: " << zone_name_ << "/" << zone_class_); @@ -719,7 +724,7 @@ DatabaseClient::Updater::deleteRRset(const RRset& rrset) { } void -DatabaseClient::Updater::commit() { +DatabaseUpdater::commit() { if (committed_) { isc_throw(DataSourceError, "Duplicate commit attempt for " << zone_name_ << "/" << zone_class_ << " on " @@ -736,5 +741,19 @@ DatabaseClient::Updater::commit() { logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_COMMIT) .arg(zone_name_).arg(zone_class_).arg(db_name_); } + +// The updater factory +ZoneUpdaterPtr +DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace) const { + shared_ptr update_accessor(accessor_->clone()); + const std::pair zone(update_accessor->startUpdateZone( + name.toText(), replace)); + if (!zone.first) { + return (ZoneUpdaterPtr()); + } + + return (ZoneUpdaterPtr(new DatabaseUpdater(update_accessor, zone.second, + name, rrclass_))); +} } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 1eed27b26f..82918ac06b 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -429,7 +429,33 @@ public: /// to the method or internal database error. virtual void rollbackUpdateZone() = 0; - /// TBD + /// Clone the accessor with the same configuration. + /// + /// Each derived class implementation of this method will create a new + /// accessor of the same derived class with the same configuration + /// (such as the database server address) as that of the caller object + /// and return it. + /// + /// Note that other internal states won't be copied to the new accessor + /// even though the name of "clone" may indicate so. For example, even + /// if the calling accessor is in the middle of a update transaction, + /// the new accessor will not start a transaction to trace the same + /// updates. + /// + /// The intended use case of cloning is to create a separate context + /// where a specific set of database operations can be performed + /// independently from the original accessor. The updater will use it + /// so that multiple updaters can be created concurrently even if the + /// underlying database system doesn't allow running multiple transactions + /// in a single database connection. + /// + /// The underlying database system may not support the functionality + /// that would be needed to implement this method. For example, it + /// may not allow a single thread (or process) to have more than one + /// database connections. In such a case the derived class implementation + /// should throw a \c DataSourceError exception. + /// + /// \return A shared pointer to the cloned accessor. virtual boost::shared_ptr clone() = 0; /** @@ -466,18 +492,18 @@ public: /** * \brief Constructor * - * It initializes the client with a database. + * It initializes the client with a database via the given accessor. * - * \exception isc::InvalidParameter if database is NULL. It might throw + * \exception isc::InvalidParameter if accessor is NULL. It might throw * standard allocation exception as well, but doesn't throw anything else. * * \param rrclass The RR class of the zones that this client will handle. - * \param database The database to use to get data. As the parameter - * suggests, the client takes ownership of the database and will - * delete it when itself deleted. + * \param accessor The accessor to the database to use to get data. + * As the parameter suggests, the client takes ownership of the accessor + * and will delete it when itself deleted. */ DatabaseClient(isc::dns::RRClass rrclass, - boost::shared_ptr database); + boost::shared_ptr accessor); /** * \brief Corresponding ZoneFinder implementation @@ -642,29 +668,6 @@ public: bool hasSubdomains(const std::string& name); }; - class Updater : public ZoneUpdater { - public: - Updater(boost::shared_ptr database, int zone_id, - const isc::dns::Name& zone_name, - const isc::dns::RRClass& zone_class); - ~Updater(); - virtual ZoneFinder& getFinder(); - virtual void addRRset(const isc::dns::RRset& rrset); - virtual void deleteRRset(const isc::dns::RRset& rrset); - virtual void commit(); - - private: - bool committed_; - boost::shared_ptr accessor_; - const int zone_id_; - std::string db_name_; - const std::string zone_name_; - const isc::dns::RRClass zone_class_; - boost::scoped_ptr finder_; - std::string add_columns_[DatabaseAccessor::ADD_COLUMN_COUNT]; - std::string del_params_[DatabaseAccessor::DEL_PARAM_COUNT]; - }; - /** * \brief Find a zone in the database * @@ -700,7 +703,10 @@ public: */ virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const; - /// TBD + /// This implementation internally clones the accessor from the one + /// used in the client and starts a separate transaction using the cloned + /// accessor. The returned updater will be able to work separately from + /// the original client. virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name, bool replace) const; diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 5096379dee..fae249b402 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -89,7 +89,8 @@ public: */ ~SQLite3Accessor(); - /// TBD + /// This implementation internally opens a new sqlite3 database for the + /// same file name specified in the constructor of the original accessor. virtual boost::shared_ptr clone(); /** diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index a2648993d6..acbb4b568c 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -210,42 +210,206 @@ typedef boost::shared_ptr ZoneFinderPtr; typedef boost::shared_ptr ConstZoneFinderPtr; /// The base class to make updates to a single zone. +/// +/// On construction, each derived class object will start a "transaction" +/// for making updates to a specific zone (this means a constructor of +/// a derived class would normally take parameters to identify the zone +/// to be updated). The underlying realization of a "transaction" will defer +/// for different derived classes; if it uses a general purpose database +/// as a backend, it will involve performing some form of "begin transaction" +/// statement for the database. +/// +/// Updates (adding or deleting RRs) are made via \c addRRset() and +/// \c deleteRRset() methods. Until the \c commit() method is called the +/// changes are local to the updater object. For example, they won't be +/// visible via a \c ZoneFinder object except the one returned by the +/// updater's own \c getFinder() method. The \c commit() completes the +/// transaction and makes the changes visible to others. +/// +/// This class does not provide an explicit "rollback" interface. If +/// something wrong or unexpected happens during the updates and the +/// caller wants to cancel the intermediate updates, the caller should +/// simply destruct the updater object without calling \c commit(). +/// The destructor is supposed to perform the "rollback" operation, +/// depending on the internal details of the derived class. +/// +/// \note This initial implementation provides a quite simple interface of +/// adding and deleting RRs (see the description of the related methods). +/// It may be revisited as we gain more experiences. class ZoneUpdater { protected: + /// The default constructor. + /// + /// This is intentionally defined as protected to ensure that this base + /// class is never instantiated directly. ZoneUpdater() {} public: + /// The destructor + /// + /// Each derived class implementation must ensure that if \c commit() + /// has not been performed by the time of the call to it, then it + /// "rollbacks" the updates made via the updater so far. virtual ~ZoneUpdater() {} - /// TBD + /// Return a finder for the zone being updated. /// - /// The finder is not expected to provide meaningful data once commit() - /// was performed. + /// The returned finder provides the functionalities of \c ZoneFinder + /// for the zone as updates are made via the updater. That is, before + /// making any update, the finder will be able to find all RRsets that + /// exist in the zone at the time the updater is created. If RRsets + /// are added or deleted via \c addRRset() or \c deleteRRset(), + /// this finder will find the added ones or miss the deleted ones + /// respectively. + /// + /// The finder returned by this method is effective only while the updates + /// are performed, i.e., from the construction of the corresponding + /// updater until \c commit() is performed or the updater is destructed + /// without commit. The result of a subsequent call to this method (or + /// the use of the result) after that is undefined. + /// + /// \return A reference to a \c ZoneFinder for the updated zone virtual ZoneFinder& getFinder() = 0; - /// TBD + /// Add an RRset to a zone via the updater /// - /// Notes about unexpected input: class mismatch will be rejected. - /// The owner name isn't checked; it's the caller's responsibility. + /// This may be revisited in a future version, but right now the intended + /// behavior of this method is simple: It "naively" adds the specified + /// RRset to the zone specified on creation of the updater. + /// It performs minimum level of validation on the specified RRset: + /// - Whether the RR class is identical to that for the zone to be updated + /// - Whether the RRset is not empty, i.e., it has at least one RDATA /// - /// Open issues: we may eventually want to return result values such as - /// there's a duplicate, etc. + /// and otherwise does not check any oddity. For example, it doesn't + /// check whether the owner name of the specified RRset is a subdomain + /// of the zone's origin; it doesn't care whether or not there is already + /// an RRset of the same name and RR type in the zone, and if there is, + /// whether any of the existing RRs have duplicate RDATA with the added + /// ones. If these conditions matter the calling application must examine + /// the existing data beforehand using the \c ZoneFinder returned by + /// \c getFinder(). /// - /// The added RRset must not be empty (i.e., it must have at least one - /// RDATA). + /// Conceptually, on successful call to this method, the zone will have + /// the specified RRset, and if there is already an RRset of the same + /// name and RR type, these two sets will be "merged". "Merged" means + /// that a subsequent call to \c ZoneFinder::find() for the name and type + /// will result in success and the returned RRset will contain all + /// previously existing and newly added RDATAs with the TTL being the + /// minimum of the two RRsets. The underlying representation of the + /// "merged" RRsets may vary depending on the characteristic of the + /// underlying data source. For example, if it uses a general purpose + /// database that stores each RR of the same RRset separately, it may + /// simply be a larger sets of RRs based on both the existing and added + /// RRsets; the TTLs of the RRs may be different within the database, and + /// there may even be duplicate RRs in different database rows. As long + /// as the RRset returned via \c ZoneFinder::find() conforms to the + /// concept of "merge", the actual internal representation is up to the + /// implementation. /// - /// This method must not be called once commit() is performed. + /// This method must not be called once commit() is performed. If it + /// calls after \c commit() the implementation must throw a + /// \c DataSourceError exception. + /// + /// \todo As noted above we may have to revisit the design details as we + /// gain experiences: + /// + /// - we may want to check (and maybe reject) if there is already a + /// duplicate RR (that has the same RDATA). + /// - we may want to check (and maybe reject) if there is already an + /// RRset of the same name and RR type with different TTL + /// - we may even want to check if there is already any RRset of the + /// same name and RR type. + /// - we may want to add an "options" parameter that can control the + /// above points + /// - we may want to have this method return a value containing the + /// information on whether there's a duplicate, etc. + /// + /// \exception DataSourceError Called after \c commit(), RRset is invalid + /// (see above), internal data source error + /// \exception std::bad_alloc Resource allocation failure + /// + /// \param rrset The RRset to be added virtual void addRRset(const isc::dns::RRset& rrset) = 0; - /// TBD + /// Delete an RRset from a zone via the updater /// - /// how to handle TTL? + /// Like \c addRRset(), the detailed semantics and behavior of this method + /// may have to be revisited in a future version. The following are + /// based on the initial implementation decisions. + /// + /// On successful completion of this method, it will remove from the zone + /// the RRs of the specified owner name and RR type that match one of + /// the RDATAs of the specified RRset. There are several points to be + /// noted: + /// - Existing RRs that don't match any of the specified RDATAs will + /// remain in the zone. + /// - Any RRs of the specified RRset that doesn't exist in the zone will + /// simply be ignored; the implementation of this method is not supposed + /// to check that condition. + /// - The TTL of the RRset is ignored; matching is only performed by + /// the owner name, RR type and RDATA + /// + /// Ignoring the TTL may not look sensible, but it's based on the + /// observation that it will result in more intuitive result, especially + /// when the underlying data source is a general purpose database. + /// See also \c DatabaseAccessor::deleteRecordInZone() on this point. + /// It also matches the dynamic update protocol (RFC2136), where TTLs + /// are ignored when deleting RRs. + /// + /// \note Since the TTL is ignored, this method could take the RRset + /// to be deleted as a tuple of name, RR type, and a list of RDATAs. + /// But in practice, it's quite likely that the caller has the RRset + /// in the form of the \c RRset object (e.g., extracted from a dynamic + /// update request message), so this interface would rather be more + /// convenient. If it turns out not to be true we can change or extend + /// the method signature. + /// + /// This method performs minimum level of validation on the specified + /// RRset: + /// - Whether the RR class is identical to that for the zone to be updated + /// - Whether the RRset is not empty, i.e., it has at least one RDATA + /// + /// This method must not be called once commit() is performed. If it + /// calls after \c commit() the implementation must throw a + /// \c DataSourceError exception. + /// + /// \todo As noted above we may have to revisit the design details as we + /// gain experiences: + /// + /// - we may want to check (and maybe reject) if some or all of the RRs + /// for the specified RRset don't exist in the zone + /// - we may want to allow an option to "delete everything" for specified + /// name and/or specified name + RR type. + /// - as mentioned above, we may want to include the TTL in matching the + /// deleted RRs + /// - we may want to add an "options" parameter that can control the + /// above points + /// - we may want to have this method return a value containing the + /// information on whether there's any RRs that are specified but don't + /// exit, the number of actually deleted RRs, etc. + /// + /// \exception DataSourceError Called after \c commit(), RRset is invalid + /// (see above), internal data source error + /// \exception std::bad_alloc Resource allocation failure + /// + /// \param rrset The RRset to be deleted virtual void deleteRRset(const isc::dns::RRset& rrset) = 0; - /// TBD + /// Commit the updates made in the updater to the zone + /// + /// This method completes the "transaction" started at the creation + /// of the updater. After successful completion of this method, the + /// updates will be visible outside the scope of the updater. + /// The actual internal behavior will defer for different derived classes. + /// For a derived class with a general purpose database as a backend, + /// for example, this method would perform a "commit" statement for the + /// database. /// /// This operation can only be performed at most once. A duplicate call /// must result in a DatasourceError exception. + /// + /// \exception DataSourceError Duplicate call of the method, + /// internal data source error virtual void commit() = 0; }; From 9c62a36b0ebf9ff4ef3dad1f4d91195d301348ed Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 27 Aug 2011 17:08:44 -0500 Subject: [PATCH 651/974] [1068] removed make rules for a currently-unused file --- src/lib/datasrc/tests/testdata/Makefile.am | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib/datasrc/tests/testdata/Makefile.am b/src/lib/datasrc/tests/testdata/Makefile.am index ba2750203a..6a35fe3c63 100644 --- a/src/lib/datasrc/tests/testdata/Makefile.am +++ b/src/lib/datasrc/tests/testdata/Makefile.am @@ -1,5 +1 @@ -BUILT_SOURCES = rwtest.sqlite3.copied CLEANFILES = *.copied - -rwtest.sqlite3.copied: rwtest.sqlite3 - cp $(srcdir)/rwtest.sqlite3 $@ From 7980a6c8e598d34f5f733f5c6c3ca83c0a0f1187 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 29 Aug 2011 21:20:13 +0200 Subject: [PATCH 652/974] [1068] one missing point: have the DatabaseUpdater's destructor catch catch exception in rollback explicitly. added a test case for that. --- src/lib/datasrc/database.cc | 17 ++++++++++++++--- src/lib/datasrc/datasrc_messages.mes | 13 +++++++++++++ src/lib/datasrc/sqlite3_accessor.cc | 3 +-- src/lib/datasrc/tests/database_unittest.cc | 20 ++++++++++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index c7564f4d55..db1d0fb07a 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -627,9 +627,20 @@ public: virtual ~DatabaseUpdater() { if (!committed_) { - accessor_->rollbackUpdateZone(); - logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK) - .arg(zone_name_).arg(zone_class_).arg(db_name_); + try { + accessor_->rollbackUpdateZone(); + logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK) + .arg(zone_name_).arg(zone_class_).arg(db_name_); + } catch (const DataSourceError& e) { + // We generally expect that rollback always succeeds, and + // it should in fact succeed in a way we execute it. But + // as the public API allows rollbackUpdateZone() to fail and + // throw, we should expect it. Obviously we cannot re-throw + // it. The best we can do is to log it as a critical error. + logger.error(DATASRC_DATABASE_UPDATER_ROLLBACKFAIL) + .arg(zone_name_).arg(zone_class_).arg(db_name_) + .arg(e.what()); + } } logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED) diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index 75bcc6a811..efb88fdfe9 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -608,6 +608,19 @@ the changes. The intermediate changes made through the updater won't be applied to the underlying database. The zone name, its class, and the underlying database name are shown in the log message. +%DATASRC_DATABASE_UPDATER_ROLLBACKFAIL failed to roll back zone updates for '%1/%2' on %3: %4 +A zone updater is being destroyed without committing the changes to +the database, and attempts to rollback incomplete updates, but it +unexpectedly fails. The higher level implementation does not expect +it to fail, so this means either a serious operational error in the +underlying data source (such as a system failure of a database) or +software bug in the underlying data source implementation. In either +case if this message is logged the administrator should carefully +examine the underlying data source to see what exactly happens and +whether the data is still valid. The zone name, its class, and the +underlying database name as well as the error message thrown from the +database module are shown in the log message. + % DATASRC_DATABASE_UPDATER_COMMIT updates committed for '%1/%2' on %3 Debug information. A set of updates to a zone has been successfully committed to the corresponding database backend. The zone name, diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 41da113aa4..956f447363 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -111,8 +111,7 @@ public: } void exec() { - if (sqlite3_step(dbparameters_.statements_[stmt_id_]) != - SQLITE_DONE) { + if (sqlite3_step(dbparameters_.statements_[stmt_id_]) != SQLITE_DONE) { sqlite3_reset(dbparameters_.statements_[stmt_id_]); isc_throw(DataSourceError, "failed to " << desc_ << ": " << sqlite3_errmsg(dbparameters_.db_)); diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 83a54f0917..25764ea7d9 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -328,6 +328,13 @@ public: *readonly_records_ = *update_records_; } virtual void rollbackUpdateZone() { + // Special hook: if something with a name of "throw.example.org" + // has been added, trigger an imaginary unexpected event with an + // exception. + if (update_records_->count("throw.example.org.") > 0) { + isc_throw(DataSourceError, "unexpected failure in rollback"); + } + rollbacked_ = true; } virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) { @@ -1572,6 +1579,19 @@ TEST_F(DatabaseClientTest, updateCancel) { EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); } +TEST_F(DatabaseClientTest, exceptionFromRollback) { + updater_ = client_->getUpdater(zname_, true); + + rrset_.reset(new RRset(Name("throw.example.org"), qclass_, qtype_, + rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), + rrset_->getClass(), "192.0.2.1")); + updater_->addRRset(*rrset_); + // destruct without commit. The added name will result in an exception + // in the MockAccessor's rollback method. It shouldn't be propagated. + EXPECT_NO_THROW(updater_.reset()); +} + TEST_F(DatabaseClientTest, duplicateCommit) { // duplicate commit. should result in exception. updater_ = client_->getUpdater(zname_, true); From da8bfe82aa18a67b1a99fa459f48cea89ee2a41a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 29 Aug 2011 21:53:37 +0200 Subject: [PATCH 653/974] [1068] missing initialization --- src/lib/datasrc/tests/database_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 25764ea7d9..4af41b9305 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -111,7 +111,7 @@ class MockAccessor : public NopAccessor { Domains; public: - MockAccessor() { + MockAccessor() : rollbacked_(false) { readonly_records_ = &readonly_records_master_; update_records_ = &update_records_master_; empty_records_ = &empty_records_master_; From c6d2a365580709981852007cd0a9a3b32afaa5c3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 29 Aug 2011 21:54:23 +0200 Subject: [PATCH 654/974] [1068] refactored the test case: move RRs of the test zone outside the MockAccessor class definition. --- src/lib/datasrc/tests/database_unittest.cc | 130 ++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 25764ea7d9..0183fe5040 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -41,6 +41,134 @@ namespace { const int READONLY_ZONE_ID = 42; const int WRITABLE_ZONE_ID = 4200; +// Commonly used test data +const char* const TEST_RECORDS[][5] = { + // some plain data + {"www.example.org.", "A", "3600", "", "192.0.2.1"}, + {"www.example.org.", "AAAA", "3600", "", "2001:db8::1"}, + {"www.example.org.", "AAAA", "3600", "", "2001:db8::2"}, + + {"www2.example.org.", "A", "3600", "", "192.0.2.1"}, + {"www2.example.org.","AAAA", "3600", "", "2001:db8::1"}, + {"www2.example.org.", "A", "3600", "", "192.0.2.2"}, + + {"cname.example.org.", "CNAME", "3600", "", "www.example.org."}, + + // some DNSSEC-'signed' data + {"signed1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"signed1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + {"signed1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"}, + {"signed1.example.org.", "AAAA", "3600", "", "2001:db8::1"}, + {"signed1.example.org.", "AAAA", "3600", "", "2001:db8::2"}, + {"signed1.example.org.", "RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + {"signedcname1.example.org.", "CNAME", "3600", "", "www.example.org."}, + {"signedcname1.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + // special case might fail; sig is for cname, which isn't there (should be ignored) + // (ignoring of 'normal' other type is done above by www.) + {"acnamesig1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"acnamesig1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"acnamesig1.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + // let's pretend we have a database that is not careful + // about the order in which it returns data + {"signed2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"signed2.example.org.", "AAAA", "3600", "", "2001:db8::2"}, + {"signed2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"}, + {"signed2.example.org.", "A", "3600", "", "192.0.2.1"}, + {"signed2.example.org.", "RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"signed2.example.org.", "AAAA", "3600", "", "2001:db8::1"}, + + {"signedcname2.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"signedcname2.example.org.", "CNAME", "3600", "", "www.example.org."}, + + {"acnamesig2.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"acnamesig2.example.org.", "A", "3600", "", "192.0.2.1"}, + {"acnamesig2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + {"acnamesig3.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"acnamesig3.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"acnamesig3.example.org.", "A", "3600", "", "192.0.2.1"}, + + {"ttldiff1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"ttldiff1.example.org.", "A", "360", "", "192.0.2.2"}, + + {"ttldiff2.example.org.", "A", "360", "", "192.0.2.1"}, + {"ttldiff2.example.org.", "A", "3600", "", "192.0.2.2"}, + + // also add some intentionally bad data + {"badcname1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"badcname1.example.org.", "CNAME", "3600", "", "www.example.org."}, + + {"badcname2.example.org.", "CNAME", "3600", "", "www.example.org."}, + {"badcname2.example.org.", "A", "3600", "", "192.0.2.1"}, + + {"badcname3.example.org.", "CNAME", "3600", "", "www.example.org."}, + {"badcname3.example.org.", "CNAME", "3600", "", "www.example2.org."}, + + {"badrdata.example.org.", "A", "3600", "", "bad"}, + + {"badtype.example.org.", "BAD_TYPE", "3600", "", "192.0.2.1"}, + + {"badttl.example.org.", "A", "badttl", "", "192.0.2.1"}, + + {"badsig.example.org.", "A", "badttl", "", "192.0.2.1"}, + {"badsig.example.org.", "RRSIG", "3600", "", "A 5 3 3600 somebaddata 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + {"badsigtype.example.org.", "A", "3600", "", "192.0.2.1"}, + {"badsigtype.example.org.", "RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + // Data for testing delegation (with NS and DNAME) + {"delegation.example.org.", "NS", "3600", "", "ns.example.com."}, + {"delegation.example.org.", "NS", "3600", "", + "ns.delegation.example.org."}, + {"delegation.example.org.", "RRSIG", "3600", "", "NS 5 3 3600 " + "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"ns.delegation.example.org.", "A", "3600", "", "192.0.2.1"}, + {"deep.below.delegation.example.org.", "A", "3600", "", "192.0.2.1"}, + + {"dname.example.org.", "A", "3600", "", "192.0.2.1"}, + {"dname.example.org.", "DNAME", "3600", "", "dname.example.com."}, + {"dname.example.org.", "RRSIG", "3600", "", + "DNAME 5 3 3600 20000101000000 20000201000000 12345 " + "example.org. FAKEFAKEFAKE"}, + + {"below.dname.example.org.", "A", "3600", "", "192.0.2.1"}, + + // Broken NS + {"brokenns1.example.org.", "A", "3600", "", "192.0.2.1"}, + {"brokenns1.example.org.", "NS", "3600", "", "ns.example.com."}, + + {"brokenns2.example.org.", "NS", "3600", "", "ns.example.com."}, + {"brokenns2.example.org.", "A", "3600", "", "192.0.2.1"}, + + // Now double DNAME, to test failure mode + {"baddname.example.org.", "DNAME", "3600", "", "dname1.example.com."}, + {"baddname.example.org.", "DNAME", "3600", "", "dname2.example.com."}, + + // Put some data into apex (including NS) so we can check our NS + // doesn't break anything + {"example.org.", "NS", "3600", "", "ns.example.com."}, + {"example.org.", "A", "3600", "", "192.0.2.1"}, + {"example.org.", "RRSIG", "3600", "", "NS 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. FAKEFAKEFAKE"}, + + // This is because of empty domain test + {"a.b.example.org.", "A", "3600", "", "192.0.2.1"}, + + // Something for wildcards + {"*.wild.example.org.", "A", "3600", "", "192.0.2.5"}, + {"cancel.here.wild.example.org.", "AAAA", "3600", "", "2001:db8::5"}, + {"delegatedwild.example.org.", "NS", "3600", "", "ns.example.com."}, + {"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"}, + {"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"}, + {"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"}, + + {NULL, NULL, NULL, NULL, NULL}, +}; + /* * An accessor with minimum implementation, keeping the original * "NotImplemented" methods. @@ -111,7 +239,7 @@ class MockAccessor : public NopAccessor { Domains; public: - MockAccessor() { + MockAccessor() : rollbacked_(false) { readonly_records_ = &readonly_records_master_; update_records_ = &update_records_master_; empty_records_ = &empty_records_master_; From fcb2409598d37e2078076cf43794ef6c445ac22f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 29 Aug 2011 21:59:16 +0200 Subject: [PATCH 655/974] [1068] cleanup --- src/lib/datasrc/tests/database_unittest.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 4af41b9305..980b7f06b0 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -713,7 +713,6 @@ public: } // Helper methods for update tests - //bool isRollbacked(bool expected = false) const { bool isRollbacked() const { return (update_accessor_->isRollbacked()); } From 3fc53ba91b92ad40ebbf46272f57a45e3d2e3a27 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Thu, 1 Sep 2011 17:43:18 +0800 Subject: [PATCH 656/974] [trac1112] Finish the basic function of HINFO rrdata support. --- src/lib/dns/rdata/generic/hinfo_13.cc | 138 +++++++++++++++++++++- src/lib/dns/rdata/generic/hinfo_13.h | 33 +++++- src/lib/dns/tests/rdata_hinfo_unittest.cc | 52 ++++++++ 3 files changed, 215 insertions(+), 8 deletions(-) diff --git a/src/lib/dns/rdata/generic/hinfo_13.cc b/src/lib/dns/rdata/generic/hinfo_13.cc index 232eda3215..cb22107a67 100644 --- a/src/lib/dns/rdata/generic/hinfo_13.cc +++ b/src/lib/dns/rdata/generic/hinfo_13.cc @@ -20,7 +20,6 @@ #include -#include #include #include #include @@ -33,34 +32,163 @@ using namespace isc::util; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE -HINFO::HINFO(const string& type_str) { + +HINFO::HINFO(const string& hinfo_str) { + string::const_iterator input_iterator = hinfo_str.begin(); + cpu_ = getNextCharacterString(hinfo_str, input_iterator); + + skipLeftSpaces(hinfo_str, input_iterator); + + os_ = getNextCharacterString(hinfo_str, input_iterator); } HINFO::HINFO(InputBuffer& buffer, size_t rdata_len) { + cpu_ = getNextCharacterString(buffer, rdata_len); + os_ = getNextCharacterString(buffer, rdata_len); } -HINFO::HINFO(const HINFO& source) { +HINFO::HINFO(const HINFO& source): + Rdata(), cpu_(source.cpu_), os_(source.os_) +{ } std::string HINFO::toText() const { + string result; + result += "\""; + result += cpu_; + result += "\" \""; + result += os_; + result += "\""; + return result; } void HINFO::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(cpu_.size()); + buffer.writeData(cpu_.c_str(), cpu_.size()); + + buffer.writeUint8(os_.size()); + buffer.writeData(os_.c_str(), os_.size()); } void HINFO::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(cpu_.size()); + renderer.writeData(cpu_.c_str(), cpu_.size()); + + renderer.writeUint8(os_.size()); + renderer.writeData(os_.c_str(), os_.size()); } int HINFO::compare(const Rdata& other) const { - // The compare method normally begins with this dynamic cast. const HINFO& other_hinfo = dynamic_cast(other); - // ... + + if (cpu_ < other_hinfo.cpu_) { + return (-1); + } else if (cpu_ > other_hinfo.cpu_) { + return (1); + } + + if (os_ < other_hinfo.os_) { + return (-1); + } else if (os_ > other_hinfo.os_) { + return (1); + } + + return (0); } +const std::string& +HINFO::getCPU() const { + return cpu_; +} + +const std::string& +HINFO::getOS() const { + return os_; +} + +void +HINFO::skipLeftSpaces(const std::string& input_str, + std::string::const_iterator& input_iterator) +{ + if (input_iterator >= input_str.end()) { + isc_throw(InvalidRdataText, + "Invalid HINFO text format, field is missing."); + } + + if (!isspace(*input_iterator)) { + isc_throw(InvalidRdataText, + "Invalid HINFO text format, fields are not separated by space."); + } + // Skip white spaces + while (input_iterator < input_str.end() && isspace(*input_iterator)) { + ++input_iterator; + } +} + +std::string +HINFO::getNextCharacterString(const std::string& input_str, + std::string::const_iterator& input_iterator) +{ + string result; + + // If the input string only contains white-spaces, it is an invalid + // + if (input_iterator >= input_str.end()) { + isc_throw(InvalidRdataText, "Invalid HINFO text format, \ + field is missing."); + } + + // Whether the is separated with double quotes (") + bool quotes_separated = (*input_iterator == '"'); + + if (quotes_separated) { + ++input_iterator; + } + + while(input_iterator < input_str.end()){ + if (quotes_separated) { + // If the is seperated with quotes symbol and + // another quotes symbol is encountered, it is the end of the + // + if (*input_iterator == '"') { + ++input_iterator; + break; + } + } else if (*input_iterator == ' ') { + // If the is not seperated with quotes symbol, + // it is seperated with char + break; + } + + result.push_back(*input_iterator); + + ++input_iterator; + } + + if (result.size() > MAX_CHARSTRING_LEN) { + isc_throw(CharStringTooLong, "HINFO is too long"); + } + + return (result); +} + +std::string +HINFO::getNextCharacterString(InputBuffer& buffer, size_t len) { + uint8_t str_len = buffer.readUint8(); + + size_t pos = buffer.getPosition(); + if (len - pos < str_len) { + isc_throw(InvalidRdataLength, "Invalid HINFO string length"); + } + + uint8_t buf[MAX_CHARSTRING_LEN]; + buffer.readData(buf, str_len); + return (string(buf, buf + str_len)); +} // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/hinfo_13.h b/src/lib/dns/rdata/generic/hinfo_13.h index 3922da1912..6b39c9b82f 100644 --- a/src/lib/dns/rdata/generic/hinfo_13.h +++ b/src/lib/dns/rdata/generic/hinfo_13.h @@ -19,6 +19,7 @@ #include #include +#include // BEGIN_ISC_NAMESPACE @@ -32,11 +33,37 @@ public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS - /// - /// Specialized methods - /// + // HINFO specific methods + const std::string& getCPU() const; + const std::string& getOS() const; private: + /// Skip the left whitespaces of the input string + /// + /// \param input_str The input string + /// \param input_iterator From which the skipping started + void skipLeftSpaces(const std::string& input_str, + std::string::const_iterator& input_iterator); + + /// Get a from a string + /// + /// \param input_str The input string + /// \param input_iterator The iterator from which to start extracting, + /// the iterator will be updated to new position after the function + /// is returned + /// \return A std::string that contains the extracted + std::string getNextCharacterString(const std::string& input_str, + std::string::const_iterator& input_iterator); + + /// Get a from a input buffer + /// + /// \param buffer The input buffer + /// \param len The input buffer total length + /// \return A std::string that contains the extracted + std::string getNextCharacterString(util::InputBuffer& buffer, size_t len); + + std::string cpu_; + std::string os_; }; diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc index 96dfda6ff5..f80952fa16 100644 --- a/src/lib/dns/tests/rdata_hinfo_unittest.cc +++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc @@ -36,9 +36,61 @@ namespace { class Rdata_HINFO_Test : public RdataTest { }; +static uint8_t hinfo_rdata[] = {0x07,0x50,0x65,0x6e,0x74,0x69,0x75,0x6d,0x05, + 0x4c,0x69,0x6e,0x75,0x78}; static const char *hinfo_str = "\"Pentium\" \"Linux\""; +static const char *hinfo_str_small1 = "\"Lentium\" \"Linux\""; +static const char *hinfo_str_small2 = "\"Pentium\" \"Kinux\""; +static const char *hinfo_str_large1 = "\"Qentium\" \"Linux\""; +static const char *hinfo_str_large2 = "\"Pentium\" \"UNIX\""; + TEST_F(Rdata_HINFO_Test, createFromText) { HINFO hinfo(hinfo_str); + EXPECT_EQ(string("Pentium"), hinfo.getCPU()); + EXPECT_EQ(string("Linux"), hinfo.getOS()); } + +TEST_F(Rdata_HINFO_Test, createFromWire) { + InputBuffer input_buffer(hinfo_rdata, sizeof(hinfo_rdata)); + HINFO hinfo(input_buffer, sizeof(hinfo_rdata)); + EXPECT_EQ(string("Pentium"), hinfo.getCPU()); + EXPECT_EQ(string("Linux"), hinfo.getOS()); +} + +TEST_F(Rdata_HINFO_Test, toText) { + HINFO hinfo(hinfo_str); + EXPECT_EQ(hinfo_str, hinfo.toText()); +} + +TEST_F(Rdata_HINFO_Test, toWire) { + HINFO hinfo(hinfo_str); + hinfo.toWire(obuffer); + + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(), + obuffer.getLength(), hinfo_rdata, sizeof(hinfo_rdata)); +} + +TEST_F(Rdata_HINFO_Test, toWireRenderer) { + HINFO hinfo(hinfo_str); + + hinfo.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(), + obuffer.getLength(), hinfo_rdata, sizeof(hinfo_rdata)); +} + +TEST_F(Rdata_HINFO_Test, compare) { + HINFO hinfo(hinfo_str); + HINFO hinfo_small1(hinfo_str_small1); + HINFO hinfo_small2(hinfo_str_small2); + HINFO hinfo_large1(hinfo_str_large1); + HINFO hinfo_large2(hinfo_str_large2); + + EXPECT_EQ(0, hinfo.compare(HINFO(hinfo_str))); + EXPECT_EQ(1, hinfo.compare(HINFO(hinfo_str_small1))); + EXPECT_EQ(1, hinfo.compare(HINFO(hinfo_str_small2))); + EXPECT_EQ(-1, hinfo.compare(HINFO(hinfo_str_large1))); + EXPECT_EQ(-1, hinfo.compare(HINFO(hinfo_str_large2))); +} + } From 4e12574323ca3db3e985acee0540c603b2b33124 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Fri, 2 Sep 2011 11:47:17 +0800 Subject: [PATCH 657/974] [trac1112] Add more unit tests and comments --- src/lib/dns/rdata/generic/hinfo_13.cc | 6 +++--- src/lib/dns/rdata/generic/hinfo_13.h | 6 ++++++ src/lib/dns/tests/rdata_hinfo_unittest.cc | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/lib/dns/rdata/generic/hinfo_13.cc b/src/lib/dns/rdata/generic/hinfo_13.cc index cb22107a67..d18584e096 100644 --- a/src/lib/dns/rdata/generic/hinfo_13.cc +++ b/src/lib/dns/rdata/generic/hinfo_13.cc @@ -60,7 +60,7 @@ HINFO::toText() const { result += "\" \""; result += os_; result += "\""; - return result; + return (result); } void @@ -102,12 +102,12 @@ HINFO::compare(const Rdata& other) const { const std::string& HINFO::getCPU() const { - return cpu_; + return (cpu_); } const std::string& HINFO::getOS() const { - return os_; + return (os_); } void diff --git a/src/lib/dns/rdata/generic/hinfo_13.h b/src/lib/dns/rdata/generic/hinfo_13.h index 6b39c9b82f..78fee675e9 100644 --- a/src/lib/dns/rdata/generic/hinfo_13.h +++ b/src/lib/dns/rdata/generic/hinfo_13.h @@ -28,6 +28,12 @@ // BEGIN_RDATA_NAMESPACE +/// \brief \c HINFO class represents the HINFO rdata defined in +/// RFC1034, RFC1035 +/// +/// This class implements the basic interfaces inherited from the +/// \c rdata::Rdata class, and provides accessors specific to the +/// HINFO rdata. class HINFO : public Rdata { public: // BEGIN_COMMON_MEMBERS diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc index f80952fa16..c9340717c2 100644 --- a/src/lib/dns/tests/rdata_hinfo_unittest.cc +++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc @@ -51,6 +51,20 @@ TEST_F(Rdata_HINFO_Test, createFromText) { EXPECT_EQ(string("Linux"), hinfo.getOS()); } +TEST_F(Rdata_HINFO_Test, badText) { + // Fields must be seperated by spaces + EXPECT_THROW(const HINFO hinfo("\"Pentium\"\"Linux\""), InvalidRdataText); + // Field cannot be missing + EXPECT_THROW(const HINFO hinfo("Pentium"), InvalidRdataText); + // The cannot exceed 255 characters + string hinfo_str; + for (int i = 0; i < 257; ++i) { + hinfo_str += 'A'; + } + hinfo_str += " Linux"; + EXPECT_THROW(const HINFO hinfo(hinfo_str), CharStringTooLong); +} + TEST_F(Rdata_HINFO_Test, createFromWire) { InputBuffer input_buffer(hinfo_rdata, sizeof(hinfo_rdata)); HINFO hinfo(input_buffer, sizeof(hinfo_rdata)); From a0e04c0ad837b4b42caf139573f2a95c86cdac76 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 5 Sep 2011 13:36:09 +0200 Subject: [PATCH 658/974] [1068] Use type name, not constructor --- src/lib/datasrc/database.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index db1d0fb07a..cc2cc232ce 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -618,8 +618,7 @@ public: committed_(false), accessor_(accessor), zone_id_(zone_id), db_name_(accessor->getDBName()), zone_name_(zone_name.toText()), zone_class_(zone_class), - finder_(new DatabaseClient::Finder::Finder(accessor_, zone_id_, - zone_name)) + finder_(new DatabaseClient::Finder(accessor_, zone_id_, zone_name)) { logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED) .arg(zone_name_).arg(zone_class_).arg(db_name_); @@ -660,7 +659,7 @@ private: const string db_name_; const string zone_name_; const RRClass zone_class_; - boost::scoped_ptr finder_; + boost::scoped_ptr finder_; string add_columns_[DatabaseAccessor::ADD_COLUMN_COUNT]; string del_params_[DatabaseAccessor::DEL_PARAM_COUNT]; }; From 748c3e1aeb833012a19b651af7d98757a8ffc50f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 5 Sep 2011 14:39:47 +0200 Subject: [PATCH 659/974] [1068] Indentation --- src/lib/datasrc/database.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index cc2cc232ce..f27c2ce976 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -762,8 +762,8 @@ DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace) const { return (ZoneUpdaterPtr()); } - return (ZoneUpdaterPtr(new DatabaseUpdater(update_accessor, zone.second, - name, rrclass_))); + return (ZoneUpdaterPtr(new DatabaseUpdater(update_accessor, zone.second, + name, rrclass_))); } } } From 3a838eb454ed0de4f073b99e94e02014eca63a56 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 6 Sep 2011 10:36:47 +0200 Subject: [PATCH 660/974] [1176] Operator to combine FindOptions --- src/bin/auth/query.cc | 6 ++---- src/lib/datasrc/zone.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc index 803123d947..f09539998c 100644 --- a/src/bin/auth/query.cc +++ b/src/bin/auth/query.cc @@ -67,7 +67,7 @@ Query::findAddrs(ZoneFinder& zone, const Name& qname, // Find A rrset if (qname_ != qname || qtype_ != RRType::A()) { ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL, - static_cast(options | dnssec_opt_)); + options); if (a_result.code == ZoneFinder::SUCCESS) { response_.addRRset(Message::SECTION_ADDITIONAL, boost::const_pointer_cast(a_result.rrset), dnssec_); @@ -77,9 +77,7 @@ Query::findAddrs(ZoneFinder& zone, const Name& qname, // Find AAAA rrset if (qname_ != qname || qtype_ != RRType::AAAA()) { ZoneFinder::FindResult aaaa_result = - zone.find(qname, RRType::AAAA(), NULL, - static_cast(options | - dnssec_opt_)); + zone.find(qname, RRType::AAAA(), NULL, options | dnssec_opt_); if (aaaa_result.code == ZoneFinder::SUCCESS) { response_.addRRset(Message::SECTION_ADDITIONAL, boost::const_pointer_cast(aaaa_result.rrset), diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index c5e1a06eea..5d5f86fe02 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -205,6 +205,18 @@ public: //@} }; +/// \brief Operator to combine FindOptions +/// +/// We would need to manually static-cast the options if we put or +/// between them, which is undesired with bit-flag options. Therefore +/// we hide the cast here, which is the simplest solution and it still +/// provides reasonable level of type safety. +inline ZoneFinder::FindOptions operator |(ZoneFinder::FindOptions a, + ZoneFinder::FindOptions b) +{ + return (static_cast(a | b)); +} + /// \brief A pointer-like type pointing to a \c ZoneFinder object. typedef boost::shared_ptr ZoneFinderPtr; From 04b04226b726b6e1fea6bba970556b9ed5cc3446 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 6 Sep 2011 11:01:26 +0200 Subject: [PATCH 661/974] [1176] Cleanup of previous commit * Avoid infinite recursion - the operator | kind of happened to call itself by accident. * Re-added lost | dnssec_opt_. --- src/bin/auth/query.cc | 2 +- src/lib/datasrc/zone.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc index f09539998c..898fff7da6 100644 --- a/src/bin/auth/query.cc +++ b/src/bin/auth/query.cc @@ -67,7 +67,7 @@ Query::findAddrs(ZoneFinder& zone, const Name& qname, // Find A rrset if (qname_ != qname || qtype_ != RRType::A()) { ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL, - options); + options | dnssec_opt_); if (a_result.code == ZoneFinder::SUCCESS) { response_.addRRset(Message::SECTION_ADDITIONAL, boost::const_pointer_cast(a_result.rrset), dnssec_); diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 5d5f86fe02..989b250b9e 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -214,7 +214,8 @@ public: inline ZoneFinder::FindOptions operator |(ZoneFinder::FindOptions a, ZoneFinder::FindOptions b) { - return (static_cast(a | b)); + return (static_cast(static_cast(a) | + static_cast(b))); } /// \brief A pointer-like type pointing to a \c ZoneFinder object. From b557ab47f3355f5fc7d4f87dfa9e4a15e7e9f3e3 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Tue, 6 Sep 2011 17:36:51 +0800 Subject: [PATCH 662/974] [trac1112] Move related functions to character_string.h/cc files Move some duplicated code of toWire() to toWireHelper() function --- src/lib/dns/Makefile.am | 1 + src/lib/dns/character_string.cc | 94 +++++++++++++++++++ src/lib/dns/character_string.h | 46 ++++++++++ src/lib/dns/rdata/generic/hinfo_13.cc | 77 ++-------------- src/lib/dns/rdata/generic/hinfo_13.h | 23 ++--- src/lib/dns/rdata/generic/naptr_35.cc | 104 ++-------------------- src/lib/dns/rdata/generic/naptr_35.h | 20 +++++ src/lib/dns/tests/rdata_hinfo_unittest.cc | 5 ++ 8 files changed, 186 insertions(+), 184 deletions(-) create mode 100644 src/lib/dns/character_string.cc create mode 100644 src/lib/dns/character_string.h diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 4a2364190b..c823dfd44a 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -94,6 +94,7 @@ libdns___la_SOURCES += tsig.h tsig.cc libdns___la_SOURCES += tsigerror.h tsigerror.cc libdns___la_SOURCES += tsigkey.h tsigkey.cc libdns___la_SOURCES += tsigrecord.h tsigrecord.cc +libdns___la_SOURCES += character_string.h character_string.cc libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc diff --git a/src/lib/dns/character_string.cc b/src/lib/dns/character_string.cc new file mode 100644 index 0000000000..c65ddcce3b --- /dev/null +++ b/src/lib/dns/character_string.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include "character_string.h" +#include "rdata.h" + +using namespace std; +using namespace isc::dns::rdata; + +namespace isc { +namespace dns { + +std::string +characterstr::getNextCharacterString(const std::string& input_str, + std::string::const_iterator& input_iterator) +{ + string result; + + // If the input string only contains white-spaces, it is an invalid + // + if (input_iterator >= input_str.end()) { + isc_throw(InvalidRdataText, "Invalid text format, \ + field is missing."); + } + + // Whether the is separated with double quotes (") + bool quotes_separated = (*input_iterator == '"'); + + if (quotes_separated) { + ++input_iterator; + } + + while(input_iterator < input_str.end()){ + if (quotes_separated) { + // If the is seperated with quotes symbol and + // another quotes symbol is encountered, it is the end of the + // + if (*input_iterator == '"') { + // Inside a " delimited string any character can occur, except + // for a " itself, which must be quoted using \ (back slash). + if (*(input_iterator - 1) == '\\') { + // pop the '\' character + result.resize(result.size() - 1); + } else { + ++input_iterator; + // Reach the end of character string + break; + } + } + } else if (*input_iterator == ' ') { + // If the is not seperated with quotes symbol, + // it is seperated with char + break; + } + + result.push_back(*input_iterator); + + ++input_iterator; + } + + if (result.size() > MAX_CHARSTRING_LEN) { + isc_throw(CharStringTooLong, " is too long"); + } + + return (result); +} + +std::string +characterstr::getNextCharacterString(util::InputBuffer& buffer, size_t len) { + uint8_t str_len = buffer.readUint8(); + + size_t pos = buffer.getPosition(); + if (len - pos < str_len) { + isc_throw(InvalidRdataLength, "Invalid string length"); + } + + uint8_t buf[MAX_CHARSTRING_LEN]; + buffer.readData(buf, str_len); + return (string(buf, buf + str_len)); +} + +} // end of namespace dns +} // end of namespace isc diff --git a/src/lib/dns/character_string.h b/src/lib/dns/character_string.h new file mode 100644 index 0000000000..8d9d9135c8 --- /dev/null +++ b/src/lib/dns/character_string.h @@ -0,0 +1,46 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __CHARACTER_STRING_H +#define __CHARACTER_STRING_H + +#include +#include +#include + +namespace isc { +namespace dns { +namespace characterstr { + /// Get a from a string + /// + /// \param input_str The input string + /// \param input_iterator The iterator from which to start extracting, + /// the iterator will be updated to new position after the function + /// is returned + /// \return A std::string that contains the extracted + std::string getNextCharacterString(const std::string& input_str, + std::string::const_iterator& input_iterator); + + /// Get a from a input buffer + /// + /// \param buffer The input buffer + /// \param len The input buffer total length + /// \return A std::string that contains the extracted + std::string getNextCharacterString(util::InputBuffer& buffer, size_t len); + +} // namespace characterstr +} // namespace dns +} // namespace isc + +#endif // __CHARACTER_STRING_H diff --git a/src/lib/dns/rdata/generic/hinfo_13.cc b/src/lib/dns/rdata/generic/hinfo_13.cc index d18584e096..45f4209863 100644 --- a/src/lib/dns/rdata/generic/hinfo_13.cc +++ b/src/lib/dns/rdata/generic/hinfo_13.cc @@ -24,10 +24,14 @@ #include #include #include +#include +#include using namespace std; using namespace boost; using namespace isc::util; +using namespace isc::dns; +using namespace isc::dns::characterstr; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE @@ -65,20 +69,12 @@ HINFO::toText() const { void HINFO::toWire(OutputBuffer& buffer) const { - buffer.writeUint8(cpu_.size()); - buffer.writeData(cpu_.c_str(), cpu_.size()); - - buffer.writeUint8(os_.size()); - buffer.writeData(os_.c_str(), os_.size()); + toWireHelper(buffer); } void HINFO::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeUint8(cpu_.size()); - renderer.writeData(cpu_.c_str(), cpu_.size()); - - renderer.writeUint8(os_.size()); - renderer.writeData(os_.c_str(), os_.size()); + toWireHelper(renderer); } int @@ -129,66 +125,5 @@ HINFO::skipLeftSpaces(const std::string& input_str, } } -std::string -HINFO::getNextCharacterString(const std::string& input_str, - std::string::const_iterator& input_iterator) -{ - string result; - - // If the input string only contains white-spaces, it is an invalid - // - if (input_iterator >= input_str.end()) { - isc_throw(InvalidRdataText, "Invalid HINFO text format, \ - field is missing."); - } - - // Whether the is separated with double quotes (") - bool quotes_separated = (*input_iterator == '"'); - - if (quotes_separated) { - ++input_iterator; - } - - while(input_iterator < input_str.end()){ - if (quotes_separated) { - // If the is seperated with quotes symbol and - // another quotes symbol is encountered, it is the end of the - // - if (*input_iterator == '"') { - ++input_iterator; - break; - } - } else if (*input_iterator == ' ') { - // If the is not seperated with quotes symbol, - // it is seperated with char - break; - } - - result.push_back(*input_iterator); - - ++input_iterator; - } - - if (result.size() > MAX_CHARSTRING_LEN) { - isc_throw(CharStringTooLong, "HINFO is too long"); - } - - return (result); -} - -std::string -HINFO::getNextCharacterString(InputBuffer& buffer, size_t len) { - uint8_t str_len = buffer.readUint8(); - - size_t pos = buffer.getPosition(); - if (len - pos < str_len) { - isc_throw(InvalidRdataLength, "Invalid HINFO string length"); - } - - uint8_t buf[MAX_CHARSTRING_LEN]; - buffer.readData(buf, str_len); - return (string(buf, buf + str_len)); -} - // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/hinfo_13.h b/src/lib/dns/rdata/generic/hinfo_13.h index 78fee675e9..85134198b6 100644 --- a/src/lib/dns/rdata/generic/hinfo_13.h +++ b/src/lib/dns/rdata/generic/hinfo_13.h @@ -51,22 +51,17 @@ private: void skipLeftSpaces(const std::string& input_str, std::string::const_iterator& input_iterator); - /// Get a from a string + /// Helper template function for toWire() /// - /// \param input_str The input string - /// \param input_iterator The iterator from which to start extracting, - /// the iterator will be updated to new position after the function - /// is returned - /// \return A std::string that contains the extracted - std::string getNextCharacterString(const std::string& input_str, - std::string::const_iterator& input_iterator); + /// \param outputer Where to write data in + template + void toWireHelper(T& outputer) const { + outputer.writeUint8(cpu_.size()); + outputer.writeData(cpu_.c_str(), cpu_.size()); - /// Get a from a input buffer - /// - /// \param buffer The input buffer - /// \param len The input buffer total length - /// \return A std::string that contains the extracted - std::string getNextCharacterString(util::InputBuffer& buffer, size_t len); + outputer.writeUint8(os_.size()); + outputer.writeData(os_.c_str(), os_.size()); + } std::string cpu_; std::string os_; diff --git a/src/lib/dns/rdata/generic/naptr_35.cc b/src/lib/dns/rdata/generic/naptr_35.cc index 52683311c6..129bf6cb75 100644 --- a/src/lib/dns/rdata/generic/naptr_35.cc +++ b/src/lib/dns/rdata/generic/naptr_35.cc @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -28,6 +29,8 @@ using namespace std; using namespace boost; using namespace isc::util; +using namespace isc::dns; +using namespace isc::dns::characterstr; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE @@ -56,79 +59,6 @@ skipLeftSpaces(const std::string& input_str, } } -/// Get a from a string -/// -/// \param input_str The input string -/// \param input_iterator The iterator from which to start extracting, -/// the iterator will be updated to new position after the function -/// is returned -/// \return A std::string that contains the extracted -std::string -getNextCharacterString(const std::string& input_str, - std::string::const_iterator& input_iterator) -{ - string result; - - // If the input string only contains white-spaces, it is an invalid - // - if (input_iterator >= input_str.end()) { - isc_throw(InvalidRdataText, "Invalid NAPTR text format, \ - field is missing."); - } - - // Whether the is separated with double quotes (") - bool quotes_separated = (*input_iterator == '"'); - - if (quotes_separated) { - ++input_iterator; - } - - while(input_iterator < input_str.end()){ - if (quotes_separated) { - // If the is seperated with quotes symbol and - // another quotes symbol is encountered, it is the end of the - // - if (*input_iterator == '"') { - ++input_iterator; - break; - } - } else if (*input_iterator == ' ') { - // If the is not seperated with quotes symbol, - // it is seperated with char - break; - } - - result.push_back(*input_iterator); - - ++input_iterator; - } - - if (result.size() > MAX_CHARSTRING_LEN) { - isc_throw(CharStringTooLong, "NAPTR is too long"); - } - - return (result); -} - -/// Get a from a input buffer -/// -/// \param buffer The input buffer -/// \param len The input buffer total length -/// \return A std::string that contains the extracted -std::string -getNextCharacterString(InputBuffer& buffer, size_t len) { - uint8_t str_len = buffer.readUint8(); - - size_t pos = buffer.getPosition(); - if (len - pos < str_len) { - isc_throw(InvalidRdataLength, "Invalid NAPTR string length"); - } - - uint8_t buf[MAX_CHARSTRING_LEN]; - buffer.readData(buf, str_len); - return (string(buf, buf + str_len)); -} - } // Anonymous namespace NAPTR::NAPTR(InputBuffer& buffer, size_t len): @@ -194,36 +124,12 @@ NAPTR::NAPTR(const NAPTR& naptr): void NAPTR::toWire(OutputBuffer& buffer) const { - buffer.writeUint16(order_); - buffer.writeUint16(preference_); - - buffer.writeUint8(flags_.size()); - buffer.writeData(flags_.c_str(), flags_.size()); - - buffer.writeUint8(services_.size()); - buffer.writeData(services_.c_str(), services_.size()); - - buffer.writeUint8(regexp_.size()); - buffer.writeData(regexp_.c_str(), regexp_.size()); - - replacement_.toWire(buffer); + toWireHelper(buffer); } void NAPTR::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeUint16(order_); - renderer.writeUint16(preference_); - - renderer.writeUint8(flags_.size()); - renderer.writeData(flags_.c_str(), flags_.size()); - - renderer.writeUint8(services_.size()); - renderer.writeData(services_.c_str(), services_.size()); - - renderer.writeUint8(regexp_.size()); - renderer.writeData(regexp_.c_str(), regexp_.size()); - - replacement_.toWire(renderer); + toWireHelper(renderer); } string diff --git a/src/lib/dns/rdata/generic/naptr_35.h b/src/lib/dns/rdata/generic/naptr_35.h index b3015ae2be..ca16b3c9f1 100644 --- a/src/lib/dns/rdata/generic/naptr_35.h +++ b/src/lib/dns/rdata/generic/naptr_35.h @@ -46,6 +46,26 @@ public: const std::string& getRegexp() const; const Name& getReplacement() const; private: + /// Helper template function for toWire() + /// + /// \param outputer Where to write data in + template + void toWireHelper(T& outputer) const { + outputer.writeUint16(order_); + outputer.writeUint16(preference_); + + outputer.writeUint8(flags_.size()); + outputer.writeData(flags_.c_str(), flags_.size()); + + outputer.writeUint8(services_.size()); + outputer.writeData(services_.c_str(), services_.size()); + + outputer.writeUint8(regexp_.size()); + outputer.writeData(regexp_.c_str(), regexp_.size()); + + replacement_.toWire(outputer); + } + uint16_t order_; uint16_t preference_; std::string flags_; diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc index c9340717c2..c52b2a05ed 100644 --- a/src/lib/dns/tests/rdata_hinfo_unittest.cc +++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc @@ -39,6 +39,7 @@ class Rdata_HINFO_Test : public RdataTest { static uint8_t hinfo_rdata[] = {0x07,0x50,0x65,0x6e,0x74,0x69,0x75,0x6d,0x05, 0x4c,0x69,0x6e,0x75,0x78}; static const char *hinfo_str = "\"Pentium\" \"Linux\""; +static const char *hinfo_str1 = "\"Pen\\\"tium\" \"Linux\""; static const char *hinfo_str_small1 = "\"Lentium\" \"Linux\""; static const char *hinfo_str_small2 = "\"Pentium\" \"Kinux\""; @@ -49,6 +50,10 @@ TEST_F(Rdata_HINFO_Test, createFromText) { HINFO hinfo(hinfo_str); EXPECT_EQ(string("Pentium"), hinfo.getCPU()); EXPECT_EQ(string("Linux"), hinfo.getOS()); + + // Test the text with double quotes in the middle of string + HINFO hinfo1(hinfo_str1); + EXPECT_EQ(string("Pen\"tium"), hinfo1.getCPU()); } TEST_F(Rdata_HINFO_Test, badText) { From 88095bed9cbc3e39c61eb0ea7dee1646ff13ac7e Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Tue, 6 Sep 2011 17:47:21 +0800 Subject: [PATCH 663/974] [trac1112] Add more comments to character_string.h --- src/lib/dns/character_string.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/dns/character_string.h b/src/lib/dns/character_string.h index 8d9d9135c8..7961274826 100644 --- a/src/lib/dns/character_string.h +++ b/src/lib/dns/character_string.h @@ -21,6 +21,17 @@ namespace isc { namespace dns { + +// \brief Some utility functions to extract from string +// or InputBuffer +// +// is expressed in one or two ways: as a contiguous set +// of characters without interior spaces, or as a string beginning with a " +// and ending with a ". Inside a " delimited string any character can +// occur, except for a " itself, which must be quoted using \ (back slash). +// Ref. RFC1035 + + namespace characterstr { /// Get a from a string /// From fda403b09887a24403c3a90d7ad6c95288f2d641 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 6 Sep 2011 11:47:19 -0700 Subject: [PATCH 664/974] [1068] fixed a typo --- src/lib/datasrc/zone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index acbb4b568c..b04515562d 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -214,7 +214,7 @@ typedef boost::shared_ptr ConstZoneFinderPtr; /// On construction, each derived class object will start a "transaction" /// for making updates to a specific zone (this means a constructor of /// a derived class would normally take parameters to identify the zone -/// to be updated). The underlying realization of a "transaction" will defer +/// to be updated). The underlying realization of a "transaction" will differ /// for different derived classes; if it uses a general purpose database /// as a backend, it will involve performing some form of "begin transaction" /// statement for the database. From 230df584722d08705f2cb3b99940b764b1cb7865 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 6 Sep 2011 12:10:42 -0700 Subject: [PATCH 665/974] [1068] Throw an exception if an RRset is being added/deleted with RRSIG RRset. Also updated doc and tests accordingly. --- src/lib/datasrc/database.cc | 10 ++++++++++ src/lib/datasrc/tests/database_unittest.cc | 12 ++++++++++++ src/lib/datasrc/zone.h | 10 ++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index f27c2ce976..ee4d05e838 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -675,6 +675,11 @@ DatabaseUpdater::addRRset(const RRset& rrset) { << "added to " << zone_name_ << "/" << zone_class_ << ": " << rrset.toText()); } + if (rrset.getRRsig()) { + isc_throw(DataSourceError, "An RRset with RRSIG is being added to " + << zone_name_ << "/" << zone_class_ << ": " + << rrset.toText()); + } RdataIteratorPtr it = rrset.getRdataIterator(); if (it->isLast()) { @@ -716,6 +721,11 @@ DatabaseUpdater::deleteRRset(const RRset& rrset) { << "deleted from " << zone_name_ << "/" << zone_class_ << ": " << rrset.toText()); } + if (rrset.getRRsig()) { + isc_throw(DataSourceError, "An RRset with RRSIG is being deleted from " + << zone_name_ << "/" << zone_class_ << ": " + << rrset.toText()); + } RdataIteratorPtr it = rrset.getRdataIterator(); if (it->isLast()) { diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 980b7f06b0..adfc241a57 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1772,6 +1772,12 @@ TEST_F(DatabaseClientTest, addEmptyRRset) { EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); } +TEST_F(DatabaseClientTest, addRRsetWithRRSIG) { + updater_ = client_->getUpdater(zname_, false); + rrset_->addRRsig(*rrsigset_); + EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); +} + TEST_F(DatabaseClientTest, addAfterCommit) { updater_ = client_->getUpdater(zname_, false); updater_->commit(); @@ -1951,4 +1957,10 @@ TEST_F(DatabaseClientTest, deleteEmptyRRset) { rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); } + +TEST_F(DatabaseClientTest, deleteRRsetWithRRSIG) { + updater_ = client_->getUpdater(zname_, false); + rrset_->addRRsig(*rrsigset_); + EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); +} } diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index b04515562d..b8d9faef1a 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -279,6 +279,8 @@ public: /// It performs minimum level of validation on the specified RRset: /// - Whether the RR class is identical to that for the zone to be updated /// - Whether the RRset is not empty, i.e., it has at least one RDATA + /// - Whether the RRset is not associated with an RRSIG, i.e., + /// whether \c getRRsig() on the RRset returns a NULL pointer. /// /// and otherwise does not check any oddity. For example, it doesn't /// check whether the owner name of the specified RRset is a subdomain @@ -289,6 +291,12 @@ public: /// the existing data beforehand using the \c ZoneFinder returned by /// \c getFinder(). /// + /// The validation requirement on the associated RRSIG is temporary. + /// If we find it more reasonable and useful to allow adding a pair of + /// RRset and its RRSIG RRset as we gain experiences with the interface, + /// we may remove this restriction. Until then we explicitly check it + /// to prevent accidental misuse. + /// /// Conceptually, on successful call to this method, the zone will have /// the specified RRset, and if there is already an RRset of the same /// name and RR type, these two sets will be "merged". "Merged" means @@ -368,6 +376,8 @@ public: /// RRset: /// - Whether the RR class is identical to that for the zone to be updated /// - Whether the RRset is not empty, i.e., it has at least one RDATA + /// - Whether the RRset is not associated with an RRSIG, i.e., + /// whether \c getRRsig() on the RRset returns a NULL pointer. /// /// This method must not be called once commit() is performed. If it /// calls after \c commit() the implementation must throw a From 3d069e2745070bc23f14c845cb7d8116d919f0da Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 6 Sep 2011 12:39:22 -0700 Subject: [PATCH 666/974] [1068] used local arrays for columns/parameters of add/delete RRset instead of class member variables (which are now removed). also added a test to detect a bug in the previous code where the sigtype field for addRRset was incorrectly carried over after adding an RRSIG. --- src/lib/datasrc/database.cc | 27 +++++++++++----------- src/lib/datasrc/tests/database_unittest.cc | 9 ++++++++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index ee4d05e838..e17baf8e61 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -660,8 +660,6 @@ private: const string zone_name_; const RRClass zone_class_; boost::scoped_ptr finder_; - string add_columns_[DatabaseAccessor::ADD_COLUMN_COUNT]; - string del_params_[DatabaseAccessor::DEL_PARAM_COUNT]; }; void @@ -688,11 +686,12 @@ DatabaseUpdater::addRRset(const RRset& rrset) { << rrset.getType()); } - add_columns_[DatabaseAccessor::ADD_NAME] = rrset.getName().toText(); - add_columns_[DatabaseAccessor::ADD_REV_NAME] = + string columns[DatabaseAccessor::ADD_COLUMN_COUNT]; // initialized with "" + columns[DatabaseAccessor::ADD_NAME] = rrset.getName().toText(); + columns[DatabaseAccessor::ADD_REV_NAME] = rrset.getName().reverse().toText(); - add_columns_[DatabaseAccessor::ADD_TTL] = rrset.getTTL().toText(); - add_columns_[DatabaseAccessor::ADD_TYPE] = rrset.getType().toText(); + columns[DatabaseAccessor::ADD_TTL] = rrset.getTTL().toText(); + columns[DatabaseAccessor::ADD_TYPE] = rrset.getType().toText(); for (; !it->isLast(); it->next()) { if (rrset.getType() == RRType::RRSIG()) { // XXX: the current interface (based on the current sqlite3 @@ -702,11 +701,11 @@ DatabaseUpdater::addRRset(const RRset& rrset) { // the interface, but until then we have to conform to the schema. const generic::RRSIG& rrsig_rdata = dynamic_cast(it->getCurrent()); - add_columns_[DatabaseAccessor::ADD_SIGTYPE] = + columns[DatabaseAccessor::ADD_SIGTYPE] = rrsig_rdata.typeCovered().toText(); } - add_columns_[DatabaseAccessor::ADD_RDATA] = it->getCurrent().toText(); - accessor_->addRecordToZone(add_columns_); + columns[DatabaseAccessor::ADD_RDATA] = it->getCurrent().toText(); + accessor_->addRecordToZone(columns); } } @@ -734,12 +733,12 @@ DatabaseUpdater::deleteRRset(const RRset& rrset) { << rrset.getType()); } - del_params_[DatabaseAccessor::DEL_NAME] = rrset.getName().toText(); - del_params_[DatabaseAccessor::DEL_TYPE] = rrset.getType().toText(); + string params[DatabaseAccessor::DEL_PARAM_COUNT]; // initialized with "" + params[DatabaseAccessor::DEL_NAME] = rrset.getName().toText(); + params[DatabaseAccessor::DEL_TYPE] = rrset.getType().toText(); for (; !it->isLast(); it->next()) { - del_params_[DatabaseAccessor::DEL_RDATA] = - it->getCurrent().toText(); - accessor_->deleteRecordInZone(del_params_); + params[DatabaseAccessor::DEL_RDATA] = it->getCurrent().toText(); + accessor_->deleteRecordInZone(params); } } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index adfc241a57..59867ba7e9 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1633,6 +1633,15 @@ TEST_F(DatabaseClientTest, addRRsetToNewZone) { qtype_, rrttl_, ZoneFinder::SUCCESS, expected_rdatas_, expected_sig_rdatas_); } + + // Add the non RRSIG RRset again, to see the attempt of adding RRSIG + // causes any unexpected effect, in particular, whether the SIGTYPE + // field might remain. + updater_->addRRset(*rrset_); + const char* const rrset_added[] = { + "www.example.org.", "org.example.www.", "3600", "A", "", "192.0.2.2" + }; + checkLastAdded(rrset_added); } TEST_F(DatabaseClientTest, addRRsetToCurrentZone) { From 4ef59f25a452f934408a9ba837cea9b7fab0be48 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 6 Sep 2011 16:22:40 -0700 Subject: [PATCH 667/974] [1068] made sure Updater::committed_ is updated immediately after commit. --- src/lib/datasrc/database.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index e17baf8e61..2c5aaeb6cf 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -750,13 +750,12 @@ DatabaseUpdater::commit() { << db_name_); } accessor_->commitUpdateZone(); + committed_ = true; // make sure the destructor won't trigger rollback // We release the accessor immediately after commit is completed so that // we don't hold the possible internal resource any longer. accessor_.reset(); - committed_ = true; - logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_COMMIT) .arg(zone_name_).arg(zone_class_).arg(db_name_); } From bcafb8b98d5df77108a83a6bd8b7746f7c2616d7 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 6 Sep 2011 16:43:51 -0700 Subject: [PATCH 668/974] [1068] added a note about possible future extensions to getUpdater() of the in-memory data source client. --- src/lib/datasrc/memory_datasrc.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index be8cb8f793..c569548f63 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -267,7 +267,13 @@ public: virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const; /// In-memory data source is read-only, so this derived method will - /// result in a NotImplemented (once merged) exception. + /// result in a NotImplemented exception. + /// + /// \note We plan to use a database-based data source as a backend + /// persistent storage for an in-memory data source. When it's + /// implemented we may also want to allow the user of the in-memory client + /// to update via its updater (this may or may not be a good idea and + /// is subject to further discussions). virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name, bool replace) const; From 40126733cc69634035b0cca3a0c90ee3a606ea3b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 6 Sep 2011 17:19:46 -0700 Subject: [PATCH 669/974] [1068] some editorial fixes that happened to be found --- src/lib/datasrc/tests/database_unittest.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 59867ba7e9..7e72ca3d1d 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -402,7 +402,7 @@ public: private: // The following member variables are storage and/or update work space // of the test zone. The "master"s are the real objects that contain - // the data, and they are shared among by all accessors cloned from + // the data, and they are shared among all accessors cloned from // an initially created one. The pointer members allow the sharing. // "readonly" is for normal lookups. "update" is the workspace for // updates. When update starts it will be initialized either as an @@ -416,7 +416,6 @@ private: const Domains* empty_records_; // used as temporary storage during the building of the fake data - //Domains records; // used as temporary storage after searchForRecord() and during // getNextRecord() calls, as well as during the building of the From 83a58b817e5c0432d543b66208f502b059fdbe13 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 6 Sep 2011 17:45:29 -0700 Subject: [PATCH 670/974] [1068] added a new test case that performs a bit more complicated update operations with multiple adds and deletes. --- src/lib/datasrc/tests/database_unittest.cc | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 7e72ca3d1d..0e1ee361d4 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1971,4 +1971,78 @@ TEST_F(DatabaseClientTest, deleteRRsetWithRRSIG) { rrset_->addRRsig(*rrsigset_); EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); } + +TEST_F(DatabaseClientTest, compoundUpdate) { + // This test case performs an arbitrary chosen add/delete operations + // in a single update transaction. Essentially there is nothing new to + // test here, but there may be some bugs that was overlooked and can + // only happen in the compound update scenario, so we test it. + + updater_ = client_->getUpdater(zname_, false); + + // add a new RR to an existing RRset + updater_->addRRset(*rrset_); + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.1"); + expected_rdatas_.push_back("192.0.2.2"); + doFindTest(updater_->getFinder(), qname_, qtype_, qtype_, rrttl_, + ZoneFinder::SUCCESS, expected_rdatas_, empty_rdatas_); + + // delete an existing RR + rrset_.reset(new RRset(Name("www.example.org"), qclass_, qtype_, rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), + rrset_->getClass(), "192.0.2.1")); + updater_->deleteRRset(*rrset_); + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.2"); + doFindTest(updater_->getFinder(), qname_, qtype_, qtype_, rrttl_, + ZoneFinder::SUCCESS, expected_rdatas_, empty_rdatas_); + + // re-add it + updater_->addRRset(*rrset_); + expected_rdatas_.push_back("192.0.2.1"); + doFindTest(updater_->getFinder(), qname_, qtype_, qtype_, rrttl_, + ZoneFinder::SUCCESS, expected_rdatas_, empty_rdatas_); + + // add a new RR with a new name + const Name newname("newname.example.org"); + const RRType newtype(RRType::AAAA()); + doFindTest(updater_->getFinder(), newname, newtype, newtype, rrttl_, + ZoneFinder::NXDOMAIN, empty_rdatas_, empty_rdatas_); + rrset_.reset(new RRset(newname, qclass_, newtype, rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), + rrset_->getClass(), "2001:db8::10")); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), + rrset_->getClass(), "2001:db8::11")); + updater_->addRRset(*rrset_); + expected_rdatas_.clear(); + expected_rdatas_.push_back("2001:db8::10"); + expected_rdatas_.push_back("2001:db8::11"); + doFindTest(updater_->getFinder(), newname, newtype, newtype, rrttl_, + ZoneFinder::SUCCESS, expected_rdatas_, empty_rdatas_); + + // delete one RR from the previous set + rrset_.reset(new RRset(newname, qclass_, newtype, rrttl_)); + rrset_->addRdata(rdata::createRdata(rrset_->getType(), + rrset_->getClass(), "2001:db8::11")); + updater_->deleteRRset(*rrset_); + expected_rdatas_.clear(); + expected_rdatas_.push_back("2001:db8::10"); + doFindTest(updater_->getFinder(), newname, newtype, newtype, rrttl_, + ZoneFinder::SUCCESS, expected_rdatas_, empty_rdatas_); + + // Commit the changes, confirm the entire changes applied. + updater_->commit(); + shared_ptr finder(getFinder()); + expected_rdatas_.clear(); + expected_rdatas_.push_back("192.0.2.2"); + expected_rdatas_.push_back("192.0.2.1"); + doFindTest(*finder, qname_, qtype_, qtype_, rrttl_, + ZoneFinder::SUCCESS, expected_rdatas_, empty_rdatas_); + + expected_rdatas_.clear(); + expected_rdatas_.push_back("2001:db8::10"); + doFindTest(*finder, newname, newtype, newtype, rrttl_, + ZoneFinder::SUCCESS, expected_rdatas_, empty_rdatas_); +} } From 6906362bebdbe7e0de66f2c8d10a00bd34911121 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 7 Sep 2011 00:28:01 -0700 Subject: [PATCH 671/974] [1068] used google test's TYPED_TEST to share many of the test cases with the mock accessor and sqlite3 accessor. --- src/lib/datasrc/tests/Makefile.am | 1 + src/lib/datasrc/tests/database_unittest.cc | 1499 ++++++++++---------- src/lib/datasrc/tests/testdata/Makefile.am | 4 + 3 files changed, 727 insertions(+), 777 deletions(-) diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index a1ef054d55..48cbc7697f 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -65,3 +65,4 @@ EXTRA_DIST += testdata/sql1.example.com.signed EXTRA_DIST += testdata/sql2.example.com.signed EXTRA_DIST += testdata/test-root.sqlite3 EXTRA_DIST += testdata/test.sqlite3 +EXTRA_DIST += testdata/rwtest.sqlite3 diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 0183fe5040..7dc4137c00 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -610,155 +611,17 @@ private: // might not come in 'normal' order) // It shall immediately fail if you try to add the same name twice. void fillData() { - // some plain data - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("AAAA", "3600", "", "2001:db8::1"); - addRecord("AAAA", "3600", "", "2001:db8::2"); - addCurName("www.example.org."); - - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("AAAA", "3600", "", "2001:db8::1"); - addRecord("A", "3600", "", "192.0.2.2"); - addCurName("www2.example.org."); - - addRecord("CNAME", "3600", "", "www.example.org."); - addCurName("cname.example.org."); - - // some DNSSEC-'signed' data - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - addRecord("AAAA", "3600", "", "2001:db8::1"); - addRecord("AAAA", "3600", "", "2001:db8::2"); - addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("signed1.example.org."); - addRecord("CNAME", "3600", "", "www.example.org."); - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("signedcname1.example.org."); - // special case might fail; sig is for cname, which isn't there (should be ignored) - // (ignoring of 'normal' other type is done above by www.) - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("acnamesig1.example.org."); - - // let's pretend we have a database that is not careful - // about the order in which it returns data - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("AAAA", "3600", "", "2001:db8::2"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("AAAA", "3600", "", "2001:db8::1"); - addCurName("signed2.example.org."); - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("CNAME", "3600", "", "www.example.org."); - addCurName("signedcname2.example.org."); - - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("acnamesig2.example.org."); - - addRecord("RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("acnamesig3.example.org."); - - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("A", "360", "", "192.0.2.2"); - addCurName("ttldiff1.example.org."); - addRecord("A", "360", "", "192.0.2.1"); - addRecord("A", "3600", "", "192.0.2.2"); - addCurName("ttldiff2.example.org."); - - // also add some intentionally bad data - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("CNAME", "3600", "", "www.example.org."); - addCurName("badcname1.example.org."); - - addRecord("CNAME", "3600", "", "www.example.org."); - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("badcname2.example.org."); - - addRecord("CNAME", "3600", "", "www.example.org."); - addRecord("CNAME", "3600", "", "www.example2.org."); - addCurName("badcname3.example.org."); - - addRecord("A", "3600", "", "bad"); - addCurName("badrdata.example.org."); - - addRecord("BAD_TYPE", "3600", "", "192.0.2.1"); - addCurName("badtype.example.org."); - - addRecord("A", "badttl", "", "192.0.2.1"); - addCurName("badttl.example.org."); - - addRecord("A", "badttl", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "A 5 3 3600 somebaddata 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("badsig.example.org."); - - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("badsigtype.example.org."); - - // Data for testing delegation (with NS and DNAME) - addRecord("NS", "3600", "", "ns.example.com."); - addRecord("NS", "3600", "", "ns.delegation.example.org."); - addRecord("RRSIG", "3600", "", "NS 5 3 3600 20000101000000 " - "20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("delegation.example.org."); - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("ns.delegation.example.org."); - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("deep.below.delegation.example.org."); - - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("DNAME", "3600", "", "dname.example.com."); - addRecord("RRSIG", "3600", "", "DNAME 5 3 3600 20000101000000 " - "20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("dname.example.org."); - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("below.dname.example.org."); - - // Broken NS - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("NS", "3600", "", "ns.example.com."); - addCurName("brokenns1.example.org."); - addRecord("NS", "3600", "", "ns.example.com."); - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("brokenns2.example.org."); - - // Now double DNAME, to test failure mode - addRecord("DNAME", "3600", "", "dname1.example.com."); - addRecord("DNAME", "3600", "", "dname2.example.com."); - addCurName("baddname.example.org."); - - // Put some data into apex (including NS) so we can check our NS - // doesn't break anything - addRecord("NS", "3600", "", "ns.example.com."); - addRecord("A", "3600", "", "192.0.2.1"); - addRecord("RRSIG", "3600", "", "NS 5 3 3600 20000101000000 " - "20000201000000 12345 example.org. FAKEFAKEFAKE"); - addCurName("example.org."); - - // This is because of empty domain test - addRecord("A", "3600", "", "192.0.2.1"); - addCurName("a.b.example.org."); - - // Something for wildcards - addRecord("A", "3600", "", "192.0.2.5"); - addCurName("*.wild.example.org."); - addRecord("AAAA", "3600", "", "2001:db8::5"); - addCurName("cancel.here.wild.example.org."); - addRecord("NS", "3600", "", "ns.example.com."); - addCurName("delegatedwild.example.org."); - addRecord("A", "3600", "", "192.0.2.5"); - addCurName("*.delegatedwild.example.org."); - addRecord("A", "3600", "", "192.0.2.5"); - addCurName("wild.*.foo.example.org."); - addRecord("A", "3600", "", "192.0.2.5"); - addCurName("wild.*.foo.*.bar.example.org."); + const char* prev_name = NULL; + for (int i = 0; TEST_RECORDS[i][0] != NULL; ++i) { + if (prev_name != NULL && + strcmp(prev_name, TEST_RECORDS[i][0]) != 0) { + addCurName(prev_name); + } + prev_name = TEST_RECORDS[i][0]; + addRecord(TEST_RECORDS[i][1], TEST_RECORDS[i][2], + TEST_RECORDS[i][3], TEST_RECORDS[i][4]); + } + addCurName(prev_name); } }; @@ -775,6 +638,10 @@ TEST(DatabaseConnectionTest, getAllRecords) { isc::NotImplemented); } +// This test fixture is templated so that we can share (most of) the test +// cases with different types of data sources. Note that in test cases +// we need to use 'this' to refer to member variables of the test class. +template class DatabaseClientTest : public ::testing::Test { public: DatabaseClientTest() : zname_("example.org"), qname_("www.example.org"), @@ -810,9 +677,10 @@ public: * times per test. */ void createClient() { - current_accessor_ = new MockAccessor(); + current_accessor_ = new ACCESSOR_TYPE(); + is_mock_ = (dynamic_cast(current_accessor_) != NULL); client_.reset(new DatabaseClient(qclass_, - shared_ptr( + shared_ptr( current_accessor_))); } @@ -826,7 +694,9 @@ public: dynamic_pointer_cast(zone.zone_finder)); ASSERT_NE(shared_ptr(), finder) << "Wrong type of finder"; - EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); + if (is_mock_) { + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); + } EXPECT_EQ(current_accessor_, &finder->getAccessor()); } @@ -835,30 +705,49 @@ public: EXPECT_EQ(result::SUCCESS, zone.code); shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); - EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); + if (is_mock_) { + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); + } return (finder); } // Helper methods for update tests - //bool isRollbacked(bool expected = false) const { - bool isRollbacked() const { - return (update_accessor_->isRollbacked()); + bool isRollbacked(bool expected = false) const { + if (is_mock_) { + const MockAccessor& mock_accessor = + dynamic_cast(*update_accessor_); + return (mock_accessor.isRollbacked()); + } else { + return (expected); + } } void checkLastAdded(const char* const expected[]) const { - for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT; ++i) { - EXPECT_EQ(expected[i], - current_accessor_->getLatestClone()->getLastAdded()[i]); + if (is_mock_) { + const MockAccessor* mock_accessor = + dynamic_cast(current_accessor_); + for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT; ++i) { + EXPECT_EQ(expected[i], + mock_accessor->getLatestClone()->getLastAdded()[i]); + } } } void setUpdateAccessor() { - update_accessor_ = current_accessor_->getLatestClone(); + if (is_mock_) { + const MockAccessor* mock_accessor = + dynamic_cast(current_accessor_); + update_accessor_ = mock_accessor->getLatestClone(); + } } + // Some tests only work for MockAccessor. We remember whether our accessor + // is of that type. + bool is_mock_; + // Will be deleted by client_, just keep the current value for comparison. - MockAccessor* current_accessor_; + ACCESSOR_TYPE* current_accessor_; shared_ptr client_; const std::string database_name_; @@ -876,7 +765,7 @@ public: // update related objects to be tested ZoneUpdaterPtr updater_; - shared_ptr update_accessor_; + shared_ptr update_accessor_; // placeholders const std::vector empty_rdatas_; // for NXRRSET/NXDOMAIN @@ -884,25 +773,55 @@ public: std::vector expected_sig_rdatas_; }; -TEST_F(DatabaseClientTest, zoneNotFound) { - DataSourceClient::FindResult zone(client_->findZone(Name("example.com"))); +class TestSQLite3Accessor : public SQLite3Accessor { +public: + TestSQLite3Accessor() : SQLite3Accessor( + TEST_DATA_BUILDDIR "/rwtest.sqlite3.copied", + RRClass::IN()) + { + startUpdateZone("example.org.", true); + string columns[ADD_COLUMN_COUNT]; + for (int i = 0; TEST_RECORDS[i][0] != NULL; ++i) { + columns[ADD_NAME] = TEST_RECORDS[i][0]; + columns[ADD_REV_NAME] = Name(columns[ADD_NAME]).reverse().toText(); + columns[ADD_TYPE] = TEST_RECORDS[i][1]; + columns[ADD_TTL] = TEST_RECORDS[i][2]; + columns[ADD_SIGTYPE] = TEST_RECORDS[i][3]; + columns[ADD_RDATA] = TEST_RECORDS[i][4]; + + addRecordToZone(columns); + } + commitUpdateZone(); + } +}; + +// The following two lines instantiate test cases with concrete accessor +// classes to be tested. +typedef ::testing::Types TestAccessorTypes; +TYPED_TEST_CASE(DatabaseClientTest, TestAccessorTypes); + +TYPED_TEST(DatabaseClientTest, zoneNotFound) { + DataSourceClient::FindResult zone( + this->client_->findZone(Name("example.com"))); EXPECT_EQ(result::NOTFOUND, zone.code); } -TEST_F(DatabaseClientTest, exactZone) { - DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); +TYPED_TEST(DatabaseClientTest, exactZone) { + DataSourceClient::FindResult zone( + this->client_->findZone(Name("example.org"))); EXPECT_EQ(result::SUCCESS, zone.code); - checkZoneFinder(zone); + this->checkZoneFinder(zone); } -TEST_F(DatabaseClientTest, superZone) { - DataSourceClient::FindResult zone(client_->findZone(Name( +TYPED_TEST(DatabaseClientTest, superZone) { + DataSourceClient::FindResult zone(this->client_->findZone(Name( "sub.example.org"))); EXPECT_EQ(result::PARTIALMATCH, zone.code); - checkZoneFinder(zone); + this->checkZoneFinder(zone); } -TEST_F(DatabaseClientTest, noAccessorException) { +// This test doesn't depend on derived accessor class, so we use TEST(). +TEST(GenericDatabaseClientTest, noAccessorException) { // We need a dummy variable here; some compiler would regard it a mere // declaration instead of an instantiation and make the test fail. EXPECT_THROW(DatabaseClient dummy(RRClass::IN(), @@ -911,13 +830,14 @@ TEST_F(DatabaseClientTest, noAccessorException) { } // If the zone doesn't exist, exception is thrown -TEST_F(DatabaseClientTest, noZoneIterator) { - EXPECT_THROW(client_->getIterator(Name("example.com")), DataSourceError); +TYPED_TEST(DatabaseClientTest, noZoneIterator) { + EXPECT_THROW(this->client_->getIterator(Name("example.com")), + DataSourceError); } // If the zone doesn't exist and iteration is not implemented, it still throws // the exception it doesn't exist -TEST_F(DatabaseClientTest, noZoneNotImplementedIterator) { +TEST(GenericDatabaseClientTest, noZoneNotImplementedIterator) { EXPECT_THROW(DatabaseClient(RRClass::IN(), boost::shared_ptr( new NopAccessor())).getIterator( @@ -925,32 +845,44 @@ TEST_F(DatabaseClientTest, noZoneNotImplementedIterator) { DataSourceError); } -TEST_F(DatabaseClientTest, notImplementedIterator) { +TEST(GenericDatabaseClientTest, notImplementedIterator) { EXPECT_THROW(DatabaseClient(RRClass::IN(), shared_ptr( new NopAccessor())).getIterator(Name("example.org")), isc::NotImplemented); } // Pretend a bug in the connection and pass NULL as the context -// Should not crash, but gracefully throw -TEST_F(DatabaseClientTest, nullIteratorContext) { - EXPECT_THROW(client_->getIterator(Name("null.example.org")), - isc::Unexpected); +// Should not crash, but gracefully throw. Works for the mock accessor only. +TYPED_TEST(DatabaseClientTest, nullIteratorContext) { + if (this->is_mock_) { + EXPECT_THROW(this->client_->getIterator(Name("null.example.org")), + isc::Unexpected); + } } -// It doesn't crash or anything if the zone is completely empty -TEST_F(DatabaseClientTest, emptyIterator) { - ZoneIteratorPtr it(client_->getIterator(Name("empty.example.org"))); +// It doesn't crash or anything if the zone is completely empty. +// Works for the mock accessor only. +TYPED_TEST(DatabaseClientTest, emptyIterator) { + if (!this->is_mock_) { + return; + } + ZoneIteratorPtr it(this->client_->getIterator(Name("empty.example.org"))); EXPECT_EQ(ConstRRsetPtr(), it->getNextRRset()); // This is past the end, it should throw EXPECT_THROW(it->getNextRRset(), isc::Unexpected); } -// Iterate trough a zone -TEST_F(DatabaseClientTest, iterator) { - ZoneIteratorPtr it(client_->getIterator(Name("example.org"))); +// Iterate through a zone +TYPED_TEST(DatabaseClientTest, iterator) { + ZoneIteratorPtr it(this->client_->getIterator(Name("example.org"))); ConstRRsetPtr rrset(it->getNextRRset()); ASSERT_NE(ConstRRsetPtr(), rrset); + + // The rest of the checks work only for the mock accessor. + if (!this->is_mock_) { + return; + } + EXPECT_EQ(Name("example.org"), rrset->getName()); EXPECT_EQ(RRClass::IN(), rrset->getClass()); EXPECT_EQ(RRType::SOA(), rrset->getType()); @@ -993,10 +925,14 @@ TEST_F(DatabaseClientTest, iterator) { } // This has inconsistent TTL in the set (the rest, like nonsense in -// the data is handled in rdata itself). -TEST_F(DatabaseClientTest, badIterator) { +// the data is handled in rdata itself). Work for mock iterator only. +TYPED_TEST(DatabaseClientTest, badIterator) { + if (!this->is_mock_) { + return; + } + // It should not throw, but get the lowest one of them - ZoneIteratorPtr it(client_->getIterator(Name("bad.example.org"))); + ZoneIteratorPtr it(this->client_->getIterator(Name("bad.example.org"))); EXPECT_EQ(it->getNextRRset()->getTTL(), isc::dns::RRTTL(300)); } @@ -1053,544 +989,511 @@ doFindTest(ZoneFinder& finder, } } -TEST_F(DatabaseClientTest, find) { - shared_ptr finder(getFinder()); +TYPED_TEST(DatabaseClientTest, find) { + shared_ptr finder(this->getFinder()); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); doFindTest(*finder, isc::dns::Name("www.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_rdatas_.push_back("192.0.2.2"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.2"); doFindTest(*finder, isc::dns::Name("www2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("2001:db8::1"); - expected_rdatas_.push_back("2001:db8::2"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("2001:db8::1"); + this->expected_rdatas_.push_back("2001:db8::2"); doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), + this->rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), - isc::dns::RRTTL(3600), + this->rrttl_, ZoneFinder::NXRRSET, - expected_rdatas_, expected_sig_rdatas_); + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("www.example.org."); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("www.example.org."); doFindTest(*finder, isc::dns::Name("cname.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::CNAME(), - isc::dns::RRTTL(3600), - ZoneFinder::CNAME, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, isc::dns::RRType::CNAME(), this->rrttl_, + ZoneFinder::CNAME, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("www.example.org."); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("www.example.org."); doFindTest(*finder, isc::dns::Name("cname.example.org."), isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("doesnotexist.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::NXDOMAIN, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::NXDOMAIN, + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("signed1.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("2001:db8::1"); - expected_rdatas_.push_back("2001:db8::2"); - expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("2001:db8::1"); + this->expected_rdatas_.push_back("2001:db8::2"); + this->expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("signed1.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("signed1.example.org."), - isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), - isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, - expected_rdatas_, expected_sig_rdatas_); + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), this->rrttl_, + ZoneFinder::NXRRSET, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("www.example.org."); - expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("www.example.org."); + this->expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("signedcname1.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::CNAME(), - isc::dns::RRTTL(3600), - ZoneFinder::CNAME, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, isc::dns::RRType::CNAME(), this->rrttl_, + ZoneFinder::CNAME, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); - expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("signed2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("2001:db8::2"); - expected_rdatas_.push_back("2001:db8::1"); - expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("2001:db8::2"); + this->expected_rdatas_.push_back("2001:db8::1"); + this->expected_sig_rdatas_.push_back("AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("signed2.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("signed2.example.org."), - isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), - isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, - expected_rdatas_, expected_sig_rdatas_); + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), this->rrttl_, + ZoneFinder::NXRRSET, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("www.example.org."); - expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("www.example.org."); + this->expected_sig_rdatas_.push_back("CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("signedcname2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::CNAME(), - isc::dns::RRTTL(3600), - ZoneFinder::CNAME, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, isc::dns::RRType::CNAME(), this->rrttl_, + ZoneFinder::CNAME, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("acnamesig1.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("acnamesig2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("acnamesig3.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_rdatas_.push_back("192.0.2.2"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.2"); doFindTest(*finder, isc::dns::Name("ttldiff1.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(360), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, isc::dns::RRTTL(360), + ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_rdatas_.push_back("192.0.2.2"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.2"); doFindTest(*finder, isc::dns::Name("ttldiff2.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(360), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, isc::dns::RRTTL(360), + ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."), - isc::dns::RRType::A(), + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); EXPECT_THROW(finder->find(isc::dns::Name("badcname2.example.org."), - isc::dns::RRType::A(), + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); EXPECT_THROW(finder->find(isc::dns::Name("badcname3.example.org."), - isc::dns::RRType::A(), + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."), - isc::dns::RRType::A(), + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."), - isc::dns::RRType::A(), + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."), - isc::dns::RRType::A(), + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); EXPECT_THROW(finder->find(isc::dns::Name("badsig.example.org."), - isc::dns::RRType::A(), + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); // Trigger the hardcoded exceptions and see if find() has cleaned up - EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), - DataSourceError); - EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), - isc::Exception); - EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), - std::exception); - EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), - DataSourceError); - EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), - isc::Exception); - EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."), - isc::dns::RRType::A(), - NULL, ZoneFinder::FIND_DEFAULT), - std::exception); + if (this->is_mock_) { + EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."), + this->qtype_, + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."), + this->qtype_, + NULL, ZoneFinder::FIND_DEFAULT), + isc::Exception); + EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."), + this->qtype_, + NULL, ZoneFinder::FIND_DEFAULT), + std::exception); + EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."), + this->qtype_, + NULL, ZoneFinder::FIND_DEFAULT), + DataSourceError); + EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."), + this->qtype_, + NULL, ZoneFinder::FIND_DEFAULT), + isc::Exception); + EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."), + this->qtype_, + NULL, ZoneFinder::FIND_DEFAULT), + std::exception); + } // This RRSIG has the wrong sigtype field, which should be // an error if we decide to keep using that field // Right now the field is ignored, so it does not error - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("badsigtype.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); } -TEST_F(DatabaseClientTest, findDelegation) { - shared_ptr finder(getFinder()); +TYPED_TEST(DatabaseClientTest, findDelegation) { + shared_ptr finder(this->getFinder()); // The apex should not be considered delegation point and we can access // data - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); doFindTest(*finder, isc::dns::Name("example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, - expected_sig_rdatas_); + this->qtype_, this->qtype_, + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); - expected_rdatas_.clear(); - expected_rdatas_.push_back("ns.example.com."); - expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 " + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("ns.example.com."); + this->expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 " "12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, - expected_sig_rdatas_); + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); // Check when we ask for something below delegation point, we get the NS // (Both when the RRset there exists and doesn't) - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); - expected_rdatas_.push_back("ns.example.com."); - expected_rdatas_.push_back("ns.delegation.example.org."); - expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 " + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + this->expected_rdatas_.push_back("ns.example.com."); + this->expected_rdatas_.push_back("ns.delegation.example.org."); + this->expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 20000201000000 " "12345 example.org. FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, - expected_sig_rdatas_, + this->qtype_, isc::dns::RRType::NS(), + this->rrttl_, ZoneFinder::DELEGATION, this->expected_rdatas_, + this->expected_sig_rdatas_, isc::dns::Name("delegation.example.org.")); doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, - expected_sig_rdatas_, + this->rrttl_, ZoneFinder::DELEGATION, this->expected_rdatas_, + this->expected_sig_rdatas_, isc::dns::Name("delegation.example.org.")); doFindTest(*finder, isc::dns::Name("deep.below.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, - expected_sig_rdatas_, + this->rrttl_, ZoneFinder::DELEGATION, this->expected_rdatas_, + this->expected_sig_rdatas_, isc::dns::Name("delegation.example.org.")); // Even when we check directly at the delegation point, we should get // the NS doFindTest(*finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, - expected_sig_rdatas_); + this->rrttl_, ZoneFinder::DELEGATION, this->expected_rdatas_, + this->expected_sig_rdatas_); // And when we ask direcly for the NS, we should still get delegation doFindTest(*finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, - expected_sig_rdatas_); + this->rrttl_, ZoneFinder::DELEGATION, this->expected_rdatas_, + this->expected_sig_rdatas_); // Now test delegation. If it is below the delegation point, we should get // the DNAME (the one with data under DNAME is invalid zone, but we test // the behaviour anyway just to make sure) - expected_rdatas_.clear(); - expected_rdatas_.push_back("dname.example.com."); - expected_sig_rdatas_.clear(); - expected_sig_rdatas_.push_back("DNAME 5 3 3600 20000101000000 " + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("dname.example.com."); + this->expected_sig_rdatas_.clear(); + this->expected_sig_rdatas_.push_back("DNAME 5 3 3600 20000101000000 " "20000201000000 12345 example.org. " "FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("below.dname.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, - expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); + this->qtype_, isc::dns::RRType::DNAME(), + this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_, + this->expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); doFindTest(*finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, - expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); + this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_, + this->expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); doFindTest(*finder, isc::dns::Name("really.deep.below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, - expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); + this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_, + this->expected_sig_rdatas_, isc::dns::Name("dname.example.org.")); // Asking direcly for DNAME should give SUCCESS doFindTest(*finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::DNAME(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, - expected_sig_rdatas_); + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); // But we don't delegate at DNAME point - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_sig_rdatas_.clear(); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_sig_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("dname.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, - expected_sig_rdatas_); - expected_rdatas_.clear(); + this->qtype_, this->qtype_, + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); + this->expected_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, - expected_sig_rdatas_); + this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_, + this->expected_sig_rdatas_); // This is broken dname, it contains two targets EXPECT_THROW(finder->find(isc::dns::Name("below.baddname.example.org."), - isc::dns::RRType::A(), NULL, + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); // Broken NS - it lives together with something else EXPECT_THROW(finder->find(isc::dns::Name("brokenns1.example.org."), - isc::dns::RRType::A(), NULL, + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); EXPECT_THROW(finder->find(isc::dns::Name("brokenns2.example.org."), - isc::dns::RRType::A(), NULL, + this->qtype_, NULL, ZoneFinder::FIND_DEFAULT), DataSourceError); } -TEST_F(DatabaseClientTest, emptyDomain) { - shared_ptr finder(getFinder()); +TYPED_TEST(DatabaseClientTest, emptyDomain) { + shared_ptr finder(this->getFinder()); // This domain doesn't exist, but a subdomain of it does. // Therefore we should pretend the domain is there, but contains no RRsets - doFindTest(*finder, isc::dns::Name("b.example.org."), isc::dns::RRType::A(), - isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_); + doFindTest(*finder, isc::dns::Name("b.example.org."), this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_); } -// Glue-OK mode. Just go trough NS delegations down (but not trough +// Glue-OK mode. Just go through NS delegations down (but not through // DNAME) and pretend it is not there. -TEST_F(DatabaseClientTest, glueOK) { - shared_ptr finder(getFinder()); +TYPED_TEST(DatabaseClientTest, glueOK) { + shared_ptr finder(this->getFinder()); - expected_rdatas_.clear(); - expected_sig_rdatas_.clear(); + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, - expected_rdatas_, expected_sig_rdatas_, + this->rrttl_, ZoneFinder::NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_, isc::dns::Name("ns.delegation.example.org."), ZoneFinder::FIND_GLUE_OK); doFindTest(*finder, isc::dns::Name("nothere.delegation.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, - expected_rdatas_, expected_sig_rdatas_, + this->rrttl_, ZoneFinder::NXDOMAIN, + this->expected_rdatas_, this->expected_sig_rdatas_, isc::dns::Name("nothere.delegation.example.org."), ZoneFinder::FIND_GLUE_OK); - expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.1"); doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_, + this->qtype_, this->qtype_, + this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_, isc::dns::Name("ns.delegation.example.org."), ZoneFinder::FIND_GLUE_OK); - expected_rdatas_.clear(); - expected_rdatas_.push_back("ns.example.com."); - expected_rdatas_.push_back("ns.delegation.example.org."); - expected_sig_rdatas_.clear(); - expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 " + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("ns.example.com."); + this->expected_rdatas_.push_back("ns.delegation.example.org."); + this->expected_sig_rdatas_.clear(); + this->expected_sig_rdatas_.push_back("NS 5 3 3600 20000101000000 " "20000201000000 12345 example.org. " "FAKEFAKEFAKE"); // When we request the NS, it should be SUCCESS, not DELEGATION // (different in GLUE_OK) doFindTest(*finder, isc::dns::Name("delegation.example.org."), isc::dns::RRType::NS(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_, + this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_, isc::dns::Name("delegation.example.org."), ZoneFinder::FIND_GLUE_OK); - expected_rdatas_.clear(); - expected_rdatas_.push_back("dname.example.com."); - expected_sig_rdatas_.clear(); - expected_sig_rdatas_.push_back("DNAME 5 3 3600 20000101000000 " + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("dname.example.com."); + this->expected_sig_rdatas_.clear(); + this->expected_sig_rdatas_.push_back("DNAME 5 3 3600 20000101000000 " "20000201000000 12345 example.org. " "FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("below.dname.example.org."), - isc::dns::RRType::A(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, - expected_sig_rdatas_, isc::dns::Name("dname.example.org."), - ZoneFinder::FIND_GLUE_OK); + this->qtype_, isc::dns::RRType::DNAME(), + this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_, + this->expected_sig_rdatas_, + isc::dns::Name("dname.example.org."), ZoneFinder::FIND_GLUE_OK); doFindTest(*finder, isc::dns::Name("below.dname.example.org."), isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(), - isc::dns::RRTTL(3600), ZoneFinder::DNAME, expected_rdatas_, - expected_sig_rdatas_, isc::dns::Name("dname.example.org."), - ZoneFinder::FIND_GLUE_OK); + this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_, + this->expected_sig_rdatas_, + isc::dns::Name("dname.example.org."), ZoneFinder::FIND_GLUE_OK); } -TEST_F(DatabaseClientTest, wildcard) { - shared_ptr finder(getFinder()); +TYPED_TEST(DatabaseClientTest, wildcard) { + shared_ptr finder(this->getFinder()); // First, simple wildcard match - expected_rdatas_.push_back("192.0.2.5"); + this->expected_rdatas_.push_back("192.0.2.5"); doFindTest(*finder, isc::dns::Name("a.wild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, - expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, + ZoneFinder::SUCCESS, this->expected_rdatas_, + this->expected_sig_rdatas_); doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, - expected_sig_rdatas_); - expected_rdatas_.clear(); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); + this->expected_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("a.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, - expected_sig_rdatas_); + this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_, + this->expected_sig_rdatas_); doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, - expected_sig_rdatas_); + this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_, + this->expected_sig_rdatas_); // Direct request for thi wildcard - expected_rdatas_.push_back("192.0.2.5"); + this->expected_rdatas_.push_back("192.0.2.5"); doFindTest(*finder, isc::dns::Name("*.wild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_, - expected_sig_rdatas_); - expected_rdatas_.clear(); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); + this->expected_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("*.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, - expected_sig_rdatas_); + this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_, + this->expected_sig_rdatas_); // This is nonsense, but check it doesn't match by some stupid accident doFindTest(*finder, isc::dns::Name("a.*.wild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::NXDOMAIN, + this->expected_rdatas_, this->expected_sig_rdatas_); // These should be canceled, since it is below a domain which exitsts doFindTest(*finder, isc::dns::Name("nothing.here.wild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::NXDOMAIN, + this->expected_rdatas_, this->expected_sig_rdatas_); doFindTest(*finder, isc::dns::Name("cancel.here.wild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_); doFindTest(*finder, isc::dns::Name("below.cancel.here.wild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, - expected_rdatas_, expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::NXDOMAIN, + this->expected_rdatas_, this->expected_sig_rdatas_); // And this should be just plain empty non-terminal domain, check // the wildcard doesn't hurt it doFindTest(*finder, isc::dns::Name("here.wild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::A(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_, - expected_sig_rdatas_); + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_); // Also make sure that the wildcard doesn't hurt the original data // below the wildcard - expected_rdatas_.push_back("2001:db8::5"); + this->expected_rdatas_.push_back("2001:db8::5"); doFindTest(*finder, isc::dns::Name("cancel.here.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); - expected_rdatas_.clear(); + this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); + this->expected_rdatas_.clear(); // How wildcard go together with delegation - expected_rdatas_.push_back("ns.example.com."); + this->expected_rdatas_.push_back("ns.example.com."); doFindTest(*finder, isc::dns::Name("below.delegatedwild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, - expected_sig_rdatas_, + this->qtype_, isc::dns::RRType::NS(), this->rrttl_, + ZoneFinder::DELEGATION, this->expected_rdatas_, + this->expected_sig_rdatas_, isc::dns::Name("delegatedwild.example.org")); // FIXME: This doesn't look logically OK, GLUE_OK should make it transparent, // so the match should either work or be canceled, but return NXDOMAIN doFindTest(*finder, isc::dns::Name("below.delegatedwild.example.org"), - isc::dns::RRType::A(), isc::dns::RRType::NS(), - isc::dns::RRTTL(3600), ZoneFinder::DELEGATION, expected_rdatas_, - expected_sig_rdatas_, + this->qtype_, isc::dns::RRType::NS(), this->rrttl_, + ZoneFinder::DELEGATION, this->expected_rdatas_, + this->expected_sig_rdatas_, isc::dns::Name("delegatedwild.example.org"), ZoneFinder::FIND_GLUE_OK); - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.5"); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.5"); // These are direct matches const char* positive_names[] = { "wild.*.foo.example.org.", @@ -1598,14 +1501,14 @@ TEST_F(DatabaseClientTest, wildcard) { NULL }; for (const char** name(positive_names); *name != NULL; ++ name) { - doFindTest(*finder, isc::dns::Name(*name), isc::dns::RRType::A(), - isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::SUCCESS, expected_rdatas_, - expected_sig_rdatas_); + doFindTest(*finder, isc::dns::Name(*name), this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, + this->expected_sig_rdatas_); } // These are wildcard matches against empty nonterminal asterisk - expected_rdatas_.clear(); + this->expected_rdatas_.clear(); const char* negative_names[] = { "a.foo.example.org.", "*.foo.example.org.", @@ -1621,463 +1524,505 @@ TEST_F(DatabaseClientTest, wildcard) { NULL }; for (const char** name(negative_names); *name != NULL; ++ name) { - doFindTest(*finder, isc::dns::Name(*name), isc::dns::RRType::A(), - isc::dns::RRType::A(), isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, expected_rdatas_, - expected_sig_rdatas_); + doFindTest(*finder, isc::dns::Name(*name), this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_); } } -TEST_F(DatabaseClientTest, getOrigin) { - DataSourceClient::FindResult zone(client_->findZone(Name("example.org"))); +TYPED_TEST(DatabaseClientTest, getOrigin) { + DataSourceClient::FindResult zone(this->client_->findZone(this->zname_)); ASSERT_EQ(result::SUCCESS, zone.code); shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); - EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); - EXPECT_EQ(isc::dns::Name("example.org"), finder->getOrigin()); + if (this->is_mock_) { + EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id()); + } + EXPECT_EQ(this->zname_, finder->getOrigin()); } -TEST_F(DatabaseClientTest, updaterFinder) { - updater_ = client_->getUpdater(zname_, false); - ASSERT_TRUE(updater_); +TYPED_TEST(DatabaseClientTest, updaterFinder) { + this->updater_ = this->client_->getUpdater(this->zname_, false); + ASSERT_TRUE(this->updater_); // If this update isn't replacing the zone, the finder should work // just like the normal find() case. - EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( - updater_->getFinder()).zone_id()); - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - doFindTest(updater_->getFinder(), qname_, - qtype_, qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + if (this->is_mock_) { + EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( + this->updater_->getFinder()).zone_id()); + } + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + doFindTest(this->updater_->getFinder(), this->qname_, + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); // When replacing the zone, the updater's finder shouldn't see anything // in the zone until something is added. - updater_.reset(); - updater_ = client_->getUpdater(zname_, true); - ASSERT_TRUE(updater_); - EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( - updater_->getFinder()).zone_id()); - doFindTest(updater_->getFinder(), qname_, qtype_, qtype_, rrttl_, - ZoneFinder::NXDOMAIN, empty_rdatas_, empty_rdatas_); + this->updater_.reset(); + this->updater_ = this->client_->getUpdater(this->zname_, true); + ASSERT_TRUE(this->updater_); + if (this->is_mock_) { + EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( + this->updater_->getFinder()).zone_id()); + } + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::NXDOMAIN, + this->empty_rdatas_, this->empty_rdatas_); } -TEST_F(DatabaseClientTest, flushZone) { +TYPED_TEST(DatabaseClientTest, flushZone) { // A simple update case: flush the entire zone - shared_ptr finder(getFinder()); + shared_ptr finder(this->getFinder()); // Before update, the name exists. - EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_, + this->qtype_).code); // start update in the replace mode. the normal finder should still // be able to see the record, but the updater's finder shouldn't. - updater_ = client_->getUpdater(zname_, true); - setUpdateAccessor(); + this->updater_ = this->client_->getUpdater(this->zname_, true); + this->setUpdateAccessor(); EXPECT_EQ(ZoneFinder::SUCCESS, - finder->find(qname_, qtype_).code); + finder->find(this->qname_, this->qtype_).code); EXPECT_EQ(ZoneFinder::NXDOMAIN, - updater_->getFinder().find(qname_, qtype_).code); + this->updater_->getFinder().find(this->qname_, + this->qtype_).code); // commit the update. now the normal finder shouldn't see it. - updater_->commit(); - EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(qname_, qtype_).code); + this->updater_->commit(); + EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(this->qname_, + this->qtype_).code); // Check rollback wasn't accidentally performed. - EXPECT_FALSE(isRollbacked()); + EXPECT_FALSE(this->isRollbacked()); } -TEST_F(DatabaseClientTest, updateCancel) { +TYPED_TEST(DatabaseClientTest, updateCancel) { // similar to the previous test, but destruct the updater before commit. - ZoneFinderPtr finder = client_->findZone(zname_).zone_finder; - EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); + ZoneFinderPtr finder = this->client_->findZone(this->zname_).zone_finder; + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_, + this->qtype_).code); - updater_ = client_->getUpdater(zname_, true); - setUpdateAccessor(); + this->updater_ = this->client_->getUpdater(this->zname_, true); + this->setUpdateAccessor(); EXPECT_EQ(ZoneFinder::NXDOMAIN, - updater_->getFinder().find(qname_, qtype_).code); + this->updater_->getFinder().find(this->qname_, + this->qtype_).code); // DB should not have been rolled back yet. - EXPECT_FALSE(isRollbacked()); - updater_.reset(); // destruct without commit + EXPECT_FALSE(this->isRollbacked()); + this->updater_.reset(); // destruct without commit // reset() should have triggered rollback (although it doesn't affect // anything to the mock accessor implementation except for the result of // isRollbacked()) - EXPECT_TRUE(isRollbacked()); - EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(qname_, qtype_).code); + EXPECT_TRUE(this->isRollbacked(true)); + EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_, + this->qtype_).code); } -TEST_F(DatabaseClientTest, exceptionFromRollback) { - updater_ = client_->getUpdater(zname_, true); +TYPED_TEST(DatabaseClientTest, exceptionFromRollback) { + this->updater_ = this->client_->getUpdater(this->zname_, true); - rrset_.reset(new RRset(Name("throw.example.org"), qclass_, qtype_, - rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), - rrset_->getClass(), "192.0.2.1")); - updater_->addRRset(*rrset_); + this->rrset_.reset(new RRset(Name("throw.example.org"), this->qclass_, + this->qtype_, this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "192.0.2.1")); + this->updater_->addRRset(*this->rrset_); // destruct without commit. The added name will result in an exception // in the MockAccessor's rollback method. It shouldn't be propagated. - EXPECT_NO_THROW(updater_.reset()); + EXPECT_NO_THROW(this->updater_.reset()); } -TEST_F(DatabaseClientTest, duplicateCommit) { +TYPED_TEST(DatabaseClientTest, duplicateCommit) { // duplicate commit. should result in exception. - updater_ = client_->getUpdater(zname_, true); - updater_->commit(); - EXPECT_THROW(updater_->commit(), DataSourceError); + this->updater_ = this->client_->getUpdater(this->zname_, true); + this->updater_->commit(); + EXPECT_THROW(this->updater_->commit(), DataSourceError); } -TEST_F(DatabaseClientTest, addRRsetToNewZone) { +TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) { // Add a single RRset to a fresh empty zone - updater_ = client_->getUpdater(zname_, true); - updater_->addRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, true); + this->updater_->addRRset(*this->rrset_); - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.2"); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.2"); { SCOPED_TRACE("add RRset"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); } // Similar to the previous case, but with RRSIG - updater_.reset(); - updater_ = client_->getUpdater(zname_, true); - updater_->addRRset(*rrset_); - updater_->addRRset(*rrsigset_); + this->updater_.reset(); + this->updater_ = this->client_->getUpdater(this->zname_, true); + this->updater_->addRRset(*this->rrset_); + this->updater_->addRRset(*this->rrsigset_); // confirm the expected columns were passed to the accessor (if checkable). const char* const rrsig_added[] = { "www.example.org.", "org.example.www.", "3600", "RRSIG", "A", "A 5 3 0 20000101000000 20000201000000 0 example.org. FAKEFAKEFAKE" }; - checkLastAdded(rrsig_added); + this->checkLastAdded(rrsig_added); - expected_sig_rdatas_.clear(); - expected_sig_rdatas_.push_back(rrsig_added[DatabaseAccessor::ADD_RDATA]); + this->expected_sig_rdatas_.clear(); + this->expected_sig_rdatas_.push_back( + rrsig_added[DatabaseAccessor::ADD_RDATA]); { SCOPED_TRACE("add RRset with RRSIG"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, expected_sig_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->expected_sig_rdatas_); } } -TEST_F(DatabaseClientTest, addRRsetToCurrentZone) { +TYPED_TEST(DatabaseClientTest, addRRsetToCurrentZone) { // Similar to the previous test, but not replacing the existing data. - shared_ptr finder(getFinder()); + shared_ptr finder(this->getFinder()); - updater_ = client_->getUpdater(zname_, false); - updater_->addRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->addRRset(*this->rrset_); // We should see both old and new data. - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_rdatas_.push_back("192.0.2.2"); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.2"); { SCOPED_TRACE("add RRset"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); } - updater_->commit(); + this->updater_->commit(); { SCOPED_TRACE("add RRset after commit"); - doFindTest(*finder, qname_, qtype_, qtype_, - rrttl_, ZoneFinder::SUCCESS, expected_rdatas_, - empty_rdatas_); + doFindTest(*finder, this->qname_, this->qtype_, this->qtype_, + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, addMultipleRRs) { +TYPED_TEST(DatabaseClientTest, addMultipleRRs) { // Similar to the previous case, but the added RRset contains multiple // RRs. - updater_ = client_->getUpdater(zname_, false); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "192.0.2.3")); - updater_->addRRset(*rrset_); - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_rdatas_.push_back("192.0.2.2"); - expected_rdatas_.push_back("192.0.2.3"); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "192.0.2.3")); + this->updater_->addRRset(*this->rrset_); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.2"); + this->expected_rdatas_.push_back("192.0.2.3"); { SCOPED_TRACE("add multiple RRs"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, addRRsetOfLargerTTL) { +TYPED_TEST(DatabaseClientTest, addRRsetOfLargerTTL) { // Similar to the previous one, but the TTL of the added RRset is larger // than that of the existing record. The finder should use the smaller // one. - updater_ = client_->getUpdater(zname_, false); - rrset_->setTTL(RRTTL(7200)); - updater_->addRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->rrset_->setTTL(RRTTL(7200)); + this->updater_->addRRset(*this->rrset_); - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_rdatas_.push_back("192.0.2.2"); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.2"); { SCOPED_TRACE("add RRset of larger TTL"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, addRRsetOfSmallerTTL) { +TYPED_TEST(DatabaseClientTest, addRRsetOfSmallerTTL) { // Similar to the previous one, but the added RRset has a smaller TTL. // The added TTL should be used by the finder. - updater_ = client_->getUpdater(zname_, false); - rrset_->setTTL(RRTTL(1800)); - updater_->addRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->rrset_->setTTL(RRTTL(1800)); + this->updater_->addRRset(*this->rrset_); - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_rdatas_.push_back("192.0.2.2"); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.2"); { SCOPED_TRACE("add RRset of smaller TTL"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, RRTTL(1800), ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, RRTTL(1800), ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, addSameRR) { +TYPED_TEST(DatabaseClientTest, addSameRR) { // Add the same RR as that is already in the data source. // Currently the add interface doesn't try to suppress the duplicate, // neither does the finder. We may want to revisit it in future versions. - updater_ = client_->getUpdater(zname_, false); - rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "192.0.2.1")); - updater_->addRRset(*rrset_); - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.1"); - expected_rdatas_.push_back("192.0.2.1"); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->rrset_.reset(new RRset(this->qname_, this->qclass_, this->qtype_, + this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "192.0.2.1")); + this->updater_->addRRset(*this->rrset_); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.1"); + this->expected_rdatas_.push_back("192.0.2.1"); { SCOPED_TRACE("add same RR"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, addDeviantRR) { - updater_ = client_->getUpdater(zname_, false); +TYPED_TEST(DatabaseClientTest, addDeviantRR) { + this->updater_ = this->client_->getUpdater(this->zname_, false); // RR class mismatch. This should be detected and rejected. - rrset_.reset(new RRset(qname_, RRClass::CH(), RRType::TXT(), rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "test text")); - EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); + this->rrset_.reset(new RRset(this->qname_, RRClass::CH(), RRType::TXT(), + this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "test text")); + EXPECT_THROW(this->updater_->addRRset(*this->rrset_), DataSourceError); // Out-of-zone owner name. At a higher level this should be rejected, // but it doesn't happen in this interface. - rrset_.reset(new RRset(Name("example.com"), qclass_, qtype_, rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "192.0.2.100")); - updater_->addRRset(*rrset_); + this->rrset_.reset(new RRset(Name("example.com"), this->qclass_, + this->qtype_, this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "192.0.2.100")); + this->updater_->addRRset(*this->rrset_); - expected_rdatas_.clear(); - expected_rdatas_.push_back("192.0.2.100"); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("192.0.2.100"); { // Note: with the find() implementation being more strict about // zone cuts, this test may fail. Then the test should be updated. SCOPED_TRACE("add out-of-zone RR"); - doFindTest(updater_->getFinder(), Name("example.com"), - qtype_, qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), Name("example.com"), + this->qtype_, this->qtype_, this->rrttl_, + ZoneFinder::SUCCESS, this->expected_rdatas_, + this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, addEmptyRRset) { - updater_ = client_->getUpdater(zname_, false); - rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); - EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); +TYPED_TEST(DatabaseClientTest, addEmptyRRset) { + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->rrset_.reset(new RRset(this->qname_, this->qclass_, this->qtype_, + this->rrttl_)); + EXPECT_THROW(this->updater_->addRRset(*this->rrset_), DataSourceError); } -TEST_F(DatabaseClientTest, addAfterCommit) { - updater_ = client_->getUpdater(zname_, false); - updater_->commit(); - EXPECT_THROW(updater_->addRRset(*rrset_), DataSourceError); +TYPED_TEST(DatabaseClientTest, addAfterCommit) { + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->commit(); + EXPECT_THROW(this->updater_->addRRset(*this->rrset_), DataSourceError); } -TEST_F(DatabaseClientTest, deleteRRset) { - shared_ptr finder(getFinder()); +TYPED_TEST(DatabaseClientTest, deleteRRset) { + shared_ptr finder(this->getFinder()); - rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "192.0.2.1")); + this->rrset_.reset(new RRset(this->qname_, this->qclass_, this->qtype_, + this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "192.0.2.1")); // Delete one RR from an RRset - updater_ = client_->getUpdater(zname_, false); - updater_->deleteRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->deleteRRset(*this->rrset_); // Delete the only RR of a name - rrset_.reset(new RRset(Name("cname.example.org"), qclass_, - RRType::CNAME(), rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "www.example.org")); - updater_->deleteRRset(*rrset_); + this->rrset_.reset(new RRset(Name("cname.example.org"), this->qclass_, + RRType::CNAME(), this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "www.example.org")); + this->updater_->deleteRRset(*this->rrset_); - // The updater_ finder should immediately see the deleted results. + // The this->updater_ finder should immediately see the deleted results. { SCOPED_TRACE("delete RRset"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::NXRRSET, - empty_rdatas_, empty_rdatas_); - doFindTest(updater_->getFinder(), Name("cname.example.org"), - qtype_, qtype_, rrttl_, ZoneFinder::NXDOMAIN, - empty_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::NXRRSET, + this->empty_rdatas_, this->empty_rdatas_); + doFindTest(this->updater_->getFinder(), Name("cname.example.org"), + this->qtype_, this->qtype_, this->rrttl_, + ZoneFinder::NXDOMAIN, this->empty_rdatas_, + this->empty_rdatas_); } // before committing the change, the original finder should see the // original record. { SCOPED_TRACE("delete RRset before commit"); - expected_rdatas_.push_back("192.0.2.1"); - doFindTest(*finder, qname_, qtype_, qtype_, - rrttl_, ZoneFinder::SUCCESS, expected_rdatas_, - empty_rdatas_); + this->expected_rdatas_.push_back("192.0.2.1"); + doFindTest(*finder, this->qname_, this->qtype_, this->qtype_, + this->rrttl_, ZoneFinder::SUCCESS, this->expected_rdatas_, + this->empty_rdatas_); - expected_rdatas_.clear(); - expected_rdatas_.push_back("www.example.org."); - doFindTest(*finder, Name("cname.example.org"), qtype_, - RRType::CNAME(), rrttl_, ZoneFinder::CNAME, - expected_rdatas_, empty_rdatas_); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("www.example.org."); + doFindTest(*finder, Name("cname.example.org"), this->qtype_, + RRType::CNAME(), this->rrttl_, ZoneFinder::CNAME, + this->expected_rdatas_, this->empty_rdatas_); } // once committed, the record should be removed from the original finder's // view, too. - updater_->commit(); + this->updater_->commit(); { SCOPED_TRACE("delete RRset after commit"); - doFindTest(*finder, qname_, qtype_, qtype_, - rrttl_, ZoneFinder::NXRRSET, empty_rdatas_, - empty_rdatas_); - doFindTest(*finder, Name("cname.example.org"), - qtype_, qtype_, rrttl_, ZoneFinder::NXDOMAIN, - empty_rdatas_, empty_rdatas_); + doFindTest(*finder, this->qname_, this->qtype_, this->qtype_, + this->rrttl_, ZoneFinder::NXRRSET, this->empty_rdatas_, + this->empty_rdatas_); + doFindTest(*finder, Name("cname.example.org"), this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::NXDOMAIN, + this->empty_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, deleteRRsetToNXDOMAIN) { +TYPED_TEST(DatabaseClientTest, deleteRRsetToNXDOMAIN) { // similar to the previous case, but it removes the only record of the // given name. a subsequent find() should result in NXDOMAIN. - rrset_.reset(new RRset(Name("cname.example.org"), qclass_, - RRType::CNAME(), rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "www.example.org")); + this->rrset_.reset(new RRset(Name("cname.example.org"), this->qclass_, + RRType::CNAME(), this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "www.example.org")); - updater_ = client_->getUpdater(zname_, false); - updater_->deleteRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->deleteRRset(*this->rrset_); { SCOPED_TRACE("delete RRset to NXDOMAIN"); - doFindTest(updater_->getFinder(), Name("cname.example.org"), - qtype_, qtype_, rrttl_, ZoneFinder::NXDOMAIN, - empty_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), Name("cname.example.org"), + this->qtype_, this->qtype_, this->rrttl_, + ZoneFinder::NXDOMAIN, this->empty_rdatas_, + this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, deleteMultipleRRs) { - rrset_.reset(new RRset(qname_, qclass_, RRType::AAAA(), rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "2001:db8::1")); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "2001:db8::2")); +TYPED_TEST(DatabaseClientTest, deleteMultipleRRs) { + this->rrset_.reset(new RRset(this->qname_, this->qclass_, RRType::AAAA(), + this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "2001:db8::1")); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "2001:db8::2")); - updater_ = client_->getUpdater(zname_, false); - updater_->deleteRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->deleteRRset(*this->rrset_); { SCOPED_TRACE("delete multiple RRs"); - doFindTest(updater_->getFinder(), qname_, RRType::AAAA(), - qtype_, rrttl_, ZoneFinder::NXRRSET, - empty_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, RRType::AAAA(), + this->qtype_, this->rrttl_, ZoneFinder::NXRRSET, + this->empty_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, partialDelete) { - rrset_.reset(new RRset(qname_, qclass_, RRType::AAAA(), rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "2001:db8::1")); +TYPED_TEST(DatabaseClientTest, partialDelete) { + this->rrset_.reset(new RRset(this->qname_, this->qclass_, RRType::AAAA(), + this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "2001:db8::1")); // This does not exist in the test data source: - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "2001:db8::3")); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "2001:db8::3")); // deleteRRset should succeed "silently", and subsequent find() should // find the remaining RR. - updater_ = client_->getUpdater(zname_, false); - updater_->deleteRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->deleteRRset(*this->rrset_); { SCOPED_TRACE("partial delete"); - expected_rdatas_.push_back("2001:db8::2"); - doFindTest(updater_->getFinder(), qname_, RRType::AAAA(), - RRType::AAAA(), rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + this->expected_rdatas_.push_back("2001:db8::2"); + doFindTest(this->updater_->getFinder(), this->qname_, RRType::AAAA(), + RRType::AAAA(), this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, deleteNoMatch) { +TYPED_TEST(DatabaseClientTest, deleteNoMatch) { // similar to the previous test, but there's not even a match in the // specified RRset. Essentially there's no difference in the result. - updater_ = client_->getUpdater(zname_, false); - updater_->deleteRRset(*rrset_); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->deleteRRset(*this->rrset_); { SCOPED_TRACE("delete no match"); - expected_rdatas_.push_back("192.0.2.1"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::SUCCESS, - expected_rdatas_, empty_rdatas_); + this->expected_rdatas_.push_back("192.0.2.1"); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->expected_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, deleteWithDifferentTTL) { +TYPED_TEST(DatabaseClientTest, deleteWithDifferentTTL) { // Our delete interface simply ignores TTL (may change in a future version) - rrset_.reset(new RRset(qname_, qclass_, qtype_, RRTTL(1800))); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "192.0.2.1")); - updater_ = client_->getUpdater(zname_, false); - updater_->deleteRRset(*rrset_); + this->rrset_.reset(new RRset(this->qname_, this->qclass_, this->qtype_, + RRTTL(1800))); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "192.0.2.1")); + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->deleteRRset(*this->rrset_); { SCOPED_TRACE("delete RRset with a different TTL"); - doFindTest(updater_->getFinder(), qname_, qtype_, - qtype_, rrttl_, ZoneFinder::NXRRSET, - empty_rdatas_, empty_rdatas_); + doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, + this->qtype_, this->rrttl_, ZoneFinder::NXRRSET, + this->empty_rdatas_, this->empty_rdatas_); } } -TEST_F(DatabaseClientTest, deleteDeviantRR) { - updater_ = client_->getUpdater(zname_, false); +TYPED_TEST(DatabaseClientTest, deleteDeviantRR) { + this->updater_ = this->client_->getUpdater(this->zname_, false); // RR class mismatch. This should be detected and rejected. - rrset_.reset(new RRset(qname_, RRClass::CH(), RRType::TXT(), rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "test text")); - EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); + this->rrset_.reset(new RRset(this->qname_, RRClass::CH(), RRType::TXT(), + this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "test text")); + EXPECT_THROW(this->updater_->deleteRRset(*this->rrset_), DataSourceError); // Out-of-zone owner name. At a higher level this should be rejected, // but it doesn't happen in this interface. - rrset_.reset(new RRset(Name("example.com"), qclass_, qtype_, rrttl_)); - rrset_->addRdata(rdata::createRdata(rrset_->getType(), rrset_->getClass(), - "192.0.2.100")); - EXPECT_NO_THROW(updater_->deleteRRset(*rrset_)); + this->rrset_.reset(new RRset(Name("example.com"), this->qclass_, + this->qtype_, this->rrttl_)); + this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(), + this->rrset_->getClass(), + "192.0.2.100")); + EXPECT_NO_THROW(this->updater_->deleteRRset(*this->rrset_)); } -TEST_F(DatabaseClientTest, deleteAfterCommit) { - updater_ = client_->getUpdater(zname_, false); - updater_->commit(); - EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); +TYPED_TEST(DatabaseClientTest, deleteAfterCommit) { + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->updater_->commit(); + EXPECT_THROW(this->updater_->deleteRRset(*this->rrset_), DataSourceError); } -TEST_F(DatabaseClientTest, deleteEmptyRRset) { - updater_ = client_->getUpdater(zname_, false); - rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_)); - EXPECT_THROW(updater_->deleteRRset(*rrset_), DataSourceError); +TYPED_TEST(DatabaseClientTest, deleteEmptyRRset) { + this->updater_ = this->client_->getUpdater(this->zname_, false); + this->rrset_.reset(new RRset(this->qname_, this->qclass_, this->qtype_, + this->rrttl_)); + EXPECT_THROW(this->updater_->deleteRRset(*this->rrset_), DataSourceError); } } diff --git a/src/lib/datasrc/tests/testdata/Makefile.am b/src/lib/datasrc/tests/testdata/Makefile.am index 6a35fe3c63..84d49fc2ea 100644 --- a/src/lib/datasrc/tests/testdata/Makefile.am +++ b/src/lib/datasrc/tests/testdata/Makefile.am @@ -1 +1,5 @@ CLEANFILES = *.copied +BUILT_SOURCES = rwtest.sqlite3.copied + +rwtest.sqlite3.copied: $(srcdir)/rwtest.sqlite3 + cp $(srcdir)/rwtest.sqlite3 $@ From c1c2ddf5be4556e6e8cd52a314ddd6d026c7e540 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 7 Sep 2011 10:24:23 +0200 Subject: [PATCH 672/974] [1176] Check no DNSSEC_OK means no RRSIGs --- src/bin/auth/tests/query_unittest.cc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index d65cc1029f..4b8f013b02 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -111,7 +111,8 @@ public: dname_name_("dname.example.com"), has_SOA_(true), has_apex_NS_(true), - rrclass_(RRClass::IN()) + rrclass_(RRClass::IN()), + include_rrsig_anyway_(false) { stringstream zone_stream; zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt << @@ -137,6 +138,9 @@ public: // the apex NS. void setApexNSFlag(bool on) { has_apex_NS_ = on; } + // Turn this on if you want it to return RRSIGs regardless of FIND_GLUE_OK + void setIncludeRRSIGAnyway(bool on) { include_rrsig_anyway_ = on; } + private: typedef map RRsetStore; typedef map Domains; @@ -181,6 +185,7 @@ private: ConstRRsetPtr delegation_rrset_; ConstRRsetPtr dname_rrset_; const RRClass rrclass_; + bool include_rrsig_anyway_; }; ZoneFinder::FindResult @@ -219,6 +224,7 @@ MockZoneFinder::find(const Name& name, const RRType& type, // Strip whatever signature there is in case DNSSEC is not required // Just to make sure the Query asks for it when it is needed if (options & ZoneFinder::FIND_DNSSEC || + include_rrsig_anyway_ || !found_rrset->second->getRRsig()) { rrset = found_rrset->second; } else { @@ -342,6 +348,17 @@ TEST_F(QueryTest, exactMatch) { www_a_txt, zone_ns_txt, ns_addrs_txt); } +TEST_F(QueryTest, exactMatchIgnoreSIG) { + // Check that we do not include the RRSIG when not requested even when + // we receive it from the data source. + mock_finder->setIncludeRRSIGAnyway(true); + Query query(memory_client, qname, qtype, response); + EXPECT_NO_THROW(query.process()); + // find match rrset + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3, + www_a_txt, zone_ns_txt, ns_addrs_txt); +} + TEST_F(QueryTest, dnssecPositive) { // Just like exactMatch, but the signatures should be included as well Query query(memory_client, qname, qtype, response, true); From 64ac0166d5ea3b565f500f8a770dfa4d7d9f6a28 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 7 Sep 2011 09:58:53 -0700 Subject: [PATCH 673/974] [1068] avoid TYPED_TEST for purely mock-specific test fixtures. --- src/lib/datasrc/tests/database_unittest.cc | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index ae5dff31fc..f1858bdf95 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -799,6 +799,11 @@ public: typedef ::testing::Types TestAccessorTypes; TYPED_TEST_CASE(DatabaseClientTest, TestAccessorTypes); +// In some cases the entire test fixture is for the mock accessor only. +// We use the usual TEST_F for them with the corresponding specialized class +// to make the code simpler. +typedef DatabaseClientTest MockDatabaseClientTest; + TYPED_TEST(DatabaseClientTest, zoneNotFound) { DataSourceClient::FindResult zone( this->client_->findZone(Name("example.com"))); @@ -852,19 +857,14 @@ TEST(GenericDatabaseClientTest, notImplementedIterator) { // Pretend a bug in the connection and pass NULL as the context // Should not crash, but gracefully throw. Works for the mock accessor only. -TYPED_TEST(DatabaseClientTest, nullIteratorContext) { - if (this->is_mock_) { - EXPECT_THROW(this->client_->getIterator(Name("null.example.org")), - isc::Unexpected); - } +TEST_F(MockDatabaseClientTest, nullIteratorContext) { + EXPECT_THROW(this->client_->getIterator(Name("null.example.org")), + isc::Unexpected); } // It doesn't crash or anything if the zone is completely empty. // Works for the mock accessor only. -TYPED_TEST(DatabaseClientTest, emptyIterator) { - if (!this->is_mock_) { - return; - } +TEST_F(MockDatabaseClientTest, emptyIterator) { ZoneIteratorPtr it(this->client_->getIterator(Name("empty.example.org"))); EXPECT_EQ(ConstRRsetPtr(), it->getNextRRset()); // This is past the end, it should throw @@ -924,12 +924,8 @@ TYPED_TEST(DatabaseClientTest, iterator) { } // This has inconsistent TTL in the set (the rest, like nonsense in -// the data is handled in rdata itself). Work for mock iterator only. -TYPED_TEST(DatabaseClientTest, badIterator) { - if (!this->is_mock_) { - return; - } - +// the data is handled in rdata itself). Works for the mock accessor only. +TEST_F(MockDatabaseClientTest, badIterator) { // It should not throw, but get the lowest one of them ZoneIteratorPtr it(this->client_->getIterator(Name("bad.example.org"))); EXPECT_EQ(it->getNextRRset()->getTTL(), isc::dns::RRTTL(300)); From 7cc9b08f18967fa1a694f5b7e320aad62d0d3e88 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 7 Sep 2011 12:34:50 -0700 Subject: [PATCH 674/974] [master] fixed a build error on MacOS-X g++ briefly discussed on jabber, didn't see any objection. --- src/lib/datasrc/tests/database_unittest.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 8faf5d7bdb..4ed9f129a5 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1553,8 +1553,9 @@ TYPED_TEST(DatabaseClientTest, updaterFinder) { // If this update isn't replacing the zone, the finder should work // just like the normal find() case. if (this->is_mock_) { - EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( - this->updater_->getFinder()).zone_id()); + DatabaseClient::Finder& finder = dynamic_cast( + this->updater_->getFinder()); + EXPECT_EQ(WRITABLE_ZONE_ID, finder.zone_id()); } this->expected_rdatas_.clear(); this->expected_rdatas_.push_back("192.0.2.1"); @@ -1568,8 +1569,9 @@ TYPED_TEST(DatabaseClientTest, updaterFinder) { this->updater_ = this->client_->getUpdater(this->zname_, true); ASSERT_TRUE(this->updater_); if (this->is_mock_) { - EXPECT_EQ(WRITABLE_ZONE_ID, dynamic_cast( - this->updater_->getFinder()).zone_id()); + DatabaseClient::Finder& finder = dynamic_cast( + this->updater_->getFinder()); + EXPECT_EQ(WRITABLE_ZONE_ID, finder.zone_id()); } doFindTest(this->updater_->getFinder(), this->qname_, this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::NXDOMAIN, From 856ff83ad2b97c136de1103a421547bdcb332e74 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 8 Sep 2011 00:20:46 -0700 Subject: [PATCH 675/974] [1199] corrected the description of the ZoneFinder class. directly committed and pushed for master. --- src/lib/datasrc/zone.h | 51 +++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 3e8b1730ee..bb4f4357d8 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -23,40 +23,31 @@ namespace isc { namespace datasrc { -/// \brief The base class for a single authoritative zone +/// \brief The base class to search a zone for RRsets /// -/// The \c Zone class is an abstract base class for representing -/// a DNS zone as part of data source. +/// The \c ZoneFinder class is an abstract base class for representing +/// an object that performs DNS lookups in a specific zone accessible via +/// a data source. In general, different types of data sources (in-memory, +/// database-based, etc) define their own derived classes of \c ZoneFinder, +/// implementing ways to retrieve the required data through the common +/// interfaces declared in the base class. Each concrete \c ZoneFinder +/// object is therefore (conceptually) associated with a specific zone +/// of one specific data source instance. /// -/// At the moment this is provided mainly for making the \c ZoneTable class -/// and the authoritative query logic testable, and only provides a minimal -/// set of features. -/// This is why this class is defined in the same header file, but it may -/// have to move to a separate header file when we understand what is -/// necessary for this class for actual operation. +/// The origin name and the RR class of the associated zone are available +/// via the \c getOrigin() and \c getClass() methods, respectively. /// -/// The idea is to provide a specific derived zone class for each data -/// source, beginning with in memory one. At that point the derived classes -/// will have more specific features. For example, they will maintain -/// information about the location of a zone file, whether it's loaded in -/// memory, etc. +/// The most important method of this class is \c find(), which performs +/// the lookup for a given domain and type. See the description of the +/// method for details. /// -/// It's not yet clear how the derived zone classes work with various other -/// data sources when we integrate these components, but one possibility is -/// something like this: -/// - If the underlying database such as some variant of SQL doesn't have an -/// explicit representation of zones (as part of public interface), we can -/// probably use a "default" zone class that simply encapsulates the -/// corresponding data source and calls a common "find" like method. -/// - Some data source may want to specialize it by inheritance as an -/// optimization. For example, in the current schema design of the sqlite3 -/// data source, its (derived) zone class would contain the information of -/// the "zone ID". -/// -/// Note: Unlike some other abstract base classes we don't name the -/// class beginning with "Abstract". This is because we want to have -/// commonly used definitions such as \c Result and \c ZoneFinderPtr, and we -/// want to make them look more intuitive. +/// \note It's not clear whether we should request that a zone finder form a +/// "transaction", that is, whether to ensure the finder is not susceptible +/// to changes made by someone else than the creator of the finder. If we +/// don't request that, for example, two different lookup results for the +/// same name and type can be different if other threads or programs make +/// updates to the zone between the lookups. We should revisit this point +/// as we gain more experiences. class ZoneFinder { public: /// Result codes of the \c find() method. From 8d1942a3b7516e8161b7f54888da2a4a4d27484e Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 8 Sep 2011 02:01:44 -0700 Subject: [PATCH 676/974] [master] use install-sh (with -m) instead of cp to make sure writable test DB file has write permission. hopefully it should fix the distcheck failure on the freebsd8 buildbot. --- src/lib/datasrc/tests/testdata/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/tests/testdata/Makefile.am b/src/lib/datasrc/tests/testdata/Makefile.am index 84d49fc2ea..64ae9559ae 100644 --- a/src/lib/datasrc/tests/testdata/Makefile.am +++ b/src/lib/datasrc/tests/testdata/Makefile.am @@ -1,5 +1,6 @@ CLEANFILES = *.copied BUILT_SOURCES = rwtest.sqlite3.copied +# We use install-sh with the -m option to make sure it's writable rwtest.sqlite3.copied: $(srcdir)/rwtest.sqlite3 - cp $(srcdir)/rwtest.sqlite3 $@ + $(top_srcdir)/install-sh -m 644 $(srcdir)/rwtest.sqlite3 $@ From 02b2e71bdc1564f4272869bb5676727af809870f Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 8 Sep 2011 13:37:23 -0500 Subject: [PATCH 677/974] [master] fix typo in the configuration name dash to underscore (obvious by looking in spec and python script.) --- src/bin/xfrin/b10-xfrin.8 | 6 +++--- src/bin/xfrin/b10-xfrin.xml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bin/xfrin/b10-xfrin.8 b/src/bin/xfrin/b10-xfrin.8 index 7f73213a0d..54dbe7c701 100644 --- a/src/bin/xfrin/b10-xfrin.8 +++ b/src/bin/xfrin/b10-xfrin.8 @@ -2,12 +2,12 @@ .\" Title: b10-xfrin .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" Date: May 19, 2011 +.\" Date: September 8, 2011 .\" Manual: BIND10 .\" Source: BIND10 .\" Language: English .\" -.TH "B10\-XFRIN" "8" "May 19, 2011" "BIND10" "BIND10" +.TH "B10\-XFRIN" "8" "September 8, 2011" "BIND10" "BIND10" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -61,7 +61,7 @@ receives its configurations from .PP The configurable settings are: .PP -\fItransfers\-in\fR +\fItransfers_in\fR defines the maximum number of inbound zone transfers that can run concurrently\&. The default is 10\&. .PP diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml index 17840fe455..d45e15f011 100644 --- a/src/bin/xfrin/b10-xfrin.xml +++ b/src/bin/xfrin/b10-xfrin.xml @@ -20,7 +20,7 @@ - May 19, 2011 + September 8, 2011 @@ -92,7 +92,7 @@ in separate zonemgr process. The configurable settings are: - transfers-in + transfers_in defines the maximum number of inbound zone transfers that can run concurrently. The default is 10. From 5b713ea8e5fd35fdb1ab7ff953e010ef9b60f98c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 6 Sep 2011 14:17:08 +0200 Subject: [PATCH 678/974] [1177] Refactor getRRset That one was getting really hairy and it wouldn't serve it's purpose in the following work. Currently it takes set of types we're interested in and returns all of them, the logic to decide which one to take is left to the caller. This simplifies the method and doesn't hurt the rest much, as the rest needed to know what to ask for anyway. Also, it allows getting NSEC or NSEC3 records in the same database search almost for free. --- src/lib/datasrc/database.cc | 269 ++++++++++++++++++++---------------- src/lib/datasrc/database.h | 60 ++------ 2 files changed, 165 insertions(+), 164 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 2c5aaeb6cf..b2ab948bac 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -174,16 +174,13 @@ private: }; } -std::pair -DatabaseClient::Finder::getRRset(const isc::dns::Name& name, - const isc::dns::RRType* type, - bool want_cname, bool want_dname, - bool want_ns, - const isc::dns::Name* construct_name) +DatabaseClient::Finder::FoundRRsets +DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, + bool check_ns, const Name* construct_name) { RRsigStore sig_store; bool records_found = false; - isc::dns::RRsetPtr result_rrset; + std::map result; // Request the context DatabaseAccessor::IteratorContextPtr @@ -198,81 +195,19 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, if (construct_name == NULL) { construct_name = &name; } + + bool seen_cname(false); + bool seen_other(false); + bool seen_ns(false); + while (context->getNext(columns)) { - if (!records_found) { - records_found = true; - } + // The domain is not empty + records_found = true; try { - const isc::dns::RRType cur_type(columns[DatabaseAccessor:: - TYPE_COLUMN]); - const isc::dns::RRTTL cur_ttl(columns[DatabaseAccessor:: - TTL_COLUMN]); - // Ths sigtype column was an optimization for finding the - // relevant RRSIG RRs for a lookup. Currently this column is - // not used in this revised datasource implementation. We - // should either start using it again, or remove it from use - // completely (i.e. also remove it from the schema and the - // backend implementation). - // Note that because we don't use it now, we also won't notice - // it if the value is wrong (i.e. if the sigtype column - // contains an rrtype that is different from the actual value - // of the 'type covered' field in the RRSIG Rdata). - //cur_sigtype(columns[SIGTYPE_COLUMN]); + const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]); - // Check for delegations before checking for the right type. - // This is needed to properly delegate request for the NS - // record itself. - // - // This happens with NS only, CNAME must be alone and DNAME - // is not checked in the exact queried domain. - if (want_ns && cur_type == isc::dns::RRType::NS()) { - if (result_rrset && - result_rrset->getType() != isc::dns::RRType::NS()) { - isc_throw(DataSourceError, "NS found together with data" - " in non-apex domain " + name.toText()); - } - addOrCreate(result_rrset, *construct_name, getClass(), - cur_type, cur_ttl, - columns[DatabaseAccessor::RDATA_COLUMN], - *accessor_); - } else if (type != NULL && cur_type == *type) { - if (result_rrset && - result_rrset->getType() == isc::dns::RRType::CNAME()) { - isc_throw(DataSourceError, "CNAME found but it is not " - "the only record for " + name.toText()); - } else if (result_rrset && want_ns && - result_rrset->getType() == isc::dns::RRType::NS()) { - isc_throw(DataSourceError, "NS found together with data" - " in non-apex domain " + name.toText()); - } - addOrCreate(result_rrset, *construct_name, getClass(), - cur_type, cur_ttl, - columns[DatabaseAccessor::RDATA_COLUMN], - *accessor_); - } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) { - // There should be no other data, so result_rrset should - // be empty. - if (result_rrset) { - isc_throw(DataSourceError, "CNAME found but it is not " - "the only record for " + name.toText()); - } - addOrCreate(result_rrset, *construct_name, getClass(), - cur_type, cur_ttl, - columns[DatabaseAccessor::RDATA_COLUMN], - *accessor_); - } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) { - // There should be max one RR of DNAME present - if (result_rrset && - result_rrset->getType() == isc::dns::RRType::DNAME()) { - isc_throw(DataSourceError, "DNAME with multiple RRs in " + - name.toText()); - } - addOrCreate(result_rrset, *construct_name, getClass(), - cur_type, cur_ttl, - columns[DatabaseAccessor::RDATA_COLUMN], - *accessor_); - } else if (cur_type == isc::dns::RRType::RRSIG()) { + if (cur_type == RRType::RRSIG()) { // If we get signatures before we get the actual data, we // can't know which ones to keep and which to drop... // So we keep a separate store of any signature that may be @@ -280,27 +215,67 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name, // done. // A possible optimization here is to not store them for // types we are certain we don't need - sig_store.addSig(isc::dns::rdata::createRdata(cur_type, - getClass(), columns[DatabaseAccessor::RDATA_COLUMN])); + sig_store.addSig(rdata::createRdata(cur_type, getClass(), + columns[DatabaseAccessor::RDATA_COLUMN])); } - } catch (const isc::dns::InvalidRRType& irt) { + + if (types.find(cur_type) != types.end()) { + // This type is requested, so put it into result + const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]); + // Ths sigtype column was an optimization for finding the + // relevant RRSIG RRs for a lookup. Currently this column is + // not used in this revised datasource implementation. We + // should either start using it again, or remove it from use + // completely (i.e. also remove it from the schema and the + // backend implementation). + // Note that because we don't use it now, we also won't notice + // it if the value is wrong (i.e. if the sigtype column + // contains an rrtype that is different from the actual value + // of the 'type covered' field in the RRSIG Rdata). + //cur_sigtype(columns[SIGTYPE_COLUMN]); + addOrCreate(result[cur_type], *construct_name, getClass(), + cur_type, cur_ttl, + columns[DatabaseAccessor::RDATA_COLUMN], + *accessor_); + } + + if (cur_type == RRType::CNAME()) { + seen_cname = true; + } else if (cur_type == RRType::NS()) { + seen_ns = true; + } else if (cur_type != RRType::RRSIG()) {//RRSIG can live anywhere + // FIXME: Is something else allowed in the delegation point? DS? + seen_other = true; + } + } catch (const InvalidRRType& irt) { isc_throw(DataSourceError, "Invalid RRType in database for " << name << ": " << columns[DatabaseAccessor:: TYPE_COLUMN]); - } catch (const isc::dns::InvalidRRTTL& irttl) { + } catch (const InvalidRRTTL& irttl) { isc_throw(DataSourceError, "Invalid TTL in database for " << name << ": " << columns[DatabaseAccessor:: TTL_COLUMN]); - } catch (const isc::dns::rdata::InvalidRdataText& ird) { + } catch (const rdata::InvalidRdataText& ird) { isc_throw(DataSourceError, "Invalid rdata in database for " << name << ": " << columns[DatabaseAccessor:: RDATA_COLUMN]); } } - if (result_rrset) { - sig_store.appendSignatures(result_rrset); + if (seen_cname && (seen_other || seen_ns)) { + isc_throw(DataSourceError, "CNAME shares domain " << name << + " with something else"); } - return (std::pair(records_found, result_rrset)); + if (check_ns && seen_ns && seen_other) { + isc_throw(DataSourceError, "NS shares domain " << name << + " with something else"); + } + // Add signatures to all found RRsets + for (std::map::iterator i(result.begin()); + i != result.end(); ++ i) { + sig_store.appendSignatures(i->second); + } + + return (FoundRRsets(records_found, result)); } bool @@ -317,6 +292,21 @@ DatabaseClient::Finder::hasSubdomains(const std::string& name) { return (context->getNext(columns)); } +// Some manipulation with RRType sets +namespace { + +std::set empty_types; + +// To conveniently put the RRTypes into the sets. This is not really clean +// design, but it is hidden inside this file and makes the calls much more +// convenient. +std::set operator +(std::set set, const RRType& type) { + set.insert(type); + return (set); +} + +} + ZoneFinder::FindResult DatabaseClient::Finder::find(const isc::dns::Name& name, const isc::dns::RRType& type, @@ -329,7 +319,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, bool glue_ok(options & FIND_GLUE_OK); isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; - std::pair found; + FoundRRsets found; logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS) .arg(accessor_->getDBName()).arg(name).arg(type); // In case we are in GLUE_OK mode and start matching wildcards, @@ -345,39 +335,52 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // This is how many labels we remove to get origin size_t remove_labels(current_label_count - origin_label_count); + // Type shortcut, used a lot here + typedef std::map::const_iterator FoundIterator; + // Now go trough all superdomains from origin down for (int i(remove_labels); i > 0; --i) { Name superdomain(name.split(i)); // Look if there's NS or DNAME (but ignore the NS in origin) - found = getRRset(superdomain, NULL, false, true, - i != remove_labels && !glue_ok); + static WantedTypes delegation_types(empty_types + RRType::DNAME() + + RRType::NS()); + found = getRRsets(superdomain, delegation_types, i != remove_labels); if (found.first) { // It contains some RRs, so it exists. last_known = superdomain.getLabelCount(); - // In case we are in GLUE_OK, we want to store the highest - // encountered RRset. - if (glue_ok && !first_ns && i != remove_labels) { - first_ns = getRRset(superdomain, NULL, false, false, - true).second; - } - } - if (found.second) { - // We found something redirecting somewhere else - // (it can be only NS or DNAME here) - result_rrset = found.second; - if (result_rrset->getType() == isc::dns::RRType::NS()) { + + const FoundIterator nsi(found.second.find(RRType::NS())); + const FoundIterator dni(found.second.find(RRType::DNAME())); + // In case we are in GLUE_OK mode, we want to store the + // highest encountered NS (but not apex) + if (glue_ok && !first_ns && i != remove_labels && + nsi != found.second.end()) { + first_ns = nsi->second; + } else if (!glue_ok && i != remove_labels && + nsi != found.second.end()) { + // Do a NS delegation, but ignore NS in glue_ok mode. Ignore + // delegation in apex LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DELEGATION). arg(accessor_->getDBName()).arg(superdomain); + result_rrset = nsi->second; result_status = DELEGATION; - } else { + // No need to go lower, found + break; + } else if (dni != found.second.end()) { + // Very similar with DNAME LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DNAME). arg(accessor_->getDBName()).arg(superdomain); + result_rrset = dni->second; result_status = DNAME; + if (result_rrset->getRdataCount() != 1) { + isc_throw(DataSourceError, "DNAME at " << superdomain << + " has " << result_rrset->getRdataCount() << + " rdata, 1 expected"); + } + break; } - // Don't search more - break; } } @@ -385,21 +388,37 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Try getting the final result and extract it // It is special if there's a CNAME or NS, DNAME is ignored here // And we don't consider the NS in origin - found = getRRset(name, &type, true, false, name != origin && !glue_ok); + + static WantedTypes final_types(empty_types + RRType::CNAME() + + RRType::NS()); + found = getRRsets(name, final_types + type, name != origin); records_found = found.first; - result_rrset = found.second; - if (result_rrset && name != origin && !glue_ok && - result_rrset->getType() == isc::dns::RRType::NS()) { + + // NS records, CNAME record and Wanted Type records + const FoundIterator nsi(found.second.find(RRType::NS())); + const FoundIterator cni(found.second.find(RRType::CNAME())); + const FoundIterator wti(found.second.find(type)); + if (name != origin && !glue_ok && nsi != found.second.end()) { + // There's a delegation at the exact node. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_DELEGATION_EXACT). arg(accessor_->getDBName()).arg(name); result_status = DELEGATION; - } else if (result_rrset && type != isc::dns::RRType::CNAME() && - result_rrset->getType() == isc::dns::RRType::CNAME()) { + result_rrset = nsi->second; + } else if (type != isc::dns::RRType::CNAME() && + cni != found.second.end()) { + // A CNAME here result_status = CNAME; - } - - if (!result_rrset && !records_found) { + result_rrset = cni->second; + if (result_rrset->getRdataCount() != 1) { + isc_throw(DataSourceError, "CNAME with " << + result_rrset->getRdataCount() << + " rdata at " << name << ", expected 1"); + } + } else if (wti != found.second.end()) { + // Just get the answer + result_rrset = wti->second; + } else if (!records_found) { // Nothing lives here. // But check if something lives below this // domain and if so, pretend something is here as well. @@ -423,8 +442,11 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, Name superdomain(name.split(i)); Name wildcard(star.concatenate(superdomain)); // TODO What do we do about DNAME here? - found = getRRset(wildcard, &type, true, false, true, - &name); + static WantedTypes wildcard_types(empty_types + + RRType::CNAME() + + RRType::NS()); + found = getRRsets(wildcard, wildcard_types + type, true, + &name); if (found.first) { if (first_ns) { // In case we are under NS, we don't @@ -445,7 +467,22 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // domain, but it could be empty non-terminal. In // that case, we need to cancel the match. records_found = true; - result_rrset = found.second; + const FoundIterator + cni(found.second.find(RRType::CNAME())); + const FoundIterator + nsi(found.second.find(RRType::NS())); + const FoundIterator wti(found.second.find(type)); + if (cni != found.second.end() && + type != RRType::CNAME()) { + result_rrset = cni->second; + result_status = CNAME; + } else if (nsi != found.second.end()) { + result_rrset = nsi->second; + result_status = DELEGATION; + } else if (wti != found.second.end()) { + result_rrset = wti->second; + } + LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_WILDCARD). arg(accessor_->getDBName()).arg(wildcard). diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 82918ac06b..6b8558b957 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -28,6 +28,9 @@ #include #include +#include +#include + namespace isc { namespace datasrc { @@ -609,54 +612,15 @@ public: boost::shared_ptr accessor_; const int zone_id_; const isc::dns::Name origin_; - - /** - * \brief Searches database for an RRset - * - * This method scans RRs of single domain specified by name and finds - * RRset with given type or any of redirection RRsets that are - * requested. - * - * This function is used internally by find(), because this part is - * called multiple times with slightly different parameters. - * - * \param name Which domain name should be scanned. - * \param type The RRType which is requested. This can be NULL, in - * which case the method will look for the redirections only. - * \param want_cname If this is true, CNAME redirection may be returned - * instead of the RRset with given type. If there's CNAME and - * something else or the CNAME has multiple RRs, it throws - * DataSourceError. - * \param want_dname If this is true, DNAME redirection may be returned - * instead. This is with type = NULL only and is not checked in - * other circumstances. If the DNAME has multiple RRs, it throws - * DataSourceError. - * \param want_ns This allows redirection by NS to be returned. If - * any other data is met as well, DataSourceError is thrown. - * \param construct_name If set to non-NULL, the resulting RRset will - * be constructed for this name instead of the queried one. This - * is useful for wildcards. - * \note It may happen that some of the above error conditions are not - * detected in some circumstances. The goal here is not to validate - * the domain in DB, but to avoid bad behaviour resulting from - * broken data. - * \return First part of the result tells if the domain contains any - * RRs. This can be used to decide between NXDOMAIN and NXRRSET. - * The second part is the RRset found (if any) with any relevant - * signatures attached to it. - * \todo This interface doesn't look very elegant. Any better idea - * would be nice. - */ - std::pair getRRset(const isc::dns::Name& - name, - const isc::dns::RRType* - type, - bool want_cname, - bool want_dname, - bool want_ns, const - isc::dns::Name* - construct_name = NULL); - + // + /// \brief Shortcut name for the result of getRRsets + typedef std::pair > + FoundRRsets; + /// \brief Just shortcut for set of types + typedef std::set WantedTypes; + FoundRRsets getRRsets(const dns::Name& name, const WantedTypes& types, + bool check_ns, + const dns::Name* construct_name = NULL); /** * \brief Checks if something lives below this domain. * From df69ad0d0231218610f68ecb2b1953ae7f28fa68 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 7 Sep 2011 15:02:07 +0200 Subject: [PATCH 679/974] [1177] Tests for NXRRSET matches Normal one and wildcard one. Empty-nonterminal is not included here. --- src/lib/datasrc/tests/database_unittest.cc | 61 +++++++++++++++++++--- src/lib/datasrc/zone.h | 2 + 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 4ed9f129a5..5395f79b4d 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -48,9 +48,11 @@ const char* const TEST_RECORDS[][5] = { {"www.example.org.", "A", "3600", "", "192.0.2.1"}, {"www.example.org.", "AAAA", "3600", "", "2001:db8::1"}, {"www.example.org.", "AAAA", "3600", "", "2001:db8::2"}, + {"www.example.org.", "NSEC", "3600", "", "www2.example.org. A AAAA NSEC RRSIG"}, + {"www.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, {"www2.example.org.", "A", "3600", "", "192.0.2.1"}, - {"www2.example.org.","AAAA", "3600", "", "2001:db8::1"}, + {"www2.example.org.", "AAAA", "3600", "", "2001:db8::1"}, {"www2.example.org.", "A", "3600", "", "192.0.2.2"}, {"cname.example.org.", "CNAME", "3600", "", "www.example.org."}, @@ -162,6 +164,8 @@ const char* const TEST_RECORDS[][5] = { // Something for wildcards {"*.wild.example.org.", "A", "3600", "", "192.0.2.5"}, {"*.wild.example.org.", "RRSIG", "3600", "A", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, + {"*.wild.example.org.", "NSEC", "3600", "", "cancel.here.wild.example.org. A NSEC RRSIG"}, + {"*.wild.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, {"cancel.here.wild.example.org.", "AAAA", "3600", "", "2001:db8::5"}, {"delegatedwild.example.org.", "NS", "3600", "", "ns.example.com."}, {"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"}, @@ -967,21 +971,25 @@ doFindTest(ZoneFinder& finder, ZoneFinder::FindResult result = finder.find(name, type, NULL, options); ASSERT_EQ(expected_result, result.code) << name << " " << type; - if (!expected_rdatas.empty()) { + if (!expected_rdatas.empty() && result.rrset) { checkRRset(result.rrset, expected_name != Name(".") ? expected_name : name, finder.getClass(), expected_type, expected_ttl, expected_rdatas); - if (!expected_sig_rdatas.empty()) { + if (!expected_sig_rdatas.empty() && result.rrset->getRRsig()) { checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ? expected_name : name, finder.getClass(), isc::dns::RRType::RRSIG(), expected_ttl, expected_sig_rdatas); - } else { + } else if (expected_sig_rdatas.empty()) { EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig()); + } else { + ADD_FAILURE() << "Missing RRSIG"; } - } else { + } else if (expected_rdatas.empty()) { EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset); + } else { + ADD_FAILURE() << "Missing result"; } } @@ -1535,8 +1543,49 @@ TYPED_TEST(DatabaseClientTest, wildcard) { } } +TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) { + // The domain exists, but doesn't have this RRType + // So we should get it's NSEC + shared_ptr finder(this->getFinder()); + + this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG"); + this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. " + "FAKEFAKEFAKE"); + doFindTest(*finder, isc::dns::Name("www.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + isc::dns::RRTTL(3600), + ZoneFinder::NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_, + Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC); +} + +TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) { + // The domain exists, but doesn't have this RRType + // So we should get it's NSEC + // + // The user will have to query us again to get the correct + // answer (eg. prove there's not an exact match) + shared_ptr finder(this->getFinder()); + + this->expected_rdatas_.push_back("cancel.here.wild.example.org. A NSEC " + "RRSIG"); + this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. " + "FAKEFAKEFAKE"); + // Note that the NSEC name should NOT be synthesized. + doFindTest(*finder, isc::dns::Name("a.wild.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + isc::dns::RRTTL(3600), + ZoneFinder::NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_, + Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC); +} + + TYPED_TEST(DatabaseClientTest, getOrigin) { - DataSourceClient::FindResult zone(this->client_->findZone(this->zname_)); + DataSourceClient::FindResult + zone(this->client_->findZone(Name("example.org"))); ASSERT_EQ(result::SUCCESS, zone.code); shared_ptr finder( dynamic_pointer_cast(zone.zone_finder)); diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index bb4f4357d8..08f67cac2c 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -61,6 +61,8 @@ public: NXRRSET, ///< There is a matching name but no RRset of the search type CNAME, ///< The search encounters and returns a CNAME RR DNAME ///< The search encounters and returns a DNAME RR + // TODO: Add WILDCARD here so we can request covering NSEC + // TODO: Add WILDCARD_NXRRSET for the same reason }; /// A helper structure to represent the search result of \c find(). From 2e1dceedf6a4f661a8d7e57757b28f9f6cb1a9b3 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 8 Sep 2011 10:59:13 +0200 Subject: [PATCH 680/974] [1177] NSEC in case of normal NXRRSET --- src/lib/datasrc/database.cc | 12 +++++++++++- src/lib/datasrc/tests/database_unittest.cc | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index b2ab948bac..24dbe86504 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -317,6 +317,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // NXDOMAIN and NXRRSET bool records_found = false; bool glue_ok(options & FIND_GLUE_OK); + bool dnssec_data(options & FIND_DNSSEC); isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; FoundRRsets found; @@ -390,7 +391,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // And we don't consider the NS in origin static WantedTypes final_types(empty_types + RRType::CNAME() + - RRType::NS()); + RRType::NS() + RRType::NSEC()); found = getRRsets(name, final_types + type, name != origin); records_found = found.first; @@ -505,6 +506,15 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } } } + } else if (dnssec_data) { + // This is the "usual" NXRRSET case + // So in case they want DNSSEC, provide the NSEC + // (which should be available already here) + result_status = NXRRSET; + const FoundIterator nci(found.second.find(RRType::NSEC())); + if (nci != found.second.end()) { + result_rrset = nci->second; + } } } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 5395f79b4d..fa67502322 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1553,7 +1553,7 @@ TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) { "20000201000000 12345 example.org. " "FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("www.example.org."), - isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, this->expected_rdatas_, this->expected_sig_rdatas_, @@ -1575,7 +1575,7 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) { "FAKEFAKEFAKE"); // Note that the NSEC name should NOT be synthesized. doFindTest(*finder, isc::dns::Name("a.wild.example.org."), - isc::dns::RRType::TXT(), isc::dns::RRType::TXT(), + isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, this->expected_rdatas_, this->expected_sig_rdatas_, From b12351c21ee92a13536aa89331cc73bd166dbe5f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 8 Sep 2011 11:30:13 +0200 Subject: [PATCH 681/974] [1177] New Result statuses To distinguish wildcard results, because then the logic will have to query for another NSEC --- src/lib/datasrc/database.cc | 18 +++++++++++++++++- src/lib/datasrc/zone.h | 21 ++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 24dbe86504..ec3ca2ed92 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -445,7 +445,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // TODO What do we do about DNAME here? static WantedTypes wildcard_types(empty_types + RRType::CNAME() + - RRType::NS()); + RRType::NS() + + RRType::NSEC()); found = getRRsets(wildcard, wildcard_types + type, true, &name); if (found.first) { @@ -472,6 +473,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, cni(found.second.find(RRType::CNAME())); const FoundIterator nsi(found.second.find(RRType::NS())); + const FoundIterator + nci(found.second.find(RRType::NSEC())); const FoundIterator wti(found.second.find(type)); if (cni != found.second.end() && type != RRType::CNAME()) { @@ -482,6 +485,19 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_status = DELEGATION; } else if (wti != found.second.end()) { result_rrset = wti->second; + } else if (dnssec_data && + nci != found.second.end()) { + // NXRRSET case in the wildcard, user wants + // a proof it's not there, include the NSEC + // + // However, we need to get the RRset in the + // name of the wildcard, not the constructed + // one, so we walk it again + found = getRRsets(wildcard, empty_types + + RRType::NSEC(), true); + result_rrset = + found.second.find(RRType::NSEC())->second; + result_status = NXRRSET; } LOG_DEBUG(logger, DBG_TRACE_DETAILED, diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 08f67cac2c..83d0cdcf05 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -54,15 +54,23 @@ public: /// /// Note: the codes are tentative. We may need more, or we may find /// some of them unnecessary as we implement more details. + /// + /// Some are synonyms of others in terms of RCODE returned to user. + /// But they help the logic to decide if it should ask for a NSEC + /// that covers something or not (for example, in case of NXRRSET, + /// the directly returned NSEC is sufficient, but with wildcard one, + /// we need to add one proving there's no exact match and this is + /// actually the best wildcard we have). Data sources that don't + /// support DNSSEC don't need to distinguish them. enum Result { SUCCESS, ///< An exact match is found. DELEGATION, ///< The search encounters a zone cut. NXDOMAIN, ///< There is no domain name that matches the search name NXRRSET, ///< There is a matching name but no RRset of the search type CNAME, ///< The search encounters and returns a CNAME RR - DNAME ///< The search encounters and returns a DNAME RR - // TODO: Add WILDCARD here so we can request covering NSEC - // TODO: Add WILDCARD_NXRRSET for the same reason + DNAME, ///< The search encounters and returns a DNAME RR + WILDCARD, ///< Succes by wildcard match, for DNSSEC + WILDCARD_NXRRSET ///< NXRRSET on wildcard, for DNSSEC }; /// A helper structure to represent the search result of \c find(). @@ -169,8 +177,8 @@ public: /// We should revisit the interface before we heavily rely on it. /// /// The \c options parameter specifies customized behavior of the search. - /// Their semantics is as follows: - /// - \c GLUE_OK Allow search under a zone cut. By default the search + /// Their semantics is as follows (they are or bit-field): + /// - \c FIND_GLUE_OK Allow search under a zone cut. By default the search /// will stop once it encounters a zone cut. If this option is specified /// it remembers information about the highest zone cut and continues /// the search until it finds an exact match for the given name or it @@ -178,6 +186,9 @@ public: /// RRsets for that name are searched just like the normal case; /// otherwise, if the search has encountered a zone cut, \c DELEGATION /// with the information of the highest zone cut will be returned. + /// - \c FIND_DNSSEC Request that DNSSEC data (like NSEC, RRSIGs) are + /// returned with the answer. It is allowed for the data source to + /// include them even when not requested. /// /// A derived version of this method may involve internal resource /// allocation, especially for constructing the resulting RRset, and may From 680f05c35753bf1f70392d25b1e6310cf46476ce Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 8 Sep 2011 11:42:20 +0200 Subject: [PATCH 682/974] [1177] Tests for wildcard result codes --- src/lib/datasrc/tests/database_unittest.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index fa67502322..ef7e71930d 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1430,21 +1430,21 @@ TYPED_TEST(DatabaseClientTest, wildcard) { "FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("a.wild.example.org"), this->qtype_, this->qtype_, this->rrttl_, - ZoneFinder::SUCCESS, this->expected_rdatas_, + ZoneFinder::WILDCARD, this->expected_rdatas_, this->expected_sig_rdatas_); doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"), - this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS, + this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::WILDCARD, this->expected_rdatas_, this->expected_sig_rdatas_); this->expected_rdatas_.clear(); this->expected_sig_rdatas_.clear(); doFindTest(*finder, isc::dns::Name("a.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_, - this->expected_sig_rdatas_); + this->rrttl_, ZoneFinder::WILDCARD_NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_); doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"), isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(), - this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_, - this->expected_sig_rdatas_); + this->rrttl_, ZoneFinder::WILDCARD_NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_); // Direct request for this wildcard this->expected_rdatas_.push_back("192.0.2.5"); @@ -1540,6 +1540,8 @@ TYPED_TEST(DatabaseClientTest, wildcard) { doFindTest(*finder, isc::dns::Name(*name), this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_, this->expected_sig_rdatas_); + // FIXME: What should be returned in this case? How does the + // DNSSEC logic handle it? } } @@ -1577,7 +1579,7 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) { doFindTest(*finder, isc::dns::Name("a.wild.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, + ZoneFinder::WILDCARD_NXRRSET, this->expected_rdatas_, this->expected_sig_rdatas_, Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC); } From f5239632a06383f2b4f6825cb6a006ceb8bea417 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 8 Sep 2011 11:57:44 +0200 Subject: [PATCH 683/974] [1177] Implement result statuses for DNSSEC --- src/lib/datasrc/database.cc | 58 +++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index ec3ca2ed92..b5f07f4c94 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -485,19 +485,24 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_status = DELEGATION; } else if (wti != found.second.end()) { result_rrset = wti->second; - } else if (dnssec_data && - nci != found.second.end()) { - // NXRRSET case in the wildcard, user wants - // a proof it's not there, include the NSEC - // - // However, we need to get the RRset in the - // name of the wildcard, not the constructed - // one, so we walk it again - found = getRRsets(wildcard, empty_types + - RRType::NSEC(), true); - result_rrset = - found.second.find(RRType::NSEC())->second; - result_status = NXRRSET; + result_status = WILDCARD; + } else { + // NXRRSET case in the wildcard + result_status = WILDCARD_NXRRSET; + if (dnssec_data && + nci != found.second.end()) { + // User wants a proof the wildcard doesn't + // contain it + // + // However, we need to get the RRset in the + // name of the wildcard, not the constructed + // one, so we walk it again + found = getRRsets(wildcard, empty_types + + RRType::NSEC(), true); + result_rrset = + found.second.find(RRType::NSEC())-> + second; + } } LOG_DEBUG(logger, DBG_TRACE_DETAILED, @@ -535,18 +540,21 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } if (!result_rrset) { - if (records_found) { - logger.debug(DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_NXRRSET) - .arg(accessor_->getDBName()).arg(name) - .arg(getClass()).arg(type); - result_status = NXRRSET; - } else { - logger.debug(DBG_TRACE_DETAILED, - DATASRC_DATABASE_FOUND_NXDOMAIN) - .arg(accessor_->getDBName()).arg(name) - .arg(getClass()).arg(type); - result_status = NXDOMAIN; + if (result_status == SUCCESS) { + // Something is not here and we didn't decide yet what + if (records_found) { + logger.debug(DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_NXRRSET) + .arg(accessor_->getDBName()).arg(name) + .arg(getClass()).arg(type); + result_status = NXRRSET; + } else { + logger.debug(DBG_TRACE_DETAILED, + DATASRC_DATABASE_FOUND_NXDOMAIN) + .arg(accessor_->getDBName()).arg(name) + .arg(getClass()).arg(type); + result_status = NXDOMAIN; + } } } else { logger.debug(DBG_TRACE_DETAILED, From 26c7bfe851f00422beb442a77d25cc0887557b79 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 8 Sep 2011 12:50:53 +0200 Subject: [PATCH 684/974] [1177] Function definiton for finding previous name It will be needed for DNSSEC logic. Not implemented nor tested: * The InMemory just throws NotImplemented * The Database one will be done in next commits --- src/lib/datasrc/database.cc | 5 ++++ src/lib/datasrc/database.h | 6 +++++ src/lib/datasrc/memory_datasrc.cc | 6 +++++ src/lib/datasrc/memory_datasrc.h | 6 +++++ .../datasrc/tests/memory_datasrc_unittest.cc | 8 +++++++ src/lib/datasrc/zone.h | 24 ++++++++++++++++++- 6 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index b5f07f4c94..2c49d0ab43 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -564,6 +564,11 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, return (FindResult(result_status, result_rrset)); } +Name +DatabaseClient::Finder::findPreviousName(const Name&) const { + return (Name::ROOT_NAME()); // TODO Implement +} + Name DatabaseClient::Finder::getOrigin() const { return (origin_); diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 6b8558b957..da14f02b69 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -589,6 +589,12 @@ public: isc::dns::RRsetList* target = NULL, const FindOptions options = FIND_DEFAULT); + /** + * \brief Implementation of ZoneFinder::findPreviousName method. + */ + virtual isc::dns::Name findPreviousName(const isc::dns::Name& query) + const; + /** * \brief The zone ID * diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 630b1c005e..2e94b678d2 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -661,6 +661,12 @@ InMemoryZoneFinder::getFileName() const { return (impl_->file_name_); } +isc::dns::Name +InMemoryZoneFinder::findPreviousName(const isc::dns::Name&) const { + isc_throw(NotImplemented, "InMemory data source doesn't support DNSSEC " + "yet, can't find previous name"); +} + /// Implementation details for \c InMemoryClient hidden from the public /// interface. /// diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index c569548f63..95f589a181 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -75,6 +75,12 @@ public: isc::dns::RRsetList* target = NULL, const FindOptions options = FIND_DEFAULT); + /// \brief Imelementation of the ZoneFinder::findPreviousName method + /// + /// This one throws NotImplemented exception, as InMemory doesn't + /// support DNSSEC currently. + virtual isc::dns::Name findPreviousName(const isc::dns::Name& query) const; + /// \brief Inserts an rrset into the zone. /// /// It puts another RRset into the zone. diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index a926935198..2b854db368 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -394,6 +394,14 @@ public: void doCancelWildcardTest(); }; +/** + * \brief Check that findPreviousName throws as it should now. + */ +TEST_F(InMemoryZoneFinderTest, findPreviousName) { + EXPECT_THROW(zone_finder_.findPreviousName(Name("www.example.org")), + isc::NotImplemented); +} + /** * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor. * diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 83d0cdcf05..1d318cabb9 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -145,7 +145,7 @@ public: //@} /// - /// \name Search Method + /// \name Search Methods /// //@{ /// Search the zone for a given pair of domain name and RR type. @@ -208,6 +208,28 @@ public: isc::dns::RRsetList* target = NULL, const FindOptions options = FIND_DEFAULT) = 0; + + /// \brief Get previous name in the zone + /// + /// Gets the previous name in the DNSSEC order. This can be used + /// to find the correct NSEC or NSEC3 records for proving nonexistenc + /// of domains. + /// + /// The concrete implementation might throw anything it thinks appropriate, + /// however it is recommended to stick to the ones listed here. The user + /// of this method should be able to handle any exceptions. + /// + /// \param query The name for which one we look for a previous one. The + /// queried name doesn't have to exist in the zone. + /// \return The preceding name + /// + /// \throw NotImplemented in case the data source backend doesn't support + /// DNSSEC. + /// \throw DataSourceError for low-level or internal datasource errors + /// (like broken connection to database, wrong data living there). + /// \throw std::bad_alloc For allocation errors. + virtual isc::dns::Name findPreviousName(const isc::dns::Name& query) + const = 0; //@} }; From c5e0db2b7d8fbdb13548e01310f623f131ea0e9c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 8 Sep 2011 14:40:41 +0200 Subject: [PATCH 685/974] [1177] Test for the previous name in DB --- src/lib/datasrc/tests/database_unittest.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index ef7e71930d..40dfaa2f86 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -171,6 +171,8 @@ const char* const TEST_RECORDS[][5] = { {"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"}, {"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"}, {"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"}, + // For finding previous, this one is the last one in the zone + {"zzz.example.org.", "NSEC", "3600", "", "example.org NSEC"}, {NULL, NULL, NULL, NULL, NULL}, }; @@ -2193,4 +2195,15 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) { ZoneFinder::SUCCESS, this->expected_rdatas_, this->empty_rdatas_); } + +TYPED_TEST(DatabaseClientTest, previous) { + shared_ptr finder(this->getFinder()); + + EXPECT_EQ(Name("www.example.org."), + finder->findPreviousName(Name("www2.example.org."))); + // Check wrap around + EXPECT_EQ(Name("zzz.example.org."), + finder->findPreviousName(Name("example.org."))); +} + } From 38af8a4225e8c82564758e8a5629da438220bc87 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 8 Sep 2011 15:34:21 +0200 Subject: [PATCH 686/974] [1177] The findPreviousName But only if the underlying DB supports it. The SQLite3 one needs to be still implemented. --- src/lib/datasrc/database.cc | 4 +-- src/lib/datasrc/database.h | 16 +++++++++++ src/lib/datasrc/sqlite3_accessor.cc | 5 ++++ src/lib/datasrc/sqlite3_accessor.h | 4 +++ src/lib/datasrc/tests/database_unittest.cc | 31 ++++++++++++++++++++++ src/lib/datasrc/zone.h | 4 +-- 6 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 2c49d0ab43..21bceed82e 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -565,8 +565,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } Name -DatabaseClient::Finder::findPreviousName(const Name&) const { - return (Name::ROOT_NAME()); // TODO Implement +DatabaseClient::Finder::findPreviousName(const Name& name) const { + return (Name(accessor_->findPreviousName(zone_id_, name.toText()))); } Name diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index da14f02b69..0ef85086d9 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -474,6 +474,22 @@ public: * \return the name of the database */ virtual const std::string& getDBName() const = 0; + + /** + * \brief It returns the previous name in DNSSEC/NSEC order. + * + * This is used in DatabaseClient::findPreviousName and does more + * or less the real work, except for working on strings. + * + * \param name The name to ask for previous of. + * \param zone_id The zone to look through. + * \return The previous name. + * + * \throw DataSourceError if there's a problem with the database. + * \throw NotImplemented if this database doesn't support DNSSEC. + */ + virtual std::string findPreviousName(int zone_id, + const std::string& name) const = 0; }; /** diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 956f447363..cfd65f1399 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -658,5 +658,10 @@ SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) { *dbparameters_, DEL_RECORD, params, "delete record from zone"); } +std::string +SQLite3Accessor::findPreviousName(int , const std::string&) const { + return ("."); // TODO Test and implement +} + } } diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index fae249b402..5727948611 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -170,6 +170,10 @@ public: /// "sqlite3_bind10.sqlite3". virtual const std::string& getDBName() const { return (database_name_); } + /// \brief Concrete implementation of the pure virtual method + virtual std::string findPreviousName(int zone_id, const std::string& name) + const; + private: /// \brief Private database data boost::scoped_ptr dbparameters_; diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 40dfaa2f86..5b572b95f0 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -229,6 +229,10 @@ public: "This database datasource can't be iterated"); } + virtual std::string findPreviousName(int, const std::string&) const { + isc_throw(isc::NotImplemented, + "This data source doesn't support DNSSEC"); + } private: const std::string database_name_; @@ -535,6 +539,26 @@ public: return (latest_clone_); } + virtual std::string findPreviousName(int id, const std::string& name) + const + { + // Hardcoded for now, but we could compute it from the data + // Maybe do it when it is needed some time in future? + if (id == -1) { + isc_throw(isc::NotImplemented, "Test not implemented behaviour"); + } else if (id == 42) { + if (name == "example.org.") { + return ("zzz.example.org."); + } else if (name == "www2.example.org.") { + return ("www.example.org."); + } else { + isc_throw(isc::Unexpected, "Unexpected name"); + } + } else { + isc_throw(isc::Unexpected, "Unknown zone ID"); + } + } + private: // The following member variables are storage and/or update work space // of the test zone. The "master"s are the real objects that contain @@ -2204,6 +2228,13 @@ TYPED_TEST(DatabaseClientTest, previous) { // Check wrap around EXPECT_EQ(Name("zzz.example.org."), finder->findPreviousName(Name("example.org."))); + // Check it doesn't crash or anything if the underlying DB throws + DataSourceClient::FindResult + zone(this->client_->findZone(Name("bad.example.org"))); + finder = dynamic_pointer_cast(zone.zone_finder); + + EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")), + isc::NotImplemented); } } diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 1d318cabb9..61fd5fb733 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -211,8 +211,8 @@ public: /// \brief Get previous name in the zone /// - /// Gets the previous name in the DNSSEC order. This can be used - /// to find the correct NSEC or NSEC3 records for proving nonexistenc + /// Gets the previous name in the DNSSEC/NSEC order. This can be used + /// to find the correct NSEC records for proving nonexistenc /// of domains. /// /// The concrete implementation might throw anything it thinks appropriate, From f8720ba467d8e107c512160a5502caf9be58a425 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 9 Sep 2011 10:54:34 +0200 Subject: [PATCH 687/974] [1177] Test for the SQLite3 find previous --- src/lib/datasrc/database.h | 4 ++-- .../datasrc/tests/sqlite3_accessor_unittest.cc | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 0ef85086d9..c9c2bc5a3a 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -481,7 +481,7 @@ public: * This is used in DatabaseClient::findPreviousName and does more * or less the real work, except for working on strings. * - * \param name The name to ask for previous of. + * \param rname The name to ask for previous of, in reversed form. * \param zone_id The zone to look through. * \return The previous name. * @@ -489,7 +489,7 @@ public: * \throw NotImplemented if this database doesn't support DNSSEC. */ virtual std::string findPreviousName(int zone_id, - const std::string& name) const = 0; + const std::string& rname) const = 0; }; /** diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 8b423f8a7a..4a15cd48cb 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -351,6 +351,22 @@ TEST_F(SQLite3AccessorTest, getRecords) { EXPECT_FALSE(context->getNext(columns)); } +TEST_F(SQLite3AccessorTest, findPrevious) { + EXPECT_EQ("dns01.example.com.", + accessor->findPreviousName(1, "com.example.dns02.")); + // Wrap around + EXPECT_EQ("www.example.com.", + accessor->findPreviousName(1, "com.example.")); +} + +TEST_F(SQLite3AccessorTest, findPreviousNoData) { + // This one doesn't hold any NSEC records, so it shouldn't work + // The underlying DB/data don't support DNSSEC, so it's not implemented + // (does it make sense? Or different exception here?) + EXPECT_THROW(accessor->findPreviousName(3, "com.example."), + isc::NotImplemented); +} + // Test fixture for creating a db that automatically deletes it before start, // and when done class SQLite3Create : public ::testing::Test { From 3f2864bf1271ca525858cf3e1fa641e3496eec59 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 9 Sep 2011 12:07:52 +0200 Subject: [PATCH 688/974] [1177] Pass reversed name to the Accessor --- src/lib/datasrc/database.cc | 3 ++- src/lib/datasrc/sqlite3_accessor.h | 2 +- src/lib/datasrc/tests/database_unittest.cc | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 21bceed82e..f82e641fab 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -566,7 +566,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, Name DatabaseClient::Finder::findPreviousName(const Name& name) const { - return (Name(accessor_->findPreviousName(zone_id_, name.toText()))); + return (Name(accessor_->findPreviousName(zone_id_, + name.reverse().toText()))); } Name diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 5727948611..c4bacad9ff 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -171,7 +171,7 @@ public: virtual const std::string& getDBName() const { return (database_name_); } /// \brief Concrete implementation of the pure virtual method - virtual std::string findPreviousName(int zone_id, const std::string& name) + virtual std::string findPreviousName(int zone_id, const std::string& rname) const; private: diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 5b572b95f0..9e58310c71 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -539,7 +539,7 @@ public: return (latest_clone_); } - virtual std::string findPreviousName(int id, const std::string& name) + virtual std::string findPreviousName(int id, const std::string& rname) const { // Hardcoded for now, but we could compute it from the data @@ -547,9 +547,9 @@ public: if (id == -1) { isc_throw(isc::NotImplemented, "Test not implemented behaviour"); } else if (id == 42) { - if (name == "example.org.") { + if (rname == "org.example.") { return ("zzz.example.org."); - } else if (name == "www2.example.org.") { + } else if (rname == "org.example.www2.") { return ("www.example.org."); } else { isc_throw(isc::Unexpected, "Unexpected name"); From c35f6b15bb6b703154e05399266dd2051ef9cfa9 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 9 Sep 2011 13:03:29 +0200 Subject: [PATCH 689/974] [1177] Implement previous name for SQLite3 --- src/lib/datasrc/sqlite3_accessor.cc | 115 +++++++++++++++--- src/lib/datasrc/tests/database_unittest.cc | 21 ++-- .../tests/sqlite3_accessor_unittest.cc | 3 + 3 files changed, 113 insertions(+), 26 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index cfd65f1399..a5ac4c7978 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -47,7 +47,9 @@ enum StatementID { ADD_RECORD = 7, DEL_RECORD = 8, ITERATE = 9, - NUM_STATEMENTS = 10 + FIND_PREVIOUS = 10, + FIND_PREVIOUS_WRAP = 11, + NUM_STATEMENTS = 12 }; const char* const text_statements[NUM_STATEMENTS] = { @@ -68,7 +70,13 @@ const char* const text_statements[NUM_STATEMENTS] = { "DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD "AND rdtype=?3 AND rdata=?4", "SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE - "WHERE zone_id = ?1 ORDER BY name, rdtype" + "WHERE zone_id = ?1 ORDER BY name, rdtype", + "SELECT name FROM records " // FIND_PREVIOUS + "WHERE zone_id=?1 AND rdtype = 'NSEC' AND " + "rname < $2 ORDER BY rname DESC LIMIT 1", // FIND_PREVIOUS_WRAP + "SELECT name FROM records " + "WHERE zone_id = ?1 AND rdtype = 'NSEC' " + "ORDER BY rname DESC LIMIT 1" }; struct SQLite3Parameters { @@ -391,6 +399,28 @@ SQLite3Accessor::getZone(const std::string& name) const { return (std::pair(false, 0)); } +namespace { + +// Conversion to plain char +const char* +convertToPlainCharInternal(const unsigned char* ucp, sqlite3 *db) { + if (ucp == NULL) { + // The field can really be NULL, in which case we return an + // empty string, or sqlite may have run out of memory, in + // which case we raise an error + if (sqlite3_errcode(db) == SQLITE_NOMEM) { + isc_throw(DataSourceError, + "Sqlite3 backend encountered a memory allocation " + "error in sqlite3_column_text()"); + } else { + return (""); + } + } + const void* p = ucp; + return (static_cast(p)); +} + +} class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext { public: // Construct an iterator for all records. When constructed this @@ -501,21 +531,8 @@ private: // In case sqlite3_column_text() returns NULL, we just make it an // empty string, unless it was caused by a memory error const char* convertToPlainChar(const unsigned char* ucp) { - if (ucp == NULL) { - // The field can really be NULL, in which case we return an - // empty string, or sqlite may have run out of memory, in - // which case we raise an error - if (sqlite3_errcode(accessor_->dbparameters_->db_) - == SQLITE_NOMEM) { - isc_throw(DataSourceError, - "Sqlite3 backend encountered a memory allocation " - "error in sqlite3_column_text()"); - } else { - return (""); - } - } - const void* p = ucp; - return (static_cast(p)); + return (convertToPlainCharInternal(ucp, + accessor_->dbparameters_->db_)); } const IteratorType iterator_type_; @@ -659,8 +676,68 @@ SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) { } std::string -SQLite3Accessor::findPreviousName(int , const std::string&) const { - return ("."); // TODO Test and implement +SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname) + const +{ + sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]); + sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS]); + + int rc = sqlite3_bind_int(dbparameters_->statements_[FIND_PREVIOUS], 1, + zone_id); + if (rc != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id << + " to SQL statement (find previous)"); + } + rc = sqlite3_bind_text(dbparameters_->statements_[FIND_PREVIOUS], 2, + rname.c_str(), -1, SQLITE_STATIC); + if (rc != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not bind name " << rname << + " to SQL statement (find previous)"); + } + + std::string result; + rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]); + if (rc == SQLITE_ROW) { + // We found it + result = convertToPlainCharInternal(sqlite3_column_text(dbparameters_-> + statements_[FIND_PREVIOUS], 0), dbparameters_->db_); + } + sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]); + + if (rc == SQLITE_DONE) { + // Nothing previous, wrap around (is it needed for anything? + // Well, just for completeness) + sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); + sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); + + int rc = sqlite3_bind_int( + dbparameters_->statements_[FIND_PREVIOUS_WRAP], 1, zone_id); + if (rc != SQLITE_OK) { + isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id << + " to SQL statement (find previous wrap)"); + } + + rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); + if (rc == SQLITE_ROW) { + // We found it + result = + convertToPlainCharInternal(sqlite3_column_text(dbparameters_-> + statements_[FIND_PREVIOUS_WRAP], 0), dbparameters_->db_); + } + sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); + + if (rc == SQLITE_DONE) { + // No NSEC records, this DB doesn't support DNSSEC + isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC"); + } + } + + if (rc != SQLITE_ROW && rc != SQLITE_DONE) { + // Some kind of error + isc_throw(SQLite3Error, "Could get data for previous name"); + } + + return (result); } } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 9e58310c71..337b8c5cf2 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -549,7 +549,8 @@ public: } else if (id == 42) { if (rname == "org.example.") { return ("zzz.example.org."); - } else if (rname == "org.example.www2.") { + } else if (rname == "org.example.www2." || + rname == "org.example.www1.") { return ("www.example.org."); } else { isc_throw(isc::Unexpected, "Unexpected name"); @@ -2228,13 +2229,19 @@ TYPED_TEST(DatabaseClientTest, previous) { // Check wrap around EXPECT_EQ(Name("zzz.example.org."), finder->findPreviousName(Name("example.org."))); - // Check it doesn't crash or anything if the underlying DB throws - DataSourceClient::FindResult - zone(this->client_->findZone(Name("bad.example.org"))); - finder = dynamic_pointer_cast(zone.zone_finder); + // Check a name that doesn't exist there + EXPECT_EQ(Name("www.example.org."), + finder->findPreviousName(Name("www1.example.org."))); + if (this->is_mock_) { // We can't really force the DB to throw + // Check it doesn't crash or anything if the underlying DB throws + DataSourceClient::FindResult + zone(this->client_->findZone(Name("bad.example.org"))); + finder = + dynamic_pointer_cast(zone.zone_finder); - EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")), - isc::NotImplemented); + EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")), + isc::NotImplemented); + } } } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 4a15cd48cb..afc1638110 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -354,6 +354,9 @@ TEST_F(SQLite3AccessorTest, getRecords) { TEST_F(SQLite3AccessorTest, findPrevious) { EXPECT_EQ("dns01.example.com.", accessor->findPreviousName(1, "com.example.dns02.")); + // A name that doesn't exist + EXPECT_EQ("dns01.example.com.", + accessor->findPreviousName(1, "com.example.dns01x.")); // Wrap around EXPECT_EQ("www.example.com.", accessor->findPreviousName(1, "com.example.")); From 05512e090c6c3cb852cebdb85ae7c12e8001603b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 9 Sep 2011 13:23:34 +0200 Subject: [PATCH 690/974] [1177] Tests for NXDOMAIN NSEC case --- src/lib/datasrc/tests/database_unittest.cc | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 337b8c5cf2..6cb2ae4377 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -552,6 +552,8 @@ public: } else if (rname == "org.example.www2." || rname == "org.example.www1.") { return ("www.example.org."); + } else if (rname == "org.example.notimplnsec.") { + isc_throw(isc::NotImplemented, "Not implemented in this test"); } else { isc_throw(isc::Unexpected, "Unexpected name"); } @@ -1611,6 +1613,37 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) { Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC); } +TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) { + // The domain doesn't exist, so we must get the right NSEC + shared_ptr finder(this->getFinder()); + + this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG"); + this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. " + "FAKEFAKEFAKE"); + doFindTest(*finder, isc::dns::Name("www1.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), + isc::dns::RRTTL(3600), + ZoneFinder::NXDOMAIN, + this->expected_rdatas_, this->expected_sig_rdatas_, + Name("www.example.org."), ZoneFinder::FIND_DNSSEC); + + // Check that if the DB doesn't support it, the exception from there + // is not propagated and it only does not include the NSEC + if (!this->is_mock_) { + return; // We don't make the real DB to throw + } + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + EXPECT_NO_THROW(doFindTest(*finder, + isc::dns::Name("notimplnsec.example.org."), + isc::dns::RRType::TXT(), + isc::dns::RRType::NSEC(), + isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, + this->expected_rdatas_, + this->expected_sig_rdatas_, + Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC)); +} TYPED_TEST(DatabaseClientTest, getOrigin) { DataSourceClient::FindResult From f847a5e079ceae0346b84fb320ed06ce9b443a63 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 9 Sep 2011 13:47:09 +0200 Subject: [PATCH 691/974] [1177] NSEC for NXDOMAIN Just for the common one, though. Empty non-terminal and wildcards must be solved yet. --- src/lib/datasrc/database.cc | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index f82e641fab..9ff455f74f 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -326,6 +326,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // In case we are in GLUE_OK mode and start matching wildcards, // we can't do it under NS, so we store it here to check isc::dns::RRsetPtr first_ns; + // This is used at multiple places + static WantedTypes nsec_types(empty_types + RRType::NSEC()); // First, do we have any kind of delegation (NS/DNAME) here? Name origin(getOrigin()); @@ -497,8 +499,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // However, we need to get the RRset in the // name of the wildcard, not the constructed // one, so we walk it again - found = getRRsets(wildcard, empty_types + - RRType::NSEC(), true); + found = getRRsets(wildcard, nsec_types, + true); result_rrset = found.second.find(RRType::NSEC())-> second; @@ -526,6 +528,32 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, break; } } + // This is the NXDOMAIN case (nothing found anywhere). If + // they wand DNSSEC data, try getting the NSEC record + if (dnssec_data && !records_found) { + try { + // Which one should contain the NSEC record? + const Name coverName(findPreviousName(name)); + // Get the record and copy it out + found = getRRsets(coverName, nsec_types, true); + const FoundIterator + nci(found.second.find(RRType::NSEC())); + if (nci != found.second.end()) { + result_status = NXDOMAIN; + result_rrset = nci->second; + } else { + // The previous doesn't contain NSEC, bug? + isc_throw(DataSourceError, "No NSEC in " + + coverName.toText() + ", but it was " + "returned as previous - " + "accessor error?"); + } + } + catch (const isc::NotImplemented&) { + // Well, they want DNSSEC, but there is no available. + // So we don't provide anything. + } + } } } else if (dnssec_data) { // This is the "usual" NXRRSET case From cb4d8443645a5c3e973b4e2477198686d8d8c507 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 9 Sep 2011 14:19:41 +0200 Subject: [PATCH 692/974] [1177] Empty non-terminal case --- src/lib/datasrc/database.cc | 51 ++++++++++++---------- src/lib/datasrc/tests/database_unittest.cc | 40 ++++++++++++++++- 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 9ff455f74f..17abd462e6 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -318,6 +318,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, bool records_found = false; bool glue_ok(options & FIND_GLUE_OK); bool dnssec_data(options & FIND_DNSSEC); + bool get_cover(false); isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; FoundRRsets found; @@ -430,6 +431,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL). arg(accessor_->getDBName()).arg(name); records_found = true; + get_cover = dnssec_data; } else { // It's not empty non-terminal. So check for wildcards. // We remove labels one by one and look for the wildcard there. @@ -521,6 +523,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } else if (hasSubdomains(wildcard.toText())) { // Empty non-terminal asterisk records_found = true; + get_cover = dnssec_data; LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_WILDCARD_EMPTY). arg(accessor_->getDBName()).arg(wildcard). @@ -531,28 +534,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // This is the NXDOMAIN case (nothing found anywhere). If // they wand DNSSEC data, try getting the NSEC record if (dnssec_data && !records_found) { - try { - // Which one should contain the NSEC record? - const Name coverName(findPreviousName(name)); - // Get the record and copy it out - found = getRRsets(coverName, nsec_types, true); - const FoundIterator - nci(found.second.find(RRType::NSEC())); - if (nci != found.second.end()) { - result_status = NXDOMAIN; - result_rrset = nci->second; - } else { - // The previous doesn't contain NSEC, bug? - isc_throw(DataSourceError, "No NSEC in " + - coverName.toText() + ", but it was " - "returned as previous - " - "accessor error?"); - } - } - catch (const isc::NotImplemented&) { - // Well, they want DNSSEC, but there is no available. - // So we don't provide anything. - } + get_cover = true; } } } else if (dnssec_data) { @@ -569,6 +551,31 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, if (!result_rrset) { if (result_status == SUCCESS) { + // Should we look for NSEC covering the name? + if (get_cover) { + try { + // Which one should contain the NSEC record? + const Name coverName(findPreviousName(name)); + // Get the record and copy it out + found = getRRsets(coverName, nsec_types, true); + const FoundIterator + nci(found.second.find(RRType::NSEC())); + if (nci != found.second.end()) { + result_status = NXDOMAIN; + result_rrset = nci->second; + } else { + // The previous doesn't contain NSEC, bug? + isc_throw(DataSourceError, "No NSEC in " + + coverName.toText() + ", but it was " + "returned as previous - " + "accessor error?"); + } + } + catch (const isc::NotImplemented&) { + // Well, they want DNSSEC, but there is no available. + // So we don't provide anything. + } + } // Something is not here and we didn't decide yet what if (records_found) { logger.debug(DBG_TRACE_DETAILED, diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 6cb2ae4377..35827f50be 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -173,6 +173,9 @@ const char* const TEST_RECORDS[][5] = { {"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"}, // For finding previous, this one is the last one in the zone {"zzz.example.org.", "NSEC", "3600", "", "example.org NSEC"}, + // For NSEC empty non-terminal + {"l.example.org.", "NSEC", "3600", "", "empty.nonterminal.example.org. NSEC"}, + {"empty.nonterminal.example.org.", "A", "3600", "", "192.0.2.1"}, {NULL, NULL, NULL, NULL, NULL}, }; @@ -549,10 +552,13 @@ public: } else if (id == 42) { if (rname == "org.example.") { return ("zzz.example.org."); + } else if (rname == "org.example.nonterminal.") { + return ("l.example.org."); } else if (rname == "org.example.www2." || rname == "org.example.www1.") { return ("www.example.org."); - } else if (rname == "org.example.notimplnsec.") { + } else if (rname == "org.example.notimplnsec." || + rname == "org.example.wild.here.") { isc_throw(isc::NotImplemented, "Not implemented in this test"); } else { isc_throw(isc::Unexpected, "Unexpected name"); @@ -1645,6 +1651,38 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) { Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC)); } +TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) { + // Same as NXDOMAIN_NSEC, but with empty non-terminal + // + // FIXME: Is the nonexistence of this node really the correct proof + // we need? + shared_ptr finder(this->getFinder()); + + this->expected_rdatas_.push_back("empty.nonterminal.example.org. NSEC"); + doFindTest(*finder, isc::dns::Name("nonterminal.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), + isc::dns::RRTTL(3600), + ZoneFinder::NXRRSET, // FIXME: Do we want to have specific code? + this->expected_rdatas_, this->expected_sig_rdatas_, + Name("l.example.org."), ZoneFinder::FIND_DNSSEC); + + // Check that if the DB doesn't support it, the exception from there + // is not propagated and it only does not include the NSEC + if (!this->is_mock_) { + return; // We don't make the real DB to throw + } + this->expected_rdatas_.clear(); + this->expected_sig_rdatas_.clear(); + EXPECT_NO_THROW(doFindTest(*finder, + isc::dns::Name("here.wild.example.org."), + isc::dns::RRType::TXT(), + isc::dns::RRType::NSEC(), + isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, + this->expected_rdatas_, + this->expected_sig_rdatas_, + Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC)); +} + TYPED_TEST(DatabaseClientTest, getOrigin) { DataSourceClient::FindResult zone(this->client_->findZone(Name("example.org"))); From 03e9f45f8a6584a373f1bd15f01f56d9296c842a Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 9 Sep 2011 14:41:52 +0200 Subject: [PATCH 693/974] [1177] Adapt the rest of the code There are some (yet unused) virtual methods and result codes, so we need to make sure the rest compiles. Just a thin layer for now. --- src/bin/auth/query.cc | 7 +++++++ src/bin/auth/tests/query_unittest.cc | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc index 898fff7da6..ab6404efad 100644 --- a/src/bin/auth/query.cc +++ b/src/bin/auth/query.cc @@ -253,6 +253,13 @@ Query::process() const { // Just empty answer with SOA in authority section putSOA(*result.zone_finder); break; + default: + // These are new result codes (WILDCARD and WILDCARD_NXRRSET) + // They should not happen from the in-memory and the database + // backend isn't used yet. + // TODO: Implement before letting the database backends in + isc_throw(isc::NotImplemented, "Unknown result code"); + break; } } } diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc index 4b8f013b02..b2d1094b9d 100644 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@ -141,6 +141,10 @@ public: // Turn this on if you want it to return RRSIGs regardless of FIND_GLUE_OK void setIncludeRRSIGAnyway(bool on) { include_rrsig_anyway_ = on; } + Name findPreviousName(const Name&) const { + isc_throw(isc::NotImplemented, "Mock doesn't support previous name"); + } + private: typedef map RRsetStore; typedef map Domains; From 4e3c6c5e5b19be3a0f970a06e3e135d1b2fae668 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Fri, 9 Sep 2011 15:40:20 +0100 Subject: [PATCH 694/974] [1202] Sort files into canonical order before processing --- src/lib/dns/gen-rdatacode.py.in | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in index b3c8da23ab..f3cd5df81a 100755 --- a/src/lib/dns/gen-rdatacode.py.in +++ b/src/lib/dns/gen-rdatacode.py.in @@ -133,7 +133,15 @@ def import_definitions(classcode2txt, typecode2txt, typeandclass): if classdir_mtime < getmtime('@srcdir@/rdata'): classdir_mtime = getmtime('@srcdir@/rdata') - for dir in list(os.listdir('@srcdir@/rdata')): + # Sort directories before iterating through them so that the directory + # list is processed in the same order on all systems. The resulting + # files should compile regardless of the order in which the components + # are included but... Having a fixed order for the directories should + # eliminate system-dependent problems. (Note that the drectory names + # in BIND 10 are ASCII, so the order should be locale-independent.) + dirlist = os.listdir('@srcdir@/rdata') + dirlist.sort() + for dir in dirlist: classdir = '@srcdir@/rdata' + os.sep + dir m = re_typecode.match(dir) if os.path.isdir(classdir) and (m != None or dir == 'generic'): @@ -145,7 +153,12 @@ def import_definitions(classcode2txt, typecode2txt, typeandclass): class_code = m.group(2) if not class_code in classcode2txt: classcode2txt[class_code] = class_txt - for file in list(os.listdir(classdir)): + + # Same considerations as directories regarding sorted order + # also apply to files. + filelist = os.listdir(classdir) + filelist.sort() + for file in filelist: file = classdir + os.sep + file m = re_typecode.match(os.path.split(file)[1]) if m != None: From 59e2ceaf7b75c38391c518436a70ac3d41b8c8be Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Fri, 9 Sep 2011 15:47:27 +0100 Subject: [PATCH 695/974] [1202] Add missing header files Update all of the RR definition files so that they are complete in themselves so that they compile in the rdataclass.cc framework, regardless of what other files are included. --- src/lib/dns/gen-rdatacode.py.in | 5 +++++ src/lib/dns/rdata/any_255/tsig_250.cc | 1 + src/lib/dns/rdata/generic/afsdb_18.cc | 1 + src/lib/dns/rdata/generic/minfo_14.cc | 1 + src/lib/dns/rdata/generic/rp_17.cc | 1 + 5 files changed, 9 insertions(+) diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in index f3cd5df81a..c984464f14 100755 --- a/src/lib/dns/gen-rdatacode.py.in +++ b/src/lib/dns/gen-rdatacode.py.in @@ -41,6 +41,10 @@ heading_txt = '''/////////////// /////////////// /////////////// +''' +rrtypeh_txt = '''// Many RR implementation files require definition of RR types. +#include + ''' def import_classdef(class_txt, file): @@ -200,6 +204,7 @@ def generate_rdatadef(file, basemtime): return rdata_deffile = open(file, 'w') rdata_deffile.write(heading_txt) + rdata_deffile.write(rrtypeh_txt) rdata_deffile.write(class_definitions) rdata_deffile.close() diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc index 04a4dc4f7b..4eb72bcf0b 100644 --- a/src/lib/dns/rdata/any_255/tsig_250.cc +++ b/src/lib/dns/rdata/any_255/tsig_250.cc @@ -23,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc index dd7fa5f861..6afc4de51e 100644 --- a/src/lib/dns/rdata/generic/afsdb_18.cc +++ b/src/lib/dns/rdata/generic/afsdb_18.cc @@ -26,6 +26,7 @@ #include using namespace std; +using namespace isc::util; using namespace isc::util::str; // BEGIN_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/minfo_14.cc b/src/lib/dns/rdata/generic/minfo_14.cc index 734fbc3102..aa5272cfc2 100644 --- a/src/lib/dns/rdata/generic/minfo_14.cc +++ b/src/lib/dns/rdata/generic/minfo_14.cc @@ -24,6 +24,7 @@ using namespace std; using namespace isc::dns; +using namespace isc::util; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE diff --git a/src/lib/dns/rdata/generic/rp_17.cc b/src/lib/dns/rdata/generic/rp_17.cc index b8b2ba21b1..781b55d6bf 100644 --- a/src/lib/dns/rdata/generic/rp_17.cc +++ b/src/lib/dns/rdata/generic/rp_17.cc @@ -24,6 +24,7 @@ using namespace std; using namespace isc::dns; +using namespace isc::util; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE From 4948e0c8965c3d39b6e1bcb1bdb12b9615260a27 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 9 Sep 2011 15:57:43 -0700 Subject: [PATCH 696/974] [1101] build setup changes so that we can use a dedicated package directory for python logging message scripts. --- configure.ac | 17 +++++ src/bin/bind10/Makefile.am | 25 +++++-- src/bin/bind10/bind10_src.py.in | 2 +- src/bin/bind10/run_bind10.sh.in | 2 +- src/bin/bind10/tests/Makefile.am | 2 +- src/bin/bindctl/Makefile.am | 2 + src/bin/bindctl/run_bindctl.sh.in | 2 +- src/bin/bindctl/tests/Makefile.am | 2 +- src/bin/cfgmgr/b10-cfgmgr.py.in | 2 +- src/bin/cfgmgr/plugins/tests/Makefile.am | 4 +- src/bin/cfgmgr/tests/Makefile.am | 4 +- src/bin/cmdctl/Makefile.am | 23 ++++-- src/bin/cmdctl/cmdctl.py.in | 2 +- src/bin/cmdctl/tests/Makefile.am | 2 +- src/bin/dhcp6/tests/Makefile.am | 2 +- src/bin/loadzone/Makefile.am | 1 + src/bin/loadzone/run_loadzone.sh.in | 2 +- src/bin/loadzone/tests/correct/Makefile.am | 2 + .../loadzone/tests/correct/correct_test.sh.in | 2 +- src/bin/loadzone/tests/error/Makefile.am | 2 + src/bin/loadzone/tests/error/error_test.sh.in | 2 +- src/bin/msgq/tests/Makefile.am | 2 +- src/bin/stats/Makefile.am | 41 +++++++--- src/bin/stats/stats.py.in | 2 +- src/bin/stats/stats_httpd.py.in | 2 +- src/bin/stats/tests/Makefile.am | 2 +- src/bin/stats/tests/isc/Makefile.am | 2 +- .../stats/tests/isc/log_messages/Makefile.am | 7 ++ .../stats/tests/isc/log_messages/__init__.py | 18 +++++ src/bin/tests/Makefile.am | 2 +- src/bin/xfrin/Makefile.am | 23 ++++-- src/bin/xfrin/tests/Makefile.am | 2 +- src/bin/xfrin/xfrin.py.in | 2 +- src/bin/xfrout/Makefile.am | 23 ++++-- src/bin/xfrout/tests/Makefile.am | 2 +- src/bin/xfrout/xfrout.py.in | 2 +- src/bin/zonemgr/Makefile.am | 22 ++++-- src/bin/zonemgr/tests/Makefile.am | 2 +- src/bin/zonemgr/zonemgr.py.in | 2 +- src/lib/python/isc/Makefile.am | 1 + src/lib/python/isc/acl/tests/Makefile.am | 2 +- src/lib/python/isc/bind10/sockcreator.py | 2 +- src/lib/python/isc/bind10/tests/Makefile.am | 2 +- src/lib/python/isc/cc/tests/Makefile.am | 2 +- src/lib/python/isc/config/Makefile.am | 48 ++++++++---- src/lib/python/isc/config/ccsession.py | 2 +- src/lib/python/isc/config/cfgmgr.py | 2 +- src/lib/python/isc/config/tests/Makefile.am | 2 +- src/lib/python/isc/datasrc/tests/Makefile.am | 2 +- src/lib/python/isc/log/tests/Makefile.am | 6 +- src/lib/python/isc/log_messages/Makefile.am | 9 +++ src/lib/python/isc/log_messages/README | 74 +++++++++++++++++++ .../python/isc/log_messages/__init__.py.in | 3 + .../python/isc/log_messages/work/Makefile.am | 12 +++ .../isc/log_messages/work/__init__.py.in | 3 + src/lib/python/isc/net/tests/Makefile.am | 2 +- src/lib/python/isc/notify/Makefile.am | 25 +++++-- src/lib/python/isc/notify/notify_out.py | 2 +- src/lib/python/isc/notify/tests/Makefile.am | 2 +- src/lib/python/isc/util/tests/Makefile.am | 2 +- 60 files changed, 368 insertions(+), 99 deletions(-) mode change 100644 => 100755 src/bin/loadzone/tests/correct/correct_test.sh.in mode change 100644 => 100755 src/bin/loadzone/tests/error/error_test.sh.in mode change 100644 => 100755 src/bin/stats/stats.py.in create mode 100644 src/bin/stats/tests/isc/log_messages/Makefile.am create mode 100644 src/bin/stats/tests/isc/log_messages/__init__.py create mode 100644 src/lib/python/isc/log_messages/Makefile.am create mode 100644 src/lib/python/isc/log_messages/README create mode 100644 src/lib/python/isc/log_messages/__init__.py.in create mode 100644 src/lib/python/isc/log_messages/work/Makefile.am create mode 100644 src/lib/python/isc/log_messages/work/__init__.py.in diff --git a/configure.ac b/configure.ac index 6ae71bf1fa..c560c19367 100644 --- a/configure.ac +++ b/configure.ac @@ -155,6 +155,18 @@ fi PYTHON_SITEPKG_DIR=${pyexecdir} AC_SUBST(PYTHON_SITEPKG_DIR) +# These will be commonly used in various Makefile.am's that need to generate +# python log messages. +PYTHON_LOGMSGPKG_DIR="\$(top_builddir)/src/lib/python/isc/log_messages" +AC_SUBST(PYTHON_LOGMSGPKG_DIR) +PYTHON_LOGMSGPKG_SRCDIR="\$(top_srcdir)/src/lib/python/isc/log_messages" +AC_SUBST(PYTHON_LOGMSGPKG_SRCDIR) + +# This is python package paths commonly used in python tests. See +# README of log_messages for why it's included. +COMMON_PYTHON_PATH="\$(abs_top_builddir)/src/lib/python/isc/log_messages:\$(abs_top_srcdir)/src/lib/python:\$(abs_top_builddir)/src/lib/python" +AC_SUBST(COMMON_PYTHON_PATH) + # Check for python development environments if test -x ${PYTHON}-config; then PYTHON_INCLUDES=`${PYTHON}-config --includes` @@ -812,6 +824,7 @@ AC_CONFIG_FILES([Makefile src/bin/stats/tests/isc/config/Makefile src/bin/stats/tests/isc/util/Makefile src/bin/stats/tests/isc/log/Makefile + src/bin/stats/tests/isc/log_messages/Makefile src/bin/stats/tests/testdata/Makefile src/bin/stats/tests/http/Makefile src/bin/usermgr/Makefile @@ -840,6 +853,8 @@ AC_CONFIG_FILES([Makefile src/lib/python/isc/config/tests/Makefile src/lib/python/isc/log/Makefile src/lib/python/isc/log/tests/Makefile + src/lib/python/isc/log_messages/Makefile + src/lib/python/isc/log_messages/work/Makefile src/lib/python/isc/net/Makefile src/lib/python/isc/net/tests/Makefile src/lib/python/isc/notify/Makefile @@ -938,6 +953,8 @@ AC_OUTPUT([doc/version.ent src/lib/python/isc/cc/tests/cc_test src/lib/python/isc/notify/tests/notify_out_test src/lib/python/isc/log/tests/log_console.py + src/lib/python/isc/log_messages/__init__.py + src/lib/python/isc/log_messages/work/__init__.py src/lib/dns/gen-rdatacode.py src/lib/python/bind10_config.py src/lib/cc/session_config.h.pre diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am index 6ab88d89ce..7dc7807f59 100644 --- a/src/bin/bind10/Makefile.am +++ b/src/bin/bind10/Makefile.am @@ -1,10 +1,16 @@ SUBDIRS = . tests sbin_SCRIPTS = bind10 -CLEANFILES = bind10 bind10_src.pyc bind10_messages.py bind10_messages.pyc +CLEANFILES = bind10 bind10_src.pyc +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.pyc pkglibexecdir = $(libexecdir)/@PACKAGE@ -pyexec_DATA = bind10_messages.py + +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ + +noinst_SCRIPTS = run_bind10.sh bind10dir = $(pkgdatadir) bind10_DATA = bob.spec @@ -13,6 +19,11 @@ EXTRA_DIST = bob.spec man_MANS = bind10.8 EXTRA_DIST += $(man_MANS) bind10.xml bind10_messages.mes +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/bind10_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/bind10_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/bind10_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/bind10_messages.py + if ENABLE_MAN bind10.8: bind10.xml @@ -20,11 +31,15 @@ bind10.8: bind10.xml endif -bind10_messages.py: bind10_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/bind10/bind10_messages.mes +$(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py : bind10_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/bind10_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/bind10_messages.py: Makefile + echo "from work.bind10_messages import *" > $@ # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix -bind10: bind10_src.py +bind10: bind10_src.py $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bind10_src.py >$@ chmod a+x $@ diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 81b24f15ff..28af8cc58f 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -66,7 +66,7 @@ import isc.cc import isc.util.process import isc.net.parse import isc.log -from bind10_messages import * +from isc.log_messages.bind10_messages import * import isc.bind10.sockcreator isc.log.init("b10-boss") diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in index b5b9721542..899ec89ede 100755 --- a/src/bin/bind10/run_bind10.sh.in +++ b/src/bin/bind10/run_bind10.sh.in @@ -23,7 +23,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/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH export PATH -PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs: +PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs: export PYTHONPATH # If necessary (rare cases), explicitly specify paths to dynamic libraries diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index f388ba1cbe..d0f36ca6b2 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -22,7 +22,7 @@ endif echo Running test: $$pytest ; \ chmod +x $(abs_builddir)/$$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ done diff --git a/src/bin/bindctl/Makefile.am b/src/bin/bindctl/Makefile.am index cd8bcb3165..700f26e30b 100644 --- a/src/bin/bindctl/Makefile.am +++ b/src/bin/bindctl/Makefile.am @@ -5,6 +5,8 @@ man_MANS = bindctl.1 EXTRA_DIST = $(man_MANS) bindctl.xml +noinst_SCRIPTS = run_bindctl.sh + python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py \ mycollections.py pythondir = $(pyexecdir)/bindctl diff --git a/src/bin/bindctl/run_bindctl.sh.in b/src/bin/bindctl/run_bindctl.sh.in index 8f6ba59351..f7d7e7828e 100755 --- a/src/bin/bindctl/run_bindctl.sh.in +++ b/src/bin/bindctl/run_bindctl.sh.in @@ -20,7 +20,7 @@ export PYTHON_EXEC BINDCTL_PATH=@abs_top_builddir@/src/bin/bindctl -PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python +PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python export PYTHONPATH # If necessary (rare cases), explicitly specify paths to dynamic libraries diff --git a/src/bin/bindctl/tests/Makefile.am b/src/bin/bindctl/tests/Makefile.am index 891d41304a..5bde145053 100644 --- a/src/bin/bindctl/tests/Makefile.am +++ b/src/bin/bindctl/tests/Makefile.am @@ -19,6 +19,6 @@ endif for pytest in $(PYTESTS) ; do \ 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/bin/bindctl:$(abs_top_srcdir)/src/bin \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/bindctl:$(abs_top_srcdir)/src/bin \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in index 8befbdf10b..2ccc4304b0 100755 --- a/src/bin/cfgmgr/b10-cfgmgr.py.in +++ b/src/bin/cfgmgr/b10-cfgmgr.py.in @@ -28,7 +28,7 @@ import os.path import isc.log isc.log.init("b10-cfgmgr") from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError, logger -from cfgmgr_messages import * +from isc.log_messages.cfgmgr_messages import * isc.util.process.rename() diff --git a/src/bin/cfgmgr/plugins/tests/Makefile.am b/src/bin/cfgmgr/plugins/tests/Makefile.am index 07b7a858c2..d4b87505ea 100644 --- a/src/bin/cfgmgr/plugins/tests/Makefile.am +++ b/src/bin/cfgmgr/plugins/tests/Makefile.am @@ -19,8 +19,8 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ - env B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \ + B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \ $(LIBRARY_PATH_PLACEHOLDER) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am index 99f8cc9471..41edc8edb6 100644 --- a/src/bin/cfgmgr/tests/Makefile.am +++ b/src/bin/cfgmgr/tests/Makefile.am @@ -21,9 +21,9 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ chmod +x $(abs_builddir)/$$pytest ; \ - env TESTDATA_PATH=$(abs_srcdir)/testdata \ + TESTDATA_PATH=$(abs_srcdir)/testdata \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/python/isc/config \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/python/isc/config \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ done diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am index fcd23f8662..fff44b08e1 100644 --- a/src/bin/cmdctl/Makefile.am +++ b/src/bin/cmdctl/Makefile.am @@ -3,7 +3,9 @@ SUBDIRS = . tests pkglibexecdir = $(libexecdir)/@PACKAGE@ pkglibexec_SCRIPTS = b10-cmdctl -pyexec_DATA = cmdctl_messages.py + +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ b10_cmdctldir = $(pkgdatadir) @@ -19,7 +21,14 @@ b10_cmdctl_DATA += cmdctl.spec EXTRA_DIST = $(CMDCTL_CONFIGURATIONS) -CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec cmdctl_messages.py cmdctl_messages.pyc +CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.pyc + +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/cmdctl_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/cmdctl_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/cmdctl_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/cmdctl_messages.py man_MANS = b10-cmdctl.8 EXTRA_DIST += $(man_MANS) b10-cmdctl.xml cmdctl_messages.mes @@ -34,11 +43,15 @@ endif cmdctl.spec: cmdctl.spec.pre $(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@ -cmdctl_messages.py: cmdctl_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/cmdctl/cmdctl_messages.mes +$(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py : cmdctl_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/cmdctl_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/cmdctl_messages.py: Makefile + echo "from work.cmdctl_messages import *" > $@ # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix -b10-cmdctl: cmdctl.py cmdctl_messages.py +b10-cmdctl: cmdctl.py $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py $(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@ chmod a+x $@ diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in index 2f89894066..fcd69b88da 100755 --- a/src/bin/cmdctl/cmdctl.py.in +++ b/src/bin/cmdctl/cmdctl.py.in @@ -47,7 +47,7 @@ import isc.net.parse from optparse import OptionParser, OptionValueError from hashlib import sha1 from isc.util import socketserver_mixin -from cmdctl_messages import * +from isc.log_messages.cmdctl_messages import * # TODO: these debug-levels are hard-coded here; we are planning on # creating a general set of debug levels, see ticket #1074. When done, diff --git a/src/bin/cmdctl/tests/Makefile.am b/src/bin/cmdctl/tests/Makefile.am index e4ec9d43f7..6bb9fba8e7 100644 --- a/src/bin/cmdctl/tests/Makefile.am +++ b/src/bin/cmdctl/tests/Makefile.am @@ -19,7 +19,7 @@ endif for pytest in $(PYTESTS) ; do \ 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/bin/cmdctl \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cmdctl \ CMDCTL_SPEC_PATH=$(abs_top_builddir)/src/bin/cmdctl \ CMDCTL_SRC_PATH=$(abs_top_srcdir)/src/bin/cmdctl \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 4a0e9186de..79f5968310 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -15,7 +15,7 @@ endif check-local: for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ $(LIBRARY_PATH_PLACEHOLDER) \ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ diff --git a/src/bin/loadzone/Makefile.am b/src/bin/loadzone/Makefile.am index 74d4dd4ddc..a235d68bd0 100644 --- a/src/bin/loadzone/Makefile.am +++ b/src/bin/loadzone/Makefile.am @@ -1,5 +1,6 @@ SUBDIRS = . tests/correct tests/error bin_SCRIPTS = b10-loadzone +noinst_SCRIPTS = run_loadzone.sh CLEANFILES = b10-loadzone diff --git a/src/bin/loadzone/run_loadzone.sh.in b/src/bin/loadzone/run_loadzone.sh.in index 95de3961aa..e6db99c7e7 100755 --- a/src/bin/loadzone/run_loadzone.sh.in +++ b/src/bin/loadzone/run_loadzone.sh.in @@ -18,7 +18,7 @@ PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@} export PYTHON_EXEC -PYTHONPATH=@abs_top_builddir@/src/lib/python +PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python export PYTHONPATH # If necessary (rare cases), explicitly specify paths to dynamic libraries diff --git a/src/bin/loadzone/tests/correct/Makefile.am b/src/bin/loadzone/tests/correct/Makefile.am index 3507bfaaaa..73c8a3464a 100644 --- a/src/bin/loadzone/tests/correct/Makefile.am +++ b/src/bin/loadzone/tests/correct/Makefile.am @@ -13,6 +13,8 @@ EXTRA_DIST += ttl2.db EXTRA_DIST += ttlext.db EXTRA_DIST += example.db +noinst_SCRIPTS = correct_test.sh + # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = diff --git a/src/bin/loadzone/tests/correct/correct_test.sh.in b/src/bin/loadzone/tests/correct/correct_test.sh.in old mode 100644 new mode 100755 index 509d8e5f13..d944451034 --- a/src/bin/loadzone/tests/correct/correct_test.sh.in +++ b/src/bin/loadzone/tests/correct/correct_test.sh.in @@ -18,7 +18,7 @@ PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@} export PYTHON_EXEC -PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python +PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python export PYTHONPATH LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone diff --git a/src/bin/loadzone/tests/error/Makefile.am b/src/bin/loadzone/tests/error/Makefile.am index 87bb1cf8ac..57f7857b21 100644 --- a/src/bin/loadzone/tests/error/Makefile.am +++ b/src/bin/loadzone/tests/error/Makefile.am @@ -12,6 +12,8 @@ EXTRA_DIST += keyerror3.db EXTRA_DIST += originerr1.db EXTRA_DIST += originerr2.db +noinst_SCRIPTS = error_test.sh + # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = diff --git a/src/bin/loadzone/tests/error/error_test.sh.in b/src/bin/loadzone/tests/error/error_test.sh.in old mode 100644 new mode 100755 index d1d6bd1837..94c5edb9d3 --- a/src/bin/loadzone/tests/error/error_test.sh.in +++ b/src/bin/loadzone/tests/error/error_test.sh.in @@ -18,7 +18,7 @@ PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@} export PYTHON_EXEC -PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python +PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python export PYTHONPATH LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone diff --git a/src/bin/msgq/tests/Makefile.am b/src/bin/msgq/tests/Makefile.am index 50c1e6eefb..ee9ffd87fc 100644 --- a/src/bin/msgq/tests/Makefile.am +++ b/src/bin/msgq/tests/Makefile.am @@ -19,7 +19,7 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_builddir)/src/bin/msgq:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/msgq \ BIND10_TEST_SOCKET_FILE=$(builddir)/test_msgq_socket.sock \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index e830f65d60..0e189b193f 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -7,12 +7,17 @@ pkglibexec_SCRIPTS = b10-stats b10-stats-httpd b10_statsdir = $(pkgdatadir) b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl -pyexec_DATA = stats_messages.py stats_httpd_messages.py + +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py +nodist_pylogmessage_PYTHON += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ CLEANFILES = b10-stats stats.pyc CLEANFILES += b10-stats-httpd stats_httpd.pyc -CLEANFILES += stats_messages.py stats_messages.pyc -CLEANFILES += stats_httpd_messages.py stats_httpd_messages.pyc +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.pyc +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.pyc man_MANS = b10-stats.8 b10-stats-httpd.8 EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml @@ -20,6 +25,16 @@ EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl EXTRA_DIST += stats_messages.mes stats_httpd_messages.mes +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/stats_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/stats_messages.py + +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/stats_httpd_messages.py + if ENABLE_MAN b10-stats.8: b10-stats.xml @@ -30,18 +45,26 @@ b10-stats-httpd.8: b10-stats-httpd.xml endif -stats_messages.py: stats_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/stats/stats_messages.mes +$(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py : stats_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/stats_messages.mes -stats_httpd_messages.py: stats_httpd_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/stats/stats_httpd_messages.mes +$(PYTHON_LOGMSGPKG_DIR)/stats_messages.py: Makefile + echo "from work.stats_messages import *" > $@ + +$(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py : stats_httpd_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/stats_httpd_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py: Makefile + echo "from work.stats_httpd_messages import *" > $@ # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix -b10-stats: stats.py +b10-stats: stats.py $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats.py >$@ chmod a+x $@ -b10-stats-httpd: stats_httpd.py +b10-stats-httpd: stats_httpd.py $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats_httpd.py >$@ chmod a+x $@ diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in old mode 100644 new mode 100755 index 51d712b33d..afed54405a --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -26,7 +26,7 @@ from isc.config.ccsession import ModuleCCSession, create_answer from isc.cc import Session, SessionError import isc.log -from stats_messages import * +from isc.log_messages.stats_messages import * isc.log.init("b10-stats") logger = isc.log.Logger("stats") diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index 74298cf288..6be6adf26c 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -35,7 +35,7 @@ import isc.config import isc.util.process import isc.log -from stats_httpd_messages import * +from isc.log_messages.stats_httpd_messages import * isc.log.init("b10-stats-httpd") logger = isc.log.Logger("stats-httpd") diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index dad6c48bbc..bb9369fba6 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -21,7 +21,7 @@ endif for pytest in $(PYTESTS) ; do \ 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/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \ B10_FROM_SOURCE=$(abs_top_srcdir) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/stats/tests/isc/Makefile.am b/src/bin/stats/tests/isc/Makefile.am index d31395d404..bdfa1eb38a 100644 --- a/src/bin/stats/tests/isc/Makefile.am +++ b/src/bin/stats/tests/isc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = cc config util log +SUBDIRS = cc config util log log_messages EXTRA_DIST = __init__.py CLEANFILES = __init__.pyc diff --git a/src/bin/stats/tests/isc/log_messages/Makefile.am b/src/bin/stats/tests/isc/log_messages/Makefile.am new file mode 100644 index 0000000000..90b449940a --- /dev/null +++ b/src/bin/stats/tests/isc/log_messages/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = __init__.py stats_messages.py stats_httpd_messages.py +CLEANFILES = __init__.pyc stats_messages.pyc stats_httpd_messages.pyc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/log_messages/__init__.py b/src/bin/stats/tests/isc/log_messages/__init__.py new file mode 100644 index 0000000000..58e99e36ce --- /dev/null +++ b/src/bin/stats/tests/isc/log_messages/__init__.py @@ -0,0 +1,18 @@ +# 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 a fake package that acts as a forwarder to the real package. +''' diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am index 034152c173..446c002b8f 100644 --- a/src/bin/tests/Makefile.am +++ b/src/bin/tests/Makefile.am @@ -22,6 +22,6 @@ endif echo Running test: $$pytest ; \ chmod +x $(abs_builddir)/$$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ done diff --git a/src/bin/xfrin/Makefile.am b/src/bin/xfrin/Makefile.am index 0af9be6d5e..f88b4d27f8 100644 --- a/src/bin/xfrin/Makefile.am +++ b/src/bin/xfrin/Makefile.am @@ -6,14 +6,23 @@ pkglibexec_SCRIPTS = b10-xfrin b10_xfrindir = $(pkgdatadir) b10_xfrin_DATA = xfrin.spec -pyexec_DATA = xfrin_messages.py -CLEANFILES = b10-xfrin xfrin.pyc xfrinlog.py xfrin_messages.py xfrin_messages.pyc +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ + +CLEANFILES = b10-xfrin xfrin.pyc xfrinlog.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.pyc man_MANS = b10-xfrin.8 EXTRA_DIST = $(man_MANS) b10-xfrin.xml EXTRA_DIST += xfrin.spec xfrin_messages.mes +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/xfrin_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/xfrin_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/xfrin_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/xfrin_messages.py + if ENABLE_MAN b10-xfrin.8: b10-xfrin.xml @@ -22,11 +31,15 @@ b10-xfrin.8: b10-xfrin.xml endif # Define rule to build logging source files from message file -xfrin_messages.py: xfrin_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/xfrin/xfrin_messages.mes +$(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py : xfrin_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/xfrin_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/xfrin_messages.py: Makefile + echo "from work.xfrin_messages import *" > $@ # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix -b10-xfrin: xfrin.py xfrin_messages.py +b10-xfrin: xfrin.py $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrin.py >$@ chmod a+x $@ diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am index 0f485aa2fd..8d2a450571 100644 --- a/src/bin/xfrin/tests/Makefile.am +++ b/src/bin/xfrin/tests/Makefile.am @@ -19,6 +19,6 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \ + PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(COMMON_PYTHON_PATH) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in index 07de8f0c9e..8845b426f5 100755 --- a/src/bin/xfrin/xfrin.py.in +++ b/src/bin/xfrin/xfrin.py.in @@ -29,7 +29,7 @@ from isc.config.ccsession import * from isc.notify import notify_out import isc.util.process import isc.net.parse -from xfrin_messages import * +from isc.log_messages.xfrin_messages import * isc.log.init("b10-xfrin") logger = isc.log.Logger("xfrin") diff --git a/src/bin/xfrout/Makefile.am b/src/bin/xfrout/Makefile.am index c5492adf09..aae2e6aa4a 100644 --- a/src/bin/xfrout/Makefile.am +++ b/src/bin/xfrout/Makefile.am @@ -6,13 +6,22 @@ pkglibexec_SCRIPTS = b10-xfrout b10_xfroutdir = $(pkgdatadir) b10_xfrout_DATA = xfrout.spec -pyexec_DATA = xfrout_messages.py -CLEANFILES= b10-xfrout xfrout.pyc xfrout.spec xfrout_messages.py xfrout_messages.pyc +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ + +CLEANFILES = b10-xfrout xfrout.pyc xfrout.spec +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.pyc man_MANS = b10-xfrout.8 EXTRA_DIST = $(man_MANS) b10-xfrout.xml xfrout_messages.mes +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/xfrout_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/xfrout_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/xfrout_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/xfrout_messages.py + if ENABLE_MAN b10-xfrout.8: b10-xfrout.xml @@ -21,14 +30,18 @@ b10-xfrout.8: b10-xfrout.xml endif # Define rule to build logging source files from message file -xfrout_messages.py: xfrout_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/xfrout/xfrout_messages.mes +$(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py : xfrout_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/xfrout_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/xfrout_messages.py: Makefile + echo "from work.xfrout_messages import *" > $@ xfrout.spec: xfrout.spec.pre $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.spec.pre >$@ # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix -b10-xfrout: xfrout.py xfrout_messages.py +b10-xfrout: xfrout.py $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.py >$@ chmod a+x $@ diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index 2f1e2eaef5..8a643d7300 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -20,6 +20,6 @@ endif echo Running test: $$pytest ; \ chmod +x $(abs_builddir)/$$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/xfrout:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ done diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index fe42c5485b..144a1b8055 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -35,7 +35,7 @@ import errno from optparse import OptionParser, OptionValueError from isc.util import socketserver_mixin -from xfrout_messages import * +from isc.log_messages.xfrout_messages import * isc.log.init("b10-xfrout") logger = isc.log.Logger("xfrout") diff --git a/src/bin/zonemgr/Makefile.am b/src/bin/zonemgr/Makefile.am index 34e662276d..637b31aef5 100644 --- a/src/bin/zonemgr/Makefile.am +++ b/src/bin/zonemgr/Makefile.am @@ -6,14 +6,22 @@ pkglibexec_SCRIPTS = b10-zonemgr b10_zonemgrdir = $(pkgdatadir) b10_zonemgr_DATA = zonemgr.spec -pyexec_DATA = zonemgr_messages.py + +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec -CLEANFILES += zonemgr_messages.py zonemgr_messages.pyc +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.pyc man_MANS = b10-zonemgr.8 EXTRA_DIST = $(man_MANS) b10-zonemgr.xml zonemgr_messages.mes +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/zonemgr_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/zonemgr_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/zonemgr_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/zonemgr_messages.py + if ENABLE_MAN b10-zonemgr.8: b10-zonemgr.xml @@ -22,13 +30,17 @@ b10-zonemgr.8: b10-zonemgr.xml endif # Build logging source file from message files -zonemgr_messages.py: zonemgr_messages.mes - $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/zonemgr/zonemgr_messages.mes +$(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py : zonemgr_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/zonemgr_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/zonemgr_messages.py: Makefile + echo "from work.zonemgr_messages import *" > $@ zonemgr.spec: zonemgr.spec.pre $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.spec.pre >$@ -b10-zonemgr: zonemgr.py +b10-zonemgr: zonemgr.py $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.py >$@ chmod a+x $@ diff --git a/src/bin/zonemgr/tests/Makefile.am b/src/bin/zonemgr/tests/Makefile.am index 97f9b5e6ca..6e8c35b913 100644 --- a/src/bin/zonemgr/tests/Makefile.am +++ b/src/bin/zonemgr/tests/Makefile.am @@ -20,6 +20,6 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in index d4de6a8d14..5c8d9b54ba 100755 --- a/src/bin/zonemgr/zonemgr.py.in +++ b/src/bin/zonemgr/zonemgr.py.in @@ -37,7 +37,7 @@ from isc.datasrc import sqlite3_ds from optparse import OptionParser, OptionValueError from isc.config.ccsession import * import isc.util.process -from zonemgr_messages import * +from isc.log_messages.zonemgr_messages import * # Initialize logging for called modules. isc.log.init("b10-zonemgr") diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am index d94100bc94..2b63da6c5b 100644 --- a/src/lib/python/isc/Makefile.am +++ b/src/lib/python/isc/Makefile.am @@ -1,4 +1,5 @@ SUBDIRS = datasrc cc config log net notify util testutils acl bind10 +SUBDIRS += log_messages python_PYTHON = __init__.py diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am index 87781d72bc..569353ff34 100644 --- a/src/lib/python/isc/acl/tests/Makefile.am +++ b/src/lib/python/isc/acl/tests/Makefile.am @@ -19,7 +19,7 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ - env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/isc/python/acl/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/isc/python/acl/.libs \ $(LIBRARY_PATH_PLACEHOLDER) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/lib/python/isc/bind10/sockcreator.py b/src/lib/python/isc/bind10/sockcreator.py index 9fcc74e74c..8e5b019536 100644 --- a/src/lib/python/isc/bind10/sockcreator.py +++ b/src/lib/python/isc/bind10/sockcreator.py @@ -17,7 +17,7 @@ import socket import struct import os import subprocess -from bind10_messages import * +from isc.log_messages.bind10_messages import * from libutil_io_python import recv_fd logger = isc.log.Logger("boss") diff --git a/src/lib/python/isc/bind10/tests/Makefile.am b/src/lib/python/isc/bind10/tests/Makefile.am index f498b86840..0cc12ff48a 100644 --- a/src/lib/python/isc/bind10/tests/Makefile.am +++ b/src/lib/python/isc/bind10/tests/Makefile.am @@ -22,7 +22,7 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/lib/python/isc/cc/tests/Makefile.am b/src/lib/python/isc/cc/tests/Makefile.am index 4e49501458..2dc6a58f38 100644 --- a/src/lib/python/isc/cc/tests/Makefile.am +++ b/src/lib/python/isc/cc/tests/Makefile.am @@ -23,7 +23,7 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \ + PYTHONPATH=$(COMMON_PYTHON_PATH) \ BIND10_TEST_SOCKET_FILE=$(builddir)/test_socket.sock \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am index 312ad3348c..62a372222e 100644 --- a/src/lib/python/isc/config/Makefile.am +++ b/src/lib/python/isc/config/Makefile.am @@ -1,27 +1,47 @@ SUBDIRS = . tests python_PYTHON = __init__.py ccsession.py cfgmgr.py config_data.py module_spec.py -pyexec_DATA = cfgmgr_messages.py $(top_builddir)/src/lib/python/config_messages.py - pythondir = $(pyexecdir)/isc/config -# Define rule to build logging source files from message file -cfgmgr_messages.py: cfgmgr_messages.mes - $(top_builddir)/src/lib/log/compiler/message \ - -p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py +BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py +nodist_pylogmessage_PYTHON += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ -$(top_builddir)/src/lib/python/config_messages.py: config_messages.mes - $(top_builddir)/src/lib/log/compiler/message \ - -p -d $(top_builddir)/src/lib/python \ - $(top_srcdir)/src/lib/python/isc/config/config_messages.mes - -CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc -CLEANFILES += $(top_builddir)/src/lib/python/config_messages.py -CLEANFILES += $(top_builddir)/src/lib/python/config_messages.pyc +CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.pyc +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.pyc CLEANDIRS = __pycache__ EXTRA_DIST = cfgmgr_messages.mes config_messages.mes +BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/cfgmgr_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/cfgmgr_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/cfgmgr_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/cfgmgr_messages.py + +BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/config_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/config_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/config_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/config_messages.py + +# Define rule to build logging source files from message file +$(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py : cfgmgr_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/cfgmgr_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/cfgmgr_messages.py: Makefile + echo "from work.cfgmgr_messages import *" > $@ + +$(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py : config_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/config_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/config_messages.py: Makefile + echo "from work.config_messages import *" > $@ + clean-local: rm -rf $(CLEANDIRS) diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index ba7724ce55..d07df1e3e8 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -43,7 +43,7 @@ from isc.util.file import path_search import bind10_config from isc.log import log_config_update import json -from config_messages import * +from isc.log_messages.config_messages import * logger = isc.log.Logger("config") diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py index 1db9fd389f..9996a19852 100644 --- a/src/lib/python/isc/config/cfgmgr.py +++ b/src/lib/python/isc/config/cfgmgr.py @@ -32,7 +32,7 @@ from isc.config import ccsession, config_data, module_spec from isc.util.file import path_search import bind10_config import isc.log -from cfgmgr_messages import * +from isc.log_messages.cfgmgr_messages import * logger = isc.log.Logger("cfgmgr") diff --git a/src/lib/python/isc/config/tests/Makefile.am b/src/lib/python/isc/config/tests/Makefile.am index 47ccc41af3..7b48f43ad4 100644 --- a/src/lib/python/isc/config/tests/Makefile.am +++ b/src/lib/python/isc/config/tests/Makefile.am @@ -21,7 +21,7 @@ endif for pytest in $(PYTESTS) ; do \ 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/config \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/python/isc/config \ 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 \ diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am index 6f6d15731d..1a50fd3695 100644 --- a/src/lib/python/isc/datasrc/tests/Makefile.am +++ b/src/lib/python/isc/datasrc/tests/Makefile.am @@ -23,7 +23,7 @@ endif for pytest in $(PYTESTS) ; do \ 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 \ + PYTHONPATH=:$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/python/isc/log \ TESTDATA_PATH=$(abs_srcdir)/testdata \ TESTDATA_WRITE_PATH=$(abs_builddir) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am index a23887c697..8fa3746956 100644 --- a/src/lib/python/isc/log/tests/Makefile.am +++ b/src/lib/python/isc/log/tests/Makefile.am @@ -16,7 +16,7 @@ endif check-local: chmod +x $(abs_builddir)/log_console.py $(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 \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/python/isc/log \ $(abs_srcdir)/check_output.sh $(abs_builddir)/log_console.py $(abs_srcdir)/console.out if ENABLE_PYTHON_COVERAGE touch $(abs_top_srcdir)/.coverage @@ -26,7 +26,7 @@ endif for pytest in $(PYTESTS_NOGEN) ; do \ 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 \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(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 ; \ @@ -34,7 +34,7 @@ endif echo Running test: $$pytest ; \ chmod +x $(abs_builddir)/$$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 \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(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_builddir)/$$pytest || exit ; \ done diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am new file mode 100644 index 0000000000..dc060a12d8 --- /dev/null +++ b/src/lib/python/isc/log_messages/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = work + +EXTRA_DIST = __init__.py +CLEANFILES = __init__.pyc + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/lib/python/isc/log_messages/README b/src/lib/python/isc/log_messages/README new file mode 100644 index 0000000000..85a2538638 --- /dev/null +++ b/src/lib/python/isc/log_messages/README @@ -0,0 +1,74 @@ +This is a placeholder package for logging messages of various modules +in the form of python scripts. This package is expected to be installed +somewhere like /python3.x/site-packages/isc/log_messages +and each message script is expected to be imported as +"isc.log_messages.some_module_messages". + +We also need to allow in-source test code to get access to the message +scripts in the same manner. That's why the package is stored in the +directory that shares the same trailing part as the install directory, +i.e., isc/log_messages. + +Furthermore, we need to support a build mode using a separate build +tree (such as in the case with 'make distcheck'). In that case if an +application (via a test script) imports "isc.log_messages.xxx", it +would try to import the module under the source tree, where the +generated message script doesn't exist. So, in the source directory +(i.e., here) we provide dummy scripts that subsequently import the +same name of module under the "work" sub-package. The caller +application is assumed to have /src/lib/python/isc/log_messages +in its module search path (this is done by including +$(COMMON_PYTHON_PATH) in the PYTHONPATH environment variable), +which ensures the right directory is chosen. + +A python module or program that defines its own log messages needs to +make sure that the setup described above is implemented. It's a +complicated process, but can generally be done by following a common +pattern. The following are a sample snippet for Makefile.in for a +module named "mymodule" (which is supposed to be generated from a file +"mymodule_messages.mes"). In many cases it should work simply by +replacing 'mymodule' with the actual module name. + +==================== begin Makefile.am additions =================== +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ + +CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.pyc + +EXTRA_DIST = mymodule_messages.mes + +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/mymodule_messages.py + +$(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py : mymodule_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR) -p $(srcdir)/mymodule_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py: Makefile + echo "from work.mymodule_messages import *" > $@ + +# This rule ensures mymodule_messages.py is (re)generated as a result of +# 'make'. If there's no other appropriate target, specify +# mymodule_messages.py in BUILT_SOURCES. +mymodule: $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py +===================== end Makefile.am additions ==================== + +Notes: +- "nodist_" prefix is important. Without this, 'make distcheck' tries + to make _messages.py before actually starting the main build, which + would fail because the message compiler isn't built yet. +- "pylogmessage" is a prefix for python scripts that define log + messages and are expected to be installed in the common isc/log_messages + directory. It's intentionally named differently from the common + "python" prefix (as in python_PYTHON), because the latter may be + used for other scripts in the same Makefile.am file. +- $(PYTHON_LOGMSGPKG_DIR) should be set to point to this directory (or + the corresponding build directory if it's different) by the + configure script. $(PYTHON_LOGMSGPKG_SRCDIR) should be set to point + to this directory by the configure script. +- The four lines starting from BUILT_SOURCES and the second make rule + are for preparing the dummy python file under the source tree. + Alternatively, you could directly add the file in this directory. diff --git a/src/lib/python/isc/log_messages/__init__.py.in b/src/lib/python/isc/log_messages/__init__.py.in new file mode 100644 index 0000000000..d222b8c0c4 --- /dev/null +++ b/src/lib/python/isc/log_messages/__init__.py.in @@ -0,0 +1,3 @@ +""" +This is an in-source forwarder package redirecting to work/* scripts. +""" diff --git a/src/lib/python/isc/log_messages/work/Makefile.am b/src/lib/python/isc/log_messages/work/Makefile.am new file mode 100644 index 0000000000..9bc5e0ff35 --- /dev/null +++ b/src/lib/python/isc/log_messages/work/Makefile.am @@ -0,0 +1,12 @@ +# .py is generated in the builddir by the configure script so that test +# scripts can refer to it when a separate builddir is used. + +python_PYTHON = __init__.py + +pythondir = $(pyexecdir)/isc/log_messages/ + +CLEANFILES = __init__.pyc +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/lib/python/isc/log_messages/work/__init__.py.in b/src/lib/python/isc/log_messages/work/__init__.py.in new file mode 100644 index 0000000000..991f10acd4 --- /dev/null +++ b/src/lib/python/isc/log_messages/work/__init__.py.in @@ -0,0 +1,3 @@ +""" +This package is a placeholder for python scripts of log messages. +""" diff --git a/src/lib/python/isc/net/tests/Makefile.am b/src/lib/python/isc/net/tests/Makefile.am index 3a04f17b40..371df59bb5 100644 --- a/src/lib/python/isc/net/tests/Makefile.am +++ b/src/lib/python/isc/net/tests/Makefile.am @@ -19,6 +19,6 @@ endif for pytest in $(PYTESTS) ; do \ 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/dns/python/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/lib/python/isc/notify/Makefile.am b/src/lib/python/isc/notify/Makefile.am index a23a1ffb4b..496c64d217 100644 --- a/src/lib/python/isc/notify/Makefile.am +++ b/src/lib/python/isc/notify/Makefile.am @@ -1,21 +1,30 @@ SUBDIRS = . tests python_PYTHON = __init__.py notify_out.py -pyexec_DATA = $(top_builddir)/src/lib/python/notify_out_messages.py - pythondir = $(pyexecdir)/isc/notify -$(top_builddir)/src/lib/python/notify_out_messages.py: notify_out_messages.mes - $(top_builddir)/src/lib/log/compiler/message \ - -p -d $(top_builddir)/src/lib/python \ - $(top_srcdir)/src/lib/python/isc/notify/notify_out_messages.mes +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ EXTRA_DIST = notify_out_messages.mes -CLEANFILES = $(top_builddir)/src/lib/python/notify_out_messages.pyc -CLEANFILES += $(top_builddir)/src/lib/python/notify_out_messages.py +BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/notify_out_messages.py +CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/notify_out_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/notify_out_messages.pyc +EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/notify_out_messages.py + +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.pyc CLEANDIRS = __pycache__ +$(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py : notify_out_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/notify_out_messages.mes + +$(PYTHON_LOGMSGPKG_DIR)/notify_out_messages.py: Makefile + echo "from work.notify_out_messages import *" > $@ + clean-local: rm -rf $(CLEANDIRS) diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py index f1e02cab29..6b91c87d55 100644 --- a/src/lib/python/isc/notify/notify_out.py +++ b/src/lib/python/isc/notify/notify_out.py @@ -23,7 +23,7 @@ import errno from isc.datasrc import sqlite3_ds from isc.net import addr import isc -from notify_out_messages import * +from isc.log_messages.notify_out_messages import * logger = isc.log.Logger("notify_out") diff --git a/src/lib/python/isc/notify/tests/Makefile.am b/src/lib/python/isc/notify/tests/Makefile.am index 1427d93c12..b0fdb19339 100644 --- a/src/lib/python/isc/notify/tests/Makefile.am +++ b/src/lib/python/isc/notify/tests/Makefile.am @@ -18,7 +18,7 @@ if ENABLE_PYTHON_COVERAGE endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ - env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \ $(LIBRARY_PATH_PLACEHOLDER) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/lib/python/isc/util/tests/Makefile.am b/src/lib/python/isc/util/tests/Makefile.am index c3d35c2ac9..db44c864ff 100644 --- a/src/lib/python/isc/util/tests/Makefile.am +++ b/src/lib/python/isc/util/tests/Makefile.am @@ -19,6 +19,6 @@ endif for pytest in $(PYTESTS) ; do \ 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/dns/python/.libs \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done From 5859f177250685fbd49c9562ffc3e984b9d5ebae Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 9 Sep 2011 16:00:49 -0700 Subject: [PATCH 697/974] [1101] fix BUILT_SOURCES overriding --- src/bin/stats/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index 0e189b193f..0ed4c1d78f 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -30,7 +30,7 @@ CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_messages.py CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_messages.pyc EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/stats_messages.py -BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py +BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.pyc EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/stats_httpd_messages.py From 3a9dc4fbd7dab867829ba3299d86c2f5b58d864f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 9 Sep 2011 16:10:23 -0700 Subject: [PATCH 698/974] [1101] missing new files --- .../isc/log_messages/stats_httpd_messages.py | 16 ++++++++++++++++ .../tests/isc/log_messages/stats_messages.py | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/bin/stats/tests/isc/log_messages/stats_httpd_messages.py create mode 100644 src/bin/stats/tests/isc/log_messages/stats_messages.py diff --git a/src/bin/stats/tests/isc/log_messages/stats_httpd_messages.py b/src/bin/stats/tests/isc/log_messages/stats_httpd_messages.py new file mode 100644 index 0000000000..0adb0f0d48 --- /dev/null +++ b/src/bin/stats/tests/isc/log_messages/stats_httpd_messages.py @@ -0,0 +1,16 @@ +# 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. + +from work.stats_httpd_messages import * diff --git a/src/bin/stats/tests/isc/log_messages/stats_messages.py b/src/bin/stats/tests/isc/log_messages/stats_messages.py new file mode 100644 index 0000000000..c05a6a8581 --- /dev/null +++ b/src/bin/stats/tests/isc/log_messages/stats_messages.py @@ -0,0 +1,16 @@ +# 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. + +from work.stats_messages import * From 3089b6fd6eff650dc06c0698b80eae1595986677 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 9 Sep 2011 16:19:43 -0700 Subject: [PATCH 699/974] [1101] additional documentation --- src/lib/python/isc/log_messages/README | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/log_messages/README b/src/lib/python/isc/log_messages/README index 85a2538638..ebb33370cb 100644 --- a/src/lib/python/isc/log_messages/README +++ b/src/lib/python/isc/log_messages/README @@ -71,4 +71,7 @@ Notes: to this directory by the configure script. - The four lines starting from BUILT_SOURCES and the second make rule are for preparing the dummy python file under the source tree. - Alternatively, you could directly add the file in this directory. + Note that EXTRA_DIST is specified so that the python script is + placed in the source tree (not only in the build tree when these + two are different). If you don't like this trick, you could + directly add the file in this directory alternatively. From 61aaae27e12db2a00cfde674931e5080e733e6b3 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Mon, 12 Sep 2011 10:14:03 +0100 Subject: [PATCH 700/974] [1202] Added include of rrtype.h in RR template file --- src/lib/dns/rdata/template.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/dns/rdata/template.cc b/src/lib/dns/rdata/template.cc index d9f08ee91b..e85f82c336 100644 --- a/src/lib/dns/rdata/template.cc +++ b/src/lib/dns/rdata/template.cc @@ -18,6 +18,7 @@ #include #include #include +#include using namespace std; using namespace isc::util; From 06341cb6cdbd5ff57c376f7b0b25aba4a35bab86 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Mon, 12 Sep 2011 11:32:00 -0400 Subject: [PATCH 701/974] [1144] tests for DS and DLV combined into a single parameterized test --- src/lib/dns/tests/Makefile.am | 2 +- src/lib/dns/tests/rdata_ds_like_unittest.cc | 125 ++++++++++++++++++++ src/lib/dns/tests/rdata_ds_unittest.cc | 99 ---------------- 3 files changed, 126 insertions(+), 100 deletions(-) create mode 100644 src/lib/dns/tests/rdata_ds_like_unittest.cc delete mode 100644 src/lib/dns/tests/rdata_ds_unittest.cc diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index bd6fbe2a6e..6d60bee53b 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -34,7 +34,7 @@ run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc run_unittests_SOURCES += rdata_dname_unittest.cc run_unittests_SOURCES += rdata_opt_unittest.cc run_unittests_SOURCES += rdata_dnskey_unittest.cc -run_unittests_SOURCES += rdata_ds_unittest.cc +run_unittests_SOURCES += rdata_ds_like_unittest.cc run_unittests_SOURCES += rdata_nsec_unittest.cc run_unittests_SOURCES += rdata_nsec3_unittest.cc run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc new file mode 100644 index 0000000000..444d41f0d3 --- /dev/null +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -0,0 +1,125 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using isc::UnitTestUtil; +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +// hacks to make templates work +template +class RRTYPE : public RRType { +public: + RRTYPE(); +}; + +template<> RRTYPE::RRTYPE() : RRType(RRType::DS()) {} +template<> RRTYPE::RRTYPE() : RRType(RRType::DLV()) {} + +namespace { +template +class Rdata_DS_LIKE_Test : public RdataTest { +protected: + static DS_LIKE const rdata_ds_like; +}; + +string ds_like_txt("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); + +template +DS_LIKE const Rdata_DS_LIKE_Test::rdata_ds_like(ds_like_txt); + +// The list of types we want to test. +typedef testing::Types Implementations; + +TYPED_TEST_CASE(Rdata_DS_LIKE_Test, Implementations); + +TYPED_TEST(Rdata_DS_LIKE_Test, toText_DS_LIKE) { + EXPECT_EQ(ds_like_txt, + Rdata_DS_LIKE_Test::rdata_ds_like.toText()); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, badText_DS_LIKE) { + EXPECT_THROW(const TypeParam ds_like2("99999 5 2 BEEF"), InvalidRdataText); + EXPECT_THROW(const TypeParam ds_like2("11111 555 2 BEEF"), + InvalidRdataText); + EXPECT_THROW(const TypeParam ds_like2("11111 5 22222 BEEF"), + InvalidRdataText); + EXPECT_THROW(const TypeParam ds_like2("11111 5 2"), InvalidRdataText); + EXPECT_THROW(const TypeParam ds_like2("GARBAGE IN"), InvalidRdataText); +} + +// this test currently fails; we must fix it, and then migrate the test to +// badText_DS_LIKE +TYPED_TEST(Rdata_DS_LIKE_Test, DISABLED_badText_DS_LIKE) { + // no space between the digest type and the digest. + EXPECT_THROW(const TypeParam ds_like2( + "12892 5 2F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"), InvalidRdataText); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, createFromWire_DS_LIKE) { + EXPECT_EQ(0, Rdata_DS_LIKE_Test::rdata_ds_like.compare( + *rdataFactoryFromFile(RRTYPE(), RRClass::IN(), + "rdata_ds_fromWire"))); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, getTag_DS_LIKE) { + EXPECT_EQ(12892, Rdata_DS_LIKE_Test::rdata_ds_like.getTag()); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, toWireRenderer) { + Rdata_DS_LIKE_Test::renderer.skip(2); + TypeParam rdata_ds_like(ds_like_txt); + rdata_ds_like.toWire(Rdata_DS_LIKE_Test::renderer); + + vector data; + UnitTestUtil::readWireData("rdata_ds_fromWire", data); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + static_cast + (Rdata_DS_LIKE_Test::obuffer.getData()) + 2, + Rdata_DS_LIKE_Test::obuffer.getLength() - 2, + &data[2], data.size() - 2); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, toWireBuffer) { + TypeParam rdata_ds_like(ds_like_txt); + rdata_ds_like.toWire(Rdata_DS_LIKE_Test::obuffer); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, compare) { + // trivial case: self equivalence + EXPECT_EQ(0, + TypeParam(ds_like_txt).compare(TypeParam(ds_like_txt))); + + // TODO: need more tests +} + +} diff --git a/src/lib/dns/tests/rdata_ds_unittest.cc b/src/lib/dns/tests/rdata_ds_unittest.cc deleted file mode 100644 index 59886208cf..0000000000 --- a/src/lib/dns/tests/rdata_ds_unittest.cc +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC 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. - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -using isc::UnitTestUtil; -using namespace std; -using namespace isc::dns; -using namespace isc::util; -using namespace isc::dns::rdata; - -namespace { -class Rdata_DS_Test : public RdataTest { - // there's nothing to specialize -}; - -string ds_txt("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" - "5F0EB5C777586DE18DA6B5"); -const generic::DS rdata_ds(ds_txt); - -TEST_F(Rdata_DS_Test, toText_DS) { - EXPECT_EQ(ds_txt, rdata_ds.toText()); -} - -TEST_F(Rdata_DS_Test, badText_DS) { - EXPECT_THROW(const generic::DS ds2("99999 5 2 BEEF"), InvalidRdataText); - EXPECT_THROW(const generic::DS ds2("11111 555 2 BEEF"), InvalidRdataText); - EXPECT_THROW(const generic::DS ds2("11111 5 22222 BEEF"), InvalidRdataText); - EXPECT_THROW(const generic::DS ds2("11111 5 2"), InvalidRdataText); - EXPECT_THROW(const generic::DS ds2("GARBAGE IN"), InvalidRdataText); -} - -// this test currently fails; we must fix it, and then migrate the test to -// badText_DS -TEST_F(Rdata_DS_Test, DISABLED_badText_DS) { - // no space between the digest type and the digest. - EXPECT_THROW(const generic::DS ds2( - "12892 5 2F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" - "5F0EB5C777586DE18DA6B5"), InvalidRdataText); -} - -TEST_F(Rdata_DS_Test, createFromWire_DS) { - EXPECT_EQ(0, rdata_ds.compare( - *rdataFactoryFromFile(RRType::DS(), RRClass::IN(), - "rdata_ds_fromWire"))); -} - -TEST_F(Rdata_DS_Test, getTag_DS) { - EXPECT_EQ(12892, rdata_ds.getTag()); -} - -TEST_F(Rdata_DS_Test, toWireRenderer) { - renderer.skip(2); - generic::DS rdata_ds(ds_txt); - rdata_ds.toWire(renderer); - - vector data; - UnitTestUtil::readWireData("rdata_ds_fromWire", data); - EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, - static_cast(obuffer.getData()) + 2, - obuffer.getLength() - 2, &data[2], data.size() - 2); -} - -TEST_F(Rdata_DS_Test, toWireBuffer) { - generic::DS rdata_ds(ds_txt); - rdata_ds.toWire(obuffer); -} - -TEST_F(Rdata_DS_Test, compare) { - // trivial case: self equivalence - EXPECT_EQ(0, generic::DS(ds_txt).compare(generic::DS(ds_txt))); - - // TODO: need more tests -} - -} From f8b10842465d60483e3bc9827e06115ea8081bfc Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 12 Sep 2011 14:36:10 -0700 Subject: [PATCH 702/974] [1144] missing 'this->' for TYPED_TEST --- src/lib/dns/tests/rdata_ds_like_unittest.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc index 444d41f0d3..62937df23e 100644 --- a/src/lib/dns/tests/rdata_ds_like_unittest.cc +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -87,8 +87,8 @@ TYPED_TEST(Rdata_DS_LIKE_Test, DISABLED_badText_DS_LIKE) { TYPED_TEST(Rdata_DS_LIKE_Test, createFromWire_DS_LIKE) { EXPECT_EQ(0, Rdata_DS_LIKE_Test::rdata_ds_like.compare( - *rdataFactoryFromFile(RRTYPE(), RRClass::IN(), - "rdata_ds_fromWire"))); + *this->rdataFactoryFromFile(RRTYPE(), RRClass::IN(), + "rdata_ds_fromWire"))); } TYPED_TEST(Rdata_DS_LIKE_Test, getTag_DS_LIKE) { From 84d7ae48d44e055cb16e3900cf2c4b2262f6a6da Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 12 Sep 2011 16:28:03 -0700 Subject: [PATCH 703/974] [1177] constify --- src/lib/datasrc/database.cc | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 17abd462e6..08e642be23 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -295,7 +295,7 @@ DatabaseClient::Finder::hasSubdomains(const std::string& name) { // Some manipulation with RRType sets namespace { -std::set empty_types; +const std::set empty_types; // To conveniently put the RRTypes into the sets. This is not really clean // design, but it is hidden inside this file and makes the calls much more @@ -328,14 +328,14 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // we can't do it under NS, so we store it here to check isc::dns::RRsetPtr first_ns; // This is used at multiple places - static WantedTypes nsec_types(empty_types + RRType::NSEC()); + static const WantedTypes nsec_types(empty_types + RRType::NSEC()); // First, do we have any kind of delegation (NS/DNAME) here? - Name origin(getOrigin()); - size_t origin_label_count(origin.getLabelCount()); + const Name origin(getOrigin()); + const size_t origin_label_count(origin.getLabelCount()); // Number of labels in the last known non-empty domain size_t last_known(origin_label_count); - size_t current_label_count(name.getLabelCount()); + const size_t current_label_count(name.getLabelCount()); // This is how many labels we remove to get origin size_t remove_labels(current_label_count - origin_label_count); @@ -346,8 +346,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, for (int i(remove_labels); i > 0; --i) { Name superdomain(name.split(i)); // Look if there's NS or DNAME (but ignore the NS in origin) - static WantedTypes delegation_types(empty_types + RRType::DNAME() + - RRType::NS()); + static const WantedTypes delegation_types(empty_types + + RRType::DNAME() + + RRType::NS()); found = getRRsets(superdomain, delegation_types, i != remove_labels); if (found.first) { // It contains some RRs, so it exists. @@ -393,8 +394,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // It is special if there's a CNAME or NS, DNAME is ignored here // And we don't consider the NS in origin - static WantedTypes final_types(empty_types + RRType::CNAME() + - RRType::NS() + RRType::NSEC()); + static const WantedTypes final_types(empty_types + RRType::CNAME() + + RRType::NS() + RRType::NSEC()); found = getRRsets(name, final_types + type, name != origin); records_found = found.first; @@ -438,19 +439,19 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Go up to first non-empty domain. remove_labels = current_label_count - last_known; - Name star("*"); + const Name star("*"); for (size_t i(1); i <= remove_labels; ++ i) { // Construct the name with * // TODO: Once the underlying DatabaseAccessor takes // string, do the concatenation on strings, not // Names - Name superdomain(name.split(i)); - Name wildcard(star.concatenate(superdomain)); + const Name superdomain(name.split(i)); + const Name wildcard(star.concatenate(superdomain)); // TODO What do we do about DNAME here? - static WantedTypes wildcard_types(empty_types + - RRType::CNAME() + - RRType::NS() + - RRType::NSEC()); + static const WantedTypes wildcard_types(empty_types + + RRType::CNAME() + + RRType::NS() + + RRType::NSEC()); found = getRRsets(wildcard, wildcard_types + type, true, &name); if (found.first) { From b59f898456b33294d71a333d3f3b4fe9dc81e3dd Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 13 Sep 2011 11:08:10 +0200 Subject: [PATCH 704/974] [1245] split up headers and make dynamic library None of the .cc files are now included directly anymore, and the main source file for the python module contains only the module definition. All functionality is moved to a dynamic library that is also usable by other wrapper modules. Also split up the rest of the source files into header and code files, and made them more consistent with the rest. Added conversion from/to PyObject* for some of the types (the ones I needed so far for trac1179) --- src/lib/dns/python/Makefile.am | 38 ++- src/lib/dns/python/edns_python.cc | 149 +++++---- src/lib/dns/python/edns_python.h | 49 +++ src/lib/dns/python/message_python.cc | 168 +++++----- src/lib/dns/python/message_python.h | 54 ++++ src/lib/dns/python/name_python.cc | 17 +- src/lib/dns/python/name_python.h | 19 ++ src/lib/dns/python/opcode_python.cc | 129 ++++---- src/lib/dns/python/opcode_python.h | 53 ++++ src/lib/dns/python/pydnspp.cc | 34 +- src/lib/dns/python/pydnspp_common.cc | 40 +++ src/lib/dns/python/question_python.cc | 154 ++++++---- src/lib/dns/python/question_python.h | 51 +++ src/lib/dns/python/rdata_python.cc | 171 ++++++----- src/lib/dns/python/rdata_python.h | 52 ++++ src/lib/dns/python/rrclass_python.cc | 426 ++++++++++++++------------ src/lib/dns/python/rrclass_python.h | 83 +++++ src/lib/dns/python/rrset_python.cc | 261 ++++++++++------ src/lib/dns/python/rrset_python.h | 86 ++++++ src/lib/dns/python/rrttl_python.cc | 164 +++++----- src/lib/dns/python/rrttl_python.h | 54 ++++ src/lib/dns/python/rrtype_python.cc | 208 +++++++------ src/lib/dns/python/rrtype_python.h | 84 +++++ 23 files changed, 1711 insertions(+), 833 deletions(-) create mode 100644 src/lib/dns/python/edns_python.h create mode 100644 src/lib/dns/python/message_python.h create mode 100644 src/lib/dns/python/opcode_python.h create mode 100644 src/lib/dns/python/question_python.h create mode 100644 src/lib/dns/python/rdata_python.h create mode 100644 src/lib/dns/python/rrclass_python.h create mode 100644 src/lib/dns/python/rrset_python.h create mode 100644 src/lib/dns/python/rrttl_python.h create mode 100644 src/lib/dns/python/rrtype_python.h diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am index 6c4ef54782..eb834be65d 100644 --- a/src/lib/dns/python/Makefile.am +++ b/src/lib/dns/python/Makefile.am @@ -4,17 +4,34 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CXXFLAGS = $(B10_CXXFLAGS) -pyexec_LTLIBRARIES = pydnspp.la -pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc pydnspp_towire.h -pydnspp_la_SOURCES += name_python.cc name_python.h -pydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h -pydnspp_la_SOURCES += rcode_python.cc rcode_python.h -pydnspp_la_SOURCES += tsigkey_python.cc tsigkey_python.h -pydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h -pydnspp_la_SOURCES += tsig_rdata_python.cc tsig_rdata_python.h -pydnspp_la_SOURCES += tsigrecord_python.cc tsigrecord_python.h -pydnspp_la_SOURCES += tsig_python.cc tsig_python.h +lib_LTLIBRARIES = libpydnspp.la +libpydnspp_la_SOURCES = pydnspp_common.cc pydnspp_towire.h +libpydnspp_la_SOURCES += name_python.cc name_python.h +libpydnspp_la_SOURCES += rrset_python.cc rrset_python.h +libpydnspp_la_SOURCES += rrclass_python.cc rrclass_python.h +libpydnspp_la_SOURCES += rrtype_python.cc rrtype_python.h +libpydnspp_la_SOURCES += rrttl_python.cc rrttl_python.h +libpydnspp_la_SOURCES += rdata_python.cc rdata_python.h +libpydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h +libpydnspp_la_SOURCES += rcode_python.cc rcode_python.h +libpydnspp_la_SOURCES += opcode_python.cc opcode_python.h +libpydnspp_la_SOURCES += question_python.cc question_python.h +libpydnspp_la_SOURCES += tsigkey_python.cc tsigkey_python.h +libpydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h +libpydnspp_la_SOURCES += tsig_rdata_python.cc tsig_rdata_python.h +libpydnspp_la_SOURCES += tsigrecord_python.cc tsigrecord_python.h +libpydnspp_la_SOURCES += tsig_python.cc tsig_python.h +libpydnspp_la_SOURCES += edns_python.cc edns_python.h +libpydnspp_la_SOURCES += message_python.cc message_python.h +libpydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) +libpydnspp_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) +libpydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS) + + + +pyexec_LTLIBRARIES = pydnspp.la +pydnspp_la_SOURCES = pydnspp.cc pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) # Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be # placed after -Wextra defined in AM_CXXFLAGS @@ -40,4 +57,5 @@ EXTRA_DIST += tsigerror_python_inc.cc pydnspp_la_LDFLAGS += -module pydnspp_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la pydnspp_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la +pydnspp_la_LIBADD += libpydnspp.la pydnspp_la_LIBADD += $(PYTHON_LIB) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index 83c3bfa3b6..9781a603bd 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -12,13 +12,27 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + #include #include +#include +#include + +#include "edns_python.h" +#include "name_python.h" +#include "rrclass_python.h" +#include "rrtype_python.h" +#include "rrttl_python.h" +#include "rdata_python.h" +#include "messagerenderer_python.h" +#include "pydnspp_common.h" using namespace isc::dns; using namespace isc::util; using namespace isc::dns::rdata; +using namespace isc::dns::python; // // Definition of the classes @@ -33,13 +47,6 @@ namespace { // EDNS // -// The s_* Class simply covers one instantiation of the object - -class s_EDNS : public PyObject { -public: - EDNS* edns; -}; - // // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other @@ -103,60 +110,6 @@ PyMethodDef EDNS_methods[] = { { NULL, NULL, 0, NULL } }; -// This defines the complete type for reflection in python and -// parsing of PyObject* to s_EDNS -// Most of the functions are not actually implemented and NULL here. -PyTypeObject edns_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.EDNS", - sizeof(s_EDNS), // tp_basicsize - 0, // tp_itemsize - (destructor)EDNS_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - EDNS_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The EDNS class encapsulates DNS extensions " - "provided by the EDNSx protocol.", - NULL, // tp_traverse - NULL, // tp_clear - NULL, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - EDNS_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)EDNS_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - EDNS* createFromRR(const Name& name, const RRClass& rrclass, const RRType& rrtype, const RRTTL& rrttl, const Rdata& rdata, uint8_t& extended_rcode) @@ -203,8 +156,8 @@ EDNS_init(s_EDNS* self, PyObject* args) { // in this context so that we can share the try-catch logic with // EDNS_createFromRR() (see below). uint8_t extended_rcode; - self->edns = createFromRR(*name->cppobj, *rrclass->rrclass, - *rrtype->rrtype, *rrttl->rrttl, + self->edns = createFromRR(*name->cppobj, *rrclass->cppobj, + *rrtype->cppobj, *rrttl->rrttl, *rdata->rdata, extended_rcode); return (self->edns != NULL ? 0 : -1); } @@ -245,7 +198,7 @@ EDNS_toWire(const s_EDNS* const self, PyObject* args) { if (PyArg_ParseTuple(args, "Ob", &bytes, &extended_rcode) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; - + OutputBuffer buffer(0); self->edns->toWire(buffer, extended_rcode); PyObject* rd_bytes = PyBytes_FromStringAndSize( @@ -334,14 +287,14 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) { return (NULL); } - edns_obj->edns = createFromRR(*name->cppobj, *rrclass->rrclass, - *rrtype->rrtype, *rrttl->rrttl, + edns_obj->edns = createFromRR(*name->cppobj, *rrclass->cppobj, + *rrtype->cppobj, *rrttl->rrttl, *rdata->rdata, extended_rcode); if (edns_obj->edns != NULL) { PyObject* extrcode_obj = Py_BuildValue("B", extended_rcode); return (Py_BuildValue("OO", edns_obj, extrcode_obj)); } - + Py_DECREF(edns_obj); return (NULL); } @@ -355,6 +308,64 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) { } // end of anonymous namespace // end of EDNS +namespace isc { +namespace dns { +namespace python { + +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_EDNS +// Most of the functions are not actually implemented and NULL here. +PyTypeObject edns_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.EDNS", + sizeof(s_EDNS), // tp_basicsize + 0, // tp_itemsize + (destructor)EDNS_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + EDNS_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The EDNS class encapsulates DNS extensions " + "provided by the EDNSx protocol.", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + EDNS_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)EDNS_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + // Module Initialization, all statics are initialized here bool initModulePart_EDNS(PyObject* mod) { @@ -373,3 +384,7 @@ initModulePart_EDNS(PyObject* mod) { return (true); } + +} // end namespace python +} // end namespace dns +} // end namespace isc diff --git a/src/lib/dns/python/edns_python.h b/src/lib/dns/python/edns_python.h new file mode 100644 index 0000000000..23c229dc80 --- /dev/null +++ b/src/lib/dns/python/edns_python.h @@ -0,0 +1,49 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_EDNS_H +#define __PYTHON_EDNS_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +class s_EDNS : public PyObject { +public: + EDNS* edns; +}; + +extern PyTypeObject edns_type; + +bool initModulePart_EDNS(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_EDNS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 00596f87d0..5b7cf48c84 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -12,24 +12,33 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + #include #include #include #include +#include +#include +#include "name_python.h" +#include "question_python.h" +#include "edns_python.h" +#include "rcode_python.h" +#include "opcode_python.h" +#include "rrset_python.h" +#include "message_python.h" +#include "messagerenderer_python.h" +#include "tsig_python.h" +#include "tsigrecord_python.h" +#include "pydnspp_common.h" + +using namespace std; using namespace isc::dns; +using namespace isc::dns::python; using namespace isc::util; namespace { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the initModulePart -// function at the end of this file -// -PyObject* po_MessageTooShort; -PyObject* po_InvalidMessageSection; -PyObject* po_InvalidMessageOperation; -PyObject* po_InvalidMessageUDPSize; // // Definition of the classes @@ -43,12 +52,6 @@ PyObject* po_InvalidMessageUDPSize; // Message // -// The s_* Class simply coverst one instantiation of the object -class s_Message : public PyObject { -public: - Message* message; -}; - // // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other @@ -178,59 +181,6 @@ PyMethodDef Message_methods[] = { { NULL, NULL, 0, NULL } }; -// This defines the complete type for reflection in python and -// parsing of PyObject* to s_Message -// Most of the functions are not actually implemented and NULL here. -PyTypeObject message_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.Message", - sizeof(s_Message), // tp_basicsize - 0, // tp_itemsize - (destructor)Message_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - Message_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The Message class encapsulates a standard DNS message.", - NULL, // tp_traverse - NULL, // tp_clear - NULL, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - Message_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)Message_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - int Message_init(s_Message* self, PyObject* args) { int i; @@ -597,7 +547,7 @@ Message_addQuestion(s_Message* self, PyObject* args) { } self->message->addQuestion(question->question); - + Py_RETURN_NONE; } @@ -682,7 +632,7 @@ PyObject* Message_toWire(s_Message* self, PyObject* args) { s_MessageRenderer* mr; s_TSIGContext* tsig_ctx = NULL; - + if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr, &tsigcontext_type, &tsig_ctx)) { try { @@ -727,7 +677,7 @@ Message_fromWire(s_Message* self, PyObject* args) { if (!PyArg_ParseTuple(args, "y#", &b, &len)) { return (NULL); } - + InputBuffer inbuf(b, len); try { self->message->fromWire(inbuf); @@ -747,6 +697,75 @@ Message_fromWire(s_Message* self, PyObject* args) { } } +} // end of unnamed namespace + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the initModulePart +// function at the end of this file +// +PyObject* po_MessageTooShort; +PyObject* po_InvalidMessageSection; +PyObject* po_InvalidMessageOperation; +PyObject* po_InvalidMessageUDPSize; + +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_Message +// Most of the functions are not actually implemented and NULL here. +PyTypeObject message_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.Message", + sizeof(s_Message), // tp_basicsize + 0, // tp_itemsize + (destructor)Message_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + Message_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The Message class encapsulates a standard DNS message.", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + Message_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)Message_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + // Module Initialization, all statics are initialized here bool initModulePart_Message(PyObject* mod) { @@ -754,7 +773,7 @@ initModulePart_Message(PyObject* mod) { return (false); } Py_INCREF(&message_type); - + // Class variables // These are added to the tp_dict of the type object // @@ -814,4 +833,7 @@ initModulePart_Message(PyObject* mod) { return (true); } -} // end of unnamed namespace + +} // end python namespace +} // end dns namespace +} // end isc namespace diff --git a/src/lib/dns/python/message_python.h b/src/lib/dns/python/message_python.h new file mode 100644 index 0000000000..91b1040c64 --- /dev/null +++ b/src/lib/dns/python/message_python.h @@ -0,0 +1,54 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_MESSAGE_H +#define __PYTHON_MESSAGE_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +extern PyObject* po_MessageTooShort; +extern PyObject* po_InvalidMessageSection; +extern PyObject* po_InvalidMessageOperation; +extern PyObject* po_InvalidMessageUDPSize; + +class s_Message : public PyObject { +public: + isc::dns::Message* message; +}; + +extern PyTypeObject message_type; + +bool initModulePart_Message(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_MESSAGE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index d00c6f7c89..63f22debc5 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -663,7 +663,7 @@ initModulePart_Name(PyObject* mod) { // // Name // - + if (PyType_Ready(&name_type) < 0) { return (false); } @@ -684,7 +684,7 @@ initModulePart_Name(PyObject* mod) { PyModule_AddObject(mod, "Name", reinterpret_cast(&name_type)); - + // Add the exceptions to the module po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL); @@ -722,6 +722,19 @@ createNameObject(const Name& source) { container.set(new Name(source)); return (container.release()); } + +bool +PyName_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &name_type)); +} + +Name& +PyName_ToName(PyObject* name_obj) { + s_Name* name = static_cast(name_obj); + return (*name->cppobj); +} + + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h index f8e793d7c5..27d79c23ba 100644 --- a/src/lib/dns/python/name_python.h +++ b/src/lib/dns/python/name_python.h @@ -74,6 +74,25 @@ bool initModulePart_Name(PyObject* mod); /// This function is expected to be called with in a try block /// followed by necessary setup for python exception. PyObject* createNameObject(const Name& source); + +/// \brief Checks if the given python object is a Name object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type Name, false otherwise +bool PyName_Check(PyObject* obj); + +/// \brief Returns a reference to the Name object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type Name; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyName_Check() +/// +/// \note This is not a copy; if the Name is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param name_obj The name object to convert +Name& PyName_ToName(PyObject* name_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index 0e2a30b8a0..a9490a709e 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -12,9 +12,17 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + #include +#include "pydnspp_common.h" +#include "opcode_python.h" +#include "edns_python.h" + using namespace isc::dns; +using namespace isc::dns::python; +using namespace isc::util; // // Declaration of the custom exceptions (None for this class) @@ -31,12 +39,6 @@ namespace { // // Opcode // -class s_Opcode : public PyObject { -public: - s_Opcode() : opcode(NULL), static_code(false) {} - const Opcode* opcode; - bool static_code; -}; int Opcode_init(s_Opcode* const self, PyObject* args); void Opcode_destroy(s_Opcode* const self); @@ -103,57 +105,6 @@ PyMethodDef Opcode_methods[] = { { NULL, NULL, 0, NULL } }; -PyTypeObject opcode_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.Opcode", - sizeof(s_Opcode), // tp_basicsize - 0, // tp_itemsize - (destructor)Opcode_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - Opcode_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The Opcode class objects represent standard OPCODEs " - "of the header section of DNS messages.", - NULL, // tp_traverse - NULL, // tp_clear - (richcmpfunc)Opcode_richcmp, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - Opcode_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)Opcode_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - int Opcode_init(s_Opcode* const self, PyObject* args) { @@ -297,7 +248,7 @@ Opcode_RESERVED15(const s_Opcode*) { return (Opcode_createStatic(Opcode::RESERVED15())); } -PyObject* +PyObject* Opcode_richcmp(const s_Opcode* const self, const s_Opcode* const other, const int op) { @@ -336,6 +287,63 @@ Opcode_richcmp(const s_Opcode* const self, const s_Opcode* const other, Py_RETURN_FALSE; } +} // end of unnamed namespace + +namespace isc { +namespace dns { +namespace python { + +PyTypeObject opcode_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.Opcode", + sizeof(s_Opcode), // tp_basicsize + 0, // tp_itemsize + (destructor)Opcode_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + Opcode_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The Opcode class objects represent standard OPCODEs " + "of the header section of DNS messages.", + NULL, // tp_traverse + NULL, // tp_clear + (richcmpfunc)Opcode_richcmp, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + Opcode_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)Opcode_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + // Module Initialization, all statics are initialized here bool initModulePart_Opcode(PyObject* mod) { @@ -387,4 +395,7 @@ initModulePart_Opcode(PyObject* mod) { return (true); } -} // end of unnamed namespace + +} // end python namespace +} // end dns namespace +} // end isc namespace diff --git a/src/lib/dns/python/opcode_python.h b/src/lib/dns/python/opcode_python.h new file mode 100644 index 0000000000..e1d826ad18 --- /dev/null +++ b/src/lib/dns/python/opcode_python.h @@ -0,0 +1,53 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_OPCODE_H +#define __PYTHON_OPCODE_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// + +class s_Opcode : public PyObject { +public: + s_Opcode() : opcode(NULL), static_code(false) {} + const isc::dns::Opcode* opcode; + bool static_code; +}; + + +extern PyTypeObject opcode_type; + +bool initModulePart_Opcode(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_OPCODE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 07abf7112e..91ec3d11f9 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -40,40 +40,24 @@ #include "pydnspp_common.h" #include "messagerenderer_python.h" #include "name_python.h" +#include "rrclass_python.h" +#include "rrtype_python.h" +#include "rrttl_python.h" +#include "rdata_python.h" #include "rcode_python.h" +#include "opcode_python.h" +#include "rrset_python.h" #include "tsigkey_python.h" #include "tsig_rdata_python.h" #include "tsigerror_python.h" #include "tsigrecord_python.h" #include "tsig_python.h" +#include "question_python.h" +#include "message_python.h" +#include "edns_python.h" -namespace isc { -namespace dns { -namespace python { -// For our 'general' isc::Exceptions -PyObject* po_IscException; -PyObject* po_InvalidParameter; - -// For our own isc::dns::Exception -PyObject* po_DNSMessageBADVERS; -} -} -} - -// order is important here! using namespace isc::dns::python; -#include // needs Messagerenderer -#include // needs Messagerenderer -#include // needs Messagerenderer -#include // needs Type, Class -#include // needs Rdata, RRTTL -#include // needs RRClass, RRType, RRTTL, - // Name -#include -#include // needs Messagerenderer, Rcode -#include // needs RRset, Question - // // Definition of the module // diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc index 8ca763a969..4a2ad16487 100644 --- a/src/lib/dns/python/pydnspp_common.cc +++ b/src/lib/dns/python/pydnspp_common.cc @@ -15,9 +15,49 @@ #include #include + +#include + +#include + +#include + +#include +#include +#include + +#include "pydnspp_common.h" +#include "messagerenderer_python.h" +#include "name_python.h" +#include "rdata_python.h" +#include "rrclass_python.h" +#include "rrtype_python.h" +#include "rrttl_python.h" +#include "rrset_python.h" +#include "rcode_python.h" +#include "opcode_python.h" +#include "tsigkey_python.h" +#include "tsig_rdata_python.h" +#include "tsigerror_python.h" +#include "tsigrecord_python.h" +#include "tsig_python.h" +#include "question_python.h" +#include "message_python.h" + +// order is important here! +using namespace isc::dns::python; + namespace isc { namespace dns { namespace python { +// For our 'general' isc::Exceptions +PyObject* po_IscException; +PyObject* po_InvalidParameter; + +// For our own isc::dns::Exception +PyObject* po_DNSMessageBADVERS; + + int readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) { PyObject* el = NULL; diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index c702f85ec2..a4f2a9482d 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -12,18 +12,28 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include #include +#include +#include +#include + +#include "question_python.h" + +#include "name_python.h" +#include "rrclass_python.h" +#include "rrtype_python.h" +#include "messagerenderer_python.h" + using namespace isc::dns; +using namespace isc::dns::python; +using namespace isc::util; +using namespace isc; // // Question // - -// The s_* Class simply coverst one instantiation of the object -class s_Question : public PyObject { -public: - QuestionPtr question; -}; +namespace { // // We declare the functions here, the definitions are below @@ -69,60 +79,6 @@ static PyMethodDef Question_methods[] = { { NULL, NULL, 0, NULL } }; -// This defines the complete type for reflection in python and -// parsing of PyObject* to s_Question -// Most of the functions are not actually implemented and NULL here. -static PyTypeObject question_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.Question", - sizeof(s_Question), // tp_basicsize - 0, // tp_itemsize - (destructor)Question_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - Question_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The Question class encapsulates the common search key of DNS" - "lookup, consisting of owner name, RR type and RR class.", - NULL, // tp_traverse - NULL, // tp_clear - NULL, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - Question_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)Question_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - static int Question_init(s_Question* self, PyObject* args) { // Try out the various combinations of arguments to call the @@ -144,8 +100,9 @@ Question_init(s_Question* self, PyObject* args) { &rrclass_type, &rrclass, &rrtype_type, &rrtype )) { - self->question = QuestionPtr(new Question(*name->cppobj, *rrclass->rrclass, - *rrtype->rrtype)); + self->question = QuestionPtr(new Question(*name->cppobj, + *rrclass->cppobj, + *rrtype->cppobj)); return (0); } else if (PyArg_ParseTuple(args, "y#|I", &b, &len, &position)) { PyErr_Clear(); @@ -201,7 +158,7 @@ Question_getType(s_Question* self) { rrtype = static_cast(rrtype_type.tp_alloc(&rrtype_type, 0)); if (rrtype != NULL) { - rrtype->rrtype = new RRType(self->question->getType()); + rrtype->cppobj = new RRType(self->question->getType()); } return (rrtype); @@ -213,7 +170,7 @@ Question_getClass(s_Question* self) { rrclass = static_cast(rrclass_type.tp_alloc(&rrclass_type, 0)); if (rrclass != NULL) { - rrclass->rrclass = new RRClass(self->question->getClass()); + rrclass->cppobj = new RRClass(self->question->getClass()); } return (rrclass); @@ -238,7 +195,7 @@ static PyObject* Question_toWire(s_Question* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; - + if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; @@ -266,6 +223,67 @@ Question_toWire(s_Question* self, PyObject* args) { // end of Question +} // end of unnamed namespace + +namespace isc { +namespace dns { +namespace python { + +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_Question +// Most of the functions are not actually implemented and NULL here. +PyTypeObject question_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.Question", + sizeof(s_Question), // tp_basicsize + 0, // tp_itemsize + (destructor)Question_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + Question_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The Question class encapsulates the common search key of DNS" + "lookup, consisting of owner name, RR type and RR class.", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + Question_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)Question_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + + // Module Initialization, all statics are initialized here bool @@ -281,6 +299,10 @@ initModulePart_Question(PyObject* mod) { Py_INCREF(&question_type); PyModule_AddObject(mod, "Question", reinterpret_cast(&question_type)); - + return (true); } + +} // end python namespace +} // end dns namespace +} // end isc namespace diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h new file mode 100644 index 0000000000..9ec563bc7c --- /dev/null +++ b/src/lib/dns/python/question_python.h @@ -0,0 +1,51 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_QUESTION_H +#define __PYTHON_QUESTION_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +extern PyObject* po_EmptyQuestion; + +class s_Question : public PyObject { +public: + isc::dns::QuestionPtr question; +}; + +extern PyTypeObject question_type; + +bool initModulePart_Question(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_QUESTION_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index faa4f4c41f..19dbd7e1ed 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -12,20 +12,22 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#define PY_SSIZE_T_CLEAN +#include #include +#include +#include + +#include "rdata_python.h" +#include "rrtype_python.h" +#include "rrclass_python.h" +#include "messagerenderer_python.h" + using namespace isc::dns; +using namespace isc::dns::python; using namespace isc::util; using namespace isc::dns::rdata; -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the initModulePart -// function at the end of this file -// -static PyObject* po_InvalidRdataLength; -static PyObject* po_InvalidRdataText; -static PyObject* po_CharStringTooLong; - // // Definition of the classes // @@ -38,17 +40,7 @@ static PyObject* po_CharStringTooLong; // Rdata // -// The s_* Class simply coverst one instantiation of the object - -// Using a shared_ptr here should not really be necessary (PyObject -// is already reference-counted), however internally on the cpp side, -// not doing so might result in problems, since we can't copy construct -// rdata field, adding them to rrsets results in a problem when the -// rrset is destroyed later -class s_Rdata : public PyObject { -public: - RdataPtr rdata; -}; +namespace { // // We declare the functions here, the definitions are below @@ -86,60 +78,6 @@ static PyMethodDef Rdata_methods[] = { { NULL, NULL, 0, NULL } }; -// This defines the complete type for reflection in python and -// parsing of PyObject* to s_Rdata -// Most of the functions are not actually implemented and NULL here. -static PyTypeObject rdata_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.Rdata", - sizeof(s_Rdata), // tp_basicsize - 0, // tp_itemsize - (destructor)Rdata_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - Rdata_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The Rdata class is an abstract base class that provides " - "a set of common interfaces to manipulate concrete RDATA objects.", - NULL, // tp_traverse - NULL, // tp_clear - (richcmpfunc)RData_richcmp, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - Rdata_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)Rdata_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - static int Rdata_init(s_Rdata* self, PyObject* args) { s_RRType* rrtype; @@ -152,12 +90,12 @@ Rdata_init(s_Rdata* self, PyObject* args) { if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype, &rrclass_type, &rrclass, &s)) { - self->rdata = createRdata(*rrtype->rrtype, *rrclass->rrclass, s); + self->rdata = createRdata(*rrtype->cppobj, *rrclass->cppobj, s); return (0); } else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype, &rrclass_type, &rrclass, &data, &len)) { InputBuffer input_buffer(data, len); - self->rdata = createRdata(*rrtype->rrtype, *rrclass->rrclass, + self->rdata = createRdata(*rrtype->cppobj, *rrclass->cppobj, input_buffer, len); return (0); } @@ -191,10 +129,10 @@ static PyObject* Rdata_toWire(s_Rdata* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; - + if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; - + OutputBuffer buffer(4); self->rdata->toWire(buffer); PyObject* rd_bytes = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); @@ -217,7 +155,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) { -static PyObject* +static PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op) { bool c; @@ -260,6 +198,75 @@ RData_richcmp(s_Rdata* self, s_Rdata* other, int op) { } // end of Rdata +} // end of unnamed namespace + +namespace isc { +namespace dns { +namespace python { + + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the initModulePart +// function at the end of this file +// +static PyObject* po_InvalidRdataLength; +static PyObject* po_InvalidRdataText; +static PyObject* po_CharStringTooLong; + +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_Rdata +// Most of the functions are not actually implemented and NULL here. +PyTypeObject rdata_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.Rdata", + sizeof(s_Rdata), // tp_basicsize + 0, // tp_itemsize + (destructor)Rdata_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + Rdata_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The Rdata class is an abstract base class that provides " + "a set of common interfaces to manipulate concrete RDATA objects.", + NULL, // tp_traverse + NULL, // tp_clear + (richcmpfunc)RData_richcmp, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + Rdata_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)Rdata_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; // Module Initialization, all statics are initialized here bool @@ -284,6 +291,10 @@ initModulePart_Rdata(PyObject* mod) { po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong", NULL, NULL); PyModule_AddObject(mod, "CharStringTooLong", po_CharStringTooLong); - + return (true); } + +} // end python namespace +} // end dns namespace +} // end isc namespace diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h new file mode 100644 index 0000000000..79fbd7de46 --- /dev/null +++ b/src/lib/dns/python/rdata_python.h @@ -0,0 +1,52 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_RDATA_H +#define __PYTHON_RDATA_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { + +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +extern PyObject* po_EmptyRdata; + +class s_Rdata : public PyObject { +public: + isc::dns::rdata::RdataPtr rdata; +}; + +extern PyTypeObject rdata_type; + +bool initModulePart_Rdata(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_RDATA_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index 6d150c2b5e..46cd87bd9f 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -11,19 +11,22 @@ // 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. +#include #include +#include +#include +#include + +#include "rrclass_python.h" +#include "messagerenderer_python.h" +#include "pydnspp_common.h" + + using namespace isc::dns; +using namespace isc::dns::python; using namespace isc::util; - -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the initModulePart -// function at the end of this file -// -static PyObject* po_InvalidRRClass; -static PyObject* po_IncompleteRRClass; - +using namespace isc::util::python; // // Definition of the classes // @@ -36,11 +39,7 @@ static PyObject* po_IncompleteRRClass; // RRClass // -// The s_* Class simply covers one instantiation of the object -class s_RRClass : public PyObject { -public: - RRClass* rrclass; -}; +namespace { // // We declare the functions here, the definitions are below @@ -67,6 +66,7 @@ static PyObject* RRClass_HS(s_RRClass *self); static PyObject* RRClass_NONE(s_RRClass *self); static PyObject* RRClass_ANY(s_RRClass *self); +typedef CPPPyObjectContainer RRClassContainer; // This list contains the actual set of functions we have in // python. Each entry has @@ -94,10 +94,202 @@ static PyMethodDef RRClass_methods[] = { { NULL, NULL, 0, NULL } }; +static int +RRClass_init(s_RRClass* self, PyObject* args) { + const char* s; + long i; + PyObject* bytes = NULL; + // The constructor argument can be a string ("IN"), an integer (1), + // or a sequence of numbers between 0 and 65535 (wire code) + + // Note that PyArg_ParseType can set PyError, and we need to clear + // that if we try several like here. Otherwise the *next* python + // call will suddenly appear to throw an exception. + // (the way to do exceptions is to set PyErr and return -1) + try { + if (PyArg_ParseTuple(args, "s", &s)) { + self->cppobj = new RRClass(s); + return (0); + } else if (PyArg_ParseTuple(args, "l", &i)) { + if (i < 0 || i > 0xffff) { + PyErr_Clear(); + PyErr_SetString(PyExc_ValueError, + "RR class number out of range"); + return (-1); + } + self->cppobj = new RRClass(i); + return (0); + } else if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { + uint8_t data[2]; + int result = readDataFromSequence(data, 2, bytes); + if (result != 0) { + return (result); + } + InputBuffer ib(data, 2); + self->cppobj = new RRClass(ib); + PyErr_Clear(); + return (0); + } + // Incomplete is never thrown, a type error would have already been raised + //when we try to read the 2 bytes above + } catch (const InvalidRRClass& ic) { + PyErr_Clear(); + PyErr_SetString(po_InvalidRRClass, ic.what()); + return (-1); + } + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, + "no valid type in constructor argument"); + return (-1); +} + +static void +RRClass_destroy(s_RRClass* self) { + delete self->cppobj; + self->cppobj = NULL; + Py_TYPE(self)->tp_free(self); +} + +static PyObject* +RRClass_toText(s_RRClass* self) { + // Py_BuildValue makes python objects from native data + return (Py_BuildValue("s", self->cppobj->toText().c_str())); +} + +static PyObject* +RRClass_str(PyObject* self) { + // Simply call the to_text method we already defined + return (PyObject_CallMethod(self, + const_cast("to_text"), + const_cast(""))); +} + +static PyObject* +RRClass_toWire(s_RRClass* self, PyObject* args) { + PyObject* bytes; + s_MessageRenderer* mr; + + if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { + PyObject* bytes_o = bytes; + + OutputBuffer buffer(2); + self->cppobj->toWire(buffer); + PyObject* n = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); + PyObject* result = PySequence_InPlaceConcat(bytes_o, n); + // We need to release the object we temporarily created here + // to prevent memory leak + Py_DECREF(n); + return (result); + } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { + self->cppobj->toWire(*mr->messagerenderer); + // If we return NULL it is seen as an error, so use this for + // None returns + Py_RETURN_NONE; + } + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, + "toWire argument must be a sequence object or a MessageRenderer"); + return (NULL); +} + +static PyObject* +RRClass_getCode(s_RRClass* self) { + return (Py_BuildValue("I", self->cppobj->getCode())); +} + +static PyObject* +RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op) { + bool c; + + // Check for null and if the types match. If different type, + // simply return False + if (!other || (self->ob_type != other->ob_type)) { + Py_RETURN_FALSE; + } + + switch (op) { + case Py_LT: + c = *self->cppobj < *other->cppobj; + break; + case Py_LE: + c = *self->cppobj < *other->cppobj || + *self->cppobj == *other->cppobj; + break; + case Py_EQ: + c = *self->cppobj == *other->cppobj; + break; + case Py_NE: + c = *self->cppobj != *other->cppobj; + break; + case Py_GT: + c = *other->cppobj < *self->cppobj; + break; + case Py_GE: + c = *other->cppobj < *self->cppobj || + *self->cppobj == *other->cppobj; + break; + default: + PyErr_SetString(PyExc_IndexError, + "Unhandled rich comparison operator"); + return (NULL); + } + if (c) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +// +// Common function for RRClass_IN/CH/etc. +// +static PyObject* RRClass_createStatic(RRClass stc) { + s_RRClass* ret = PyObject_New(s_RRClass, &rrclass_type); + if (ret != NULL) { + ret->cppobj = new RRClass(stc); + } + return (ret); +} + +static PyObject* RRClass_IN(s_RRClass*) { + return (RRClass_createStatic(RRClass::IN())); +} + +static PyObject* RRClass_CH(s_RRClass*) { + return (RRClass_createStatic(RRClass::CH())); +} + +static PyObject* RRClass_HS(s_RRClass*) { + return (RRClass_createStatic(RRClass::HS())); +} + +static PyObject* RRClass_NONE(s_RRClass*) { + return (RRClass_createStatic(RRClass::NONE())); +} + +static PyObject* RRClass_ANY(s_RRClass*) { + return (RRClass_createStatic(RRClass::ANY())); +} +// end of RRClass + +} // end anonymous namespace + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the initModulePart +// function at the end of this file +// +PyObject* po_InvalidRRClass; +PyObject* po_IncompleteRRClass; + + // This defines the complete type for reflection in python and // parsing of PyObject* to s_RRClass // Most of the functions are not actually implemented and NULL here. -static PyTypeObject rrclass_type = { +PyTypeObject rrclass_type = { PyVarObject_HEAD_INIT(NULL, 0) "pydnspp.RRClass", sizeof(s_RRClass), // tp_basicsize @@ -111,7 +303,7 @@ static PyTypeObject rrclass_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call RRClass_str, // tp_str NULL, // tp_getattro @@ -150,183 +342,6 @@ static PyTypeObject rrclass_type = { 0 // tp_version_tag }; -static int -RRClass_init(s_RRClass* self, PyObject* args) { - const char* s; - long i; - PyObject* bytes = NULL; - // The constructor argument can be a string ("IN"), an integer (1), - // or a sequence of numbers between 0 and 65535 (wire code) - - // Note that PyArg_ParseType can set PyError, and we need to clear - // that if we try several like here. Otherwise the *next* python - // call will suddenly appear to throw an exception. - // (the way to do exceptions is to set PyErr and return -1) - try { - if (PyArg_ParseTuple(args, "s", &s)) { - self->rrclass = new RRClass(s); - return (0); - } else if (PyArg_ParseTuple(args, "l", &i)) { - if (i < 0 || i > 0xffff) { - PyErr_Clear(); - PyErr_SetString(PyExc_ValueError, - "RR class number out of range"); - return (-1); - } - self->rrclass = new RRClass(i); - return (0); - } else if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { - uint8_t data[2]; - int result = readDataFromSequence(data, 2, bytes); - if (result != 0) { - return (result); - } - InputBuffer ib(data, 2); - self->rrclass = new RRClass(ib); - PyErr_Clear(); - return (0); - } - // Incomplete is never thrown, a type error would have already been raised - //when we try to read the 2 bytes above - } catch (const InvalidRRClass& ic) { - PyErr_Clear(); - PyErr_SetString(po_InvalidRRClass, ic.what()); - return (-1); - } - PyErr_Clear(); - PyErr_SetString(PyExc_TypeError, - "no valid type in constructor argument"); - return (-1); -} - -static void -RRClass_destroy(s_RRClass* self) { - delete self->rrclass; - self->rrclass = NULL; - Py_TYPE(self)->tp_free(self); -} - -static PyObject* -RRClass_toText(s_RRClass* self) { - // Py_BuildValue makes python objects from native data - return (Py_BuildValue("s", self->rrclass->toText().c_str())); -} - -static PyObject* -RRClass_str(PyObject* self) { - // Simply call the to_text method we already defined - return (PyObject_CallMethod(self, - const_cast("to_text"), - const_cast(""))); -} - -static PyObject* -RRClass_toWire(s_RRClass* self, PyObject* args) { - PyObject* bytes; - s_MessageRenderer* mr; - - if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { - PyObject* bytes_o = bytes; - - OutputBuffer buffer(2); - self->rrclass->toWire(buffer); - PyObject* n = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); - PyObject* result = PySequence_InPlaceConcat(bytes_o, n); - // We need to release the object we temporarily created here - // to prevent memory leak - Py_DECREF(n); - return (result); - } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->rrclass->toWire(*mr->messagerenderer); - // If we return NULL it is seen as an error, so use this for - // None returns - Py_RETURN_NONE; - } - PyErr_Clear(); - PyErr_SetString(PyExc_TypeError, - "toWire argument must be a sequence object or a MessageRenderer"); - return (NULL); -} - -static PyObject* -RRClass_getCode(s_RRClass* self) { - return (Py_BuildValue("I", self->rrclass->getCode())); -} - -static PyObject* -RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op) { - bool c; - - // Check for null and if the types match. If different type, - // simply return False - if (!other || (self->ob_type != other->ob_type)) { - Py_RETURN_FALSE; - } - - switch (op) { - case Py_LT: - c = *self->rrclass < *other->rrclass; - break; - case Py_LE: - c = *self->rrclass < *other->rrclass || - *self->rrclass == *other->rrclass; - break; - case Py_EQ: - c = *self->rrclass == *other->rrclass; - break; - case Py_NE: - c = *self->rrclass != *other->rrclass; - break; - case Py_GT: - c = *other->rrclass < *self->rrclass; - break; - case Py_GE: - c = *other->rrclass < *self->rrclass || - *self->rrclass == *other->rrclass; - break; - default: - PyErr_SetString(PyExc_IndexError, - "Unhandled rich comparison operator"); - return (NULL); - } - if (c) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -// -// Common function for RRClass_IN/CH/etc. -// -static PyObject* RRClass_createStatic(RRClass stc) { - s_RRClass* ret = PyObject_New(s_RRClass, &rrclass_type); - if (ret != NULL) { - ret->rrclass = new RRClass(stc); - } - return (ret); -} - -static PyObject* RRClass_IN(s_RRClass*) { - return (RRClass_createStatic(RRClass::IN())); -} - -static PyObject* RRClass_CH(s_RRClass*) { - return (RRClass_createStatic(RRClass::CH())); -} - -static PyObject* RRClass_HS(s_RRClass*) { - return (RRClass_createStatic(RRClass::HS())); -} - -static PyObject* RRClass_NONE(s_RRClass*) { - return (RRClass_createStatic(RRClass::NONE())); -} - -static PyObject* RRClass_ANY(s_RRClass*) { - return (RRClass_createStatic(RRClass::ANY())); -} -// end of RRClass - // Module Initialization, all statics are initialized here bool @@ -348,6 +363,29 @@ initModulePart_RRClass(PyObject* mod) { Py_INCREF(&rrclass_type); PyModule_AddObject(mod, "RRClass", reinterpret_cast(&rrclass_type)); - + return (true); } + +PyObject* +createRRClassObject(const RRClass& source) { + RRClassContainer container = PyObject_New(s_RRClass, &rrclass_type); + container.set(new RRClass(source)); + return (container.release()); +} + + +bool +PyRRClass_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &rrclass_type)); +} + +RRClass& +PyRRClass_ToRRClassPtr(PyObject* rrclass_obj) { + s_RRClass* rrclass = static_cast(rrclass_obj); + return (*rrclass->cppobj); +} + +} // end namespace python +} // end namespace dns +} // end namespace isc diff --git a/src/lib/dns/python/rrclass_python.h b/src/lib/dns/python/rrclass_python.h new file mode 100644 index 0000000000..e4e372c35e --- /dev/null +++ b/src/lib/dns/python/rrclass_python.h @@ -0,0 +1,83 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_RRCLASS_H +#define __PYTHON_RRCLASS_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { +class RRClass; + +namespace python { + +extern PyObject* po_InvalidRRClass; +extern PyObject* po_IncompleteRRClass; + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +// The s_* Class simply covers one instantiation of the object +class s_RRClass : public PyObject { +public: + RRClass* cppobj; +}; + +extern PyTypeObject rrclass_type; + +bool initModulePart_RRClass(PyObject* mod); + +/// This is A simple shortcut to create a python RRClass object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called with in a try block +/// followed by necessary setup for python exception. +PyObject* createRRClassObject(const RRClass& source); + +/// \brief Checks if the given python object is a RRClass object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type RRClass, false otherwise +bool PyRRClass_Check(PyObject* obj); + +/// \brief Returns a reference to the RRClass object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type RRClass; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyRRClass_Check() +/// +/// \note This is not a copy; if the RRClass is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param rrclass_obj The rrclass object to convert +RRClass& PyRRClass_ToRRClass(PyObject* rrclass_obj); + + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_RRCLASS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index 71a0710f8b..c9513c24d3 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -12,14 +12,24 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include +#include -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// -static PyObject* po_EmptyRRset; +#include + +#include +#include +#include + +#include "name_python.h" +#include "pydnspp_common.h" +#include "rrset_python.h" +#include "rrclass_python.h" +#include "rrtype_python.h" +#include "rrttl_python.h" +#include "rdata_python.h" +#include "messagerenderer_python.h" + +namespace { // // Definition of the classes @@ -29,19 +39,14 @@ static PyObject* po_EmptyRRset; // and static wrappers around the methods we export), a list of methods, // and a type description using namespace isc::dns; +using namespace isc::dns::python; using namespace isc::util; +using namespace isc::util::python; // RRset -// Using a shared_ptr here should not really be necessary (PyObject -// is already reference-counted), however internally on the cpp side, -// not doing so might result in problems, since we can't copy construct -// rrsets, adding them to messages results in a problem when the -// message is destroyed or cleared later -class s_RRset : public PyObject { -public: - RRsetPtr rrset; -}; +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer RRsetContainer; static int RRset_init(s_RRset* self, PyObject* args); static void RRset_destroy(s_RRset* self); @@ -58,6 +63,8 @@ static PyObject* RRset_str(PyObject* self); static PyObject* RRset_toWire(s_RRset* self, PyObject* args); static PyObject* RRset_addRdata(s_RRset* self, PyObject* args); static PyObject* RRset_getRdata(s_RRset* self); +static PyObject* RRset_removeRRsig(s_RRset* self); + // TODO: iterator? static PyMethodDef RRset_methods[] = { @@ -88,74 +95,11 @@ static PyMethodDef RRset_methods[] = { "Adds the rdata for one RR to the RRset.\nTakes an Rdata object as an argument" }, { "get_rdata", reinterpret_cast(RRset_getRdata), METH_NOARGS, "Returns a List containing all Rdata elements" }, + { "remove_rrsig", reinterpret_cast(RRset_removeRRsig), METH_NOARGS, + "Clears the list of RRsigs for this RRset" }, { NULL, NULL, 0, NULL } }; -static PyTypeObject rrset_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.RRset", - sizeof(s_RRset), // tp_basicsize - 0, // tp_itemsize - (destructor)RRset_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - RRset_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The AbstractRRset class is an abstract base class that " - "models a DNS RRset.\n\n" - "An object of (a specific derived class of) AbstractRRset " - "models an RRset as described in the DNS standard:\n" - "A set of DNS resource records (RRs) of the same type and class. " - "The standard requires the TTL of all RRs in an RRset be the same; " - "this class follows that requirement.\n\n" - "Note about duplicate RDATA: RFC2181 states that it's meaningless that an " - "RRset contains two identical RRs and that name servers should suppress " - "such duplicates.\n" - "This class is not responsible for ensuring this requirement: For example, " - "addRdata() method doesn't check if there's already RDATA identical " - "to the one being added.\n" - "This is because such checks can be expensive, and it's often easy to " - "ensure the uniqueness requirement at the %data preparation phase " - "(e.g. when loading a zone).", - NULL, // tp_traverse - NULL, // tp_clear - NULL, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - RRset_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)RRset_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - static int RRset_init(s_RRset* self, PyObject* args) { s_Name* name; @@ -168,8 +112,8 @@ RRset_init(s_RRset* self, PyObject* args) { &rrtype_type, &rrtype, &rrttl_type, &rrttl )) { - self->rrset = RRsetPtr(new RRset(*name->cppobj, *rrclass->rrclass, - *rrtype->rrtype, *rrttl->rrttl)); + self->rrset = RRsetPtr(new RRset(*name->cppobj, *rrclass->cppobj, + *rrtype->cppobj, *rrttl->rrttl)); return (0); } @@ -214,8 +158,8 @@ RRset_getClass(s_RRset* self) { rrclass = static_cast(rrclass_type.tp_alloc(&rrclass_type, 0)); if (rrclass != NULL) { - rrclass->rrclass = new RRClass(self->rrset->getClass()); - if (rrclass->rrclass == NULL) + rrclass->cppobj = new RRClass(self->rrset->getClass()); + if (rrclass->cppobj == NULL) { Py_DECREF(rrclass); return (NULL); @@ -231,8 +175,8 @@ RRset_getType(s_RRset* self) { rrtype = static_cast(rrtype_type.tp_alloc(&rrtype_type, 0)); if (rrtype != NULL) { - rrtype->rrtype = new RRType(self->rrset->getType()); - if (rrtype->rrtype == NULL) + rrtype->cppobj = new RRType(self->rrset->getType()); + if (rrtype->cppobj == NULL) { Py_DECREF(rrtype); return (NULL); @@ -305,7 +249,7 @@ RRset_toWire(s_RRset* self, PyObject* args) { try { if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; - + OutputBuffer buffer(4096); self->rrset->toWire(buffer); PyObject* n = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); @@ -360,20 +304,107 @@ RRset_getRdata(s_RRset* self) { // hmz them iterators/shared_ptrs and private constructors // make this a bit weird, so we create a new one with // the data available - const Rdata *rd = &it->getCurrent(); + const rdata::Rdata *rd = &it->getCurrent(); rds->rdata = createRdata(self->rrset->getType(), self->rrset->getClass(), *rd); PyList_Append(list, rds); } else { return (NULL); } } - + return (list); } +static PyObject* +RRset_removeRRsig(s_RRset* self) { + self->rrset->removeRRsig(); + Py_RETURN_NONE; +} + + // end of RRset +} // end of unnamed namespace + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +PyObject* po_EmptyRRset; + +PyTypeObject rrset_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.RRset", + sizeof(s_RRset), // tp_basicsize + 0, // tp_itemsize + (destructor)RRset_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + RRset_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The AbstractRRset class is an abstract base class that " + "models a DNS RRset.\n\n" + "An object of (a specific derived class of) AbstractRRset " + "models an RRset as described in the DNS standard:\n" + "A set of DNS resource records (RRs) of the same type and class. " + "The standard requires the TTL of all RRs in an RRset be the same; " + "this class follows that requirement.\n\n" + "Note about duplicate RDATA: RFC2181 states that it's meaningless that an " + "RRset contains two identical RRs and that name servers should suppress " + "such duplicates.\n" + "This class is not responsible for ensuring this requirement: For example, " + "addRdata() method doesn't check if there's already RDATA identical " + "to the one being added.\n" + "This is because such checks can be expensive, and it's often easy to " + "ensure the uniqueness requirement at the %data preparation phase " + "(e.g. when loading a zone).", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + RRset_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)RRset_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + + + // Module Initialization, all statics are initialized here bool initModulePart_RRset(PyObject* mod) { @@ -396,7 +427,55 @@ initModulePart_RRset(PyObject* mod) { Py_INCREF(&rrset_type); PyModule_AddObject(mod, "RRset", reinterpret_cast(&rrset_type)); - + return (true); } +PyObject* +createRRsetObject(const RRset& source) { + isc::dns::python::s_RRset* py_rrset = static_cast( + isc::dns::python::rrset_type.tp_alloc(&isc::dns::python::rrset_type, 0)); + if (py_rrset == NULL) { + isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " + "probably due to short memory"); + } + + // RRsets are noncopyable, so as a workaround we recreate a new one + // and copy over all content + try { + py_rrset->rrset = isc::dns::RRsetPtr( + new isc::dns::RRset(source.getName(), source.getClass(), + source.getType(), source.getTTL())); + + isc::dns::RdataIteratorPtr rdata_it(source.getRdataIterator()); + for (rdata_it->first(); !rdata_it->isLast(); rdata_it->next()) { + py_rrset->rrset->addRdata(rdata_it->getCurrent()); + } + + isc::dns::RRsetPtr sigs = source.getRRsig(); + if (sigs) { + py_rrset->rrset->addRRsig(sigs); + } + return py_rrset; + } catch (const std::bad_alloc&) { + isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " + "probably due to short memory"); + } +} + +bool +PyRRset_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &rrset_type)); +} + +RRset& +PyRRset_ToRRset(PyObject* rrset_obj) { + s_RRset* rrset = static_cast(rrset_obj); + return (*rrset->rrset); +} + + + +} // end python namespace +} // end dns namespace +} // end isc namespace diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h new file mode 100644 index 0000000000..e125207d25 --- /dev/null +++ b/src/lib/dns/python/rrset_python.h @@ -0,0 +1,86 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_RRSET_H +#define __PYTHON_RRSET_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +extern PyObject* po_EmptyRRset; + +// The s_* Class simply coverst one instantiation of the object + +// Using a shared_ptr here should not really be necessary (PyObject +// is already reference-counted), however internally on the cpp side, +// not doing so might result in problems, since we can't copy construct +// rdata field, adding them to rrsets results in a problem when the +// rrset is destroyed later +class s_RRset : public PyObject { +public: + isc::dns::RRsetPtr rrset; +}; + +extern PyTypeObject rrset_type; + +bool initModulePart_RRset(PyObject* mod); + +/// This is A simple shortcut to create a python RRset object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called with in a try block +/// followed by necessary setup for python exception. +PyObject* createRRsetObject(const RRset& source); + +/// \brief Checks if the given python object is a RRset object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type RRset, false otherwise +bool PyRRset_Check(PyObject* obj); + +/// \brief Returns a reference to the RRset object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type RRset; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyRRset_Check() +/// +/// \note This is not a copy; if the RRset is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param rrset_obj The rrset object to convert +RRset& PyRRset_ToRRset(PyObject* rrset_obj); + + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_RRSET_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index c4b25bfa30..901ea4ebdb 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -12,22 +12,23 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include #include #include +#include +#include + +#include "rrttl_python.h" +#include "pydnspp_common.h" +#include "messagerenderer_python.h" using namespace std; using namespace isc::dns; +using namespace isc::dns::python; using namespace isc::util; -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the initModulePart -// function at the end of this file -// -static PyObject* po_InvalidRRTTL; -static PyObject* po_IncompleteRRTTL; - +namespace { // // Definition of the classes // @@ -40,12 +41,6 @@ static PyObject* po_IncompleteRRTTL; // RRTTL // -// The s_* Class simply covers one instantiation of the object -class s_RRTTL : public PyObject { -public: - RRTTL* rrttl; -}; - // // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other @@ -85,64 +80,6 @@ static PyMethodDef RRTTL_methods[] = { { NULL, NULL, 0, NULL } }; -// This defines the complete type for reflection in python and -// parsing of PyObject* to s_RRTTL -// Most of the functions are not actually implemented and NULL here. -static PyTypeObject rrttl_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.RRTTL", - sizeof(s_RRTTL), // tp_basicsize - 0, // tp_itemsize - (destructor)RRTTL_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - RRTTL_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The RRTTL class encapsulates TTLs used in DNS resource records.\n\n" - "This is a straightforward class; an RRTTL object simply maintains a " - "32-bit unsigned integer corresponding to the TTL value. The main purpose " - "of this class is to provide convenient interfaces to convert a textual " - "representation into the integer TTL value and vice versa, and to handle " - "wire-format representations.", - NULL, // tp_traverse - NULL, // tp_clear - (richcmpfunc)RRTTL_richcmp, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - RRTTL_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)RRTTL_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - static int RRTTL_init(s_RRTTL* self, PyObject* args) { const char* s; @@ -225,10 +162,10 @@ static PyObject* RRTTL_toWire(s_RRTTL* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; - + if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; - + OutputBuffer buffer(4); self->rrttl->toWire(buffer); PyObject* n = PyBytes_FromStringAndSize(static_cast(buffer.getData()), @@ -255,7 +192,7 @@ RRTTL_getValue(s_RRTTL* self) { return (Py_BuildValue("I", self->rrttl->getValue())); } -static PyObject* +static PyObject* RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op) { bool c = false; @@ -293,8 +230,79 @@ RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op) { Py_RETURN_FALSE; } // end of RRTTL +} // end anonymous namespace +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the initModulePart +// function at the end of this file +// +PyObject* po_InvalidRRTTL; +PyObject* po_IncompleteRRTTL; + +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_RRTTL +// Most of the functions are not actually implemented and NULL here. +PyTypeObject rrttl_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.RRTTL", + sizeof(s_RRTTL), // tp_basicsize + 0, // tp_itemsize + (destructor)RRTTL_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + RRTTL_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The RRTTL class encapsulates TTLs used in DNS resource records.\n\n" + "This is a straightforward class; an RRTTL object simply maintains a " + "32-bit unsigned integer corresponding to the TTL value. The main purpose " + "of this class is to provide convenient interfaces to convert a textual " + "representation into the integer TTL value and vice versa, and to handle " + "wire-format representations.", + NULL, // tp_traverse + NULL, // tp_clear + (richcmpfunc)RRTTL_richcmp, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + RRTTL_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)RRTTL_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + // Module Initialization, all statics are initialized here bool initModulePart_RRTTL(PyObject* mod) { @@ -313,6 +321,10 @@ initModulePart_RRTTL(PyObject* mod) { Py_INCREF(&rrttl_type); PyModule_AddObject(mod, "RRTTL", reinterpret_cast(&rrttl_type)); - + return (true); } + +} // namespace python +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h new file mode 100644 index 0000000000..d59e18b7e5 --- /dev/null +++ b/src/lib/dns/python/rrttl_python.h @@ -0,0 +1,54 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_RRTTL_H +#define __PYTHON_RRTTL_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +extern PyObject* po_InvalidRRTTL; +extern PyObject* po_IncompleteRRTTL; + +// The s_* Class simply covers one instantiation of the object +class s_RRTTL : public PyObject { +public: + isc::dns::RRTTL* rrttl; +}; + + +extern PyTypeObject rrttl_type; + +bool initModulePart_RRTTL(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_RRTTL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index 00e0acd632..e59fef49bb 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -12,21 +12,23 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include #include #include +#include +#include + +#include "rrtype_python.h" +#include "messagerenderer_python.h" +#include "pydnspp_common.h" using namespace std; using namespace isc::dns; +using namespace isc::dns::python; using namespace isc::util; +using namespace isc::util::python; -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the initModulePart -// function at the end of this file -// -static PyObject* po_InvalidRRType; -static PyObject* po_IncompleteRRType; // // Definition of the classes @@ -35,17 +37,12 @@ static PyObject* po_IncompleteRRType; // For each class, we need a struct, a helper functions (init, destroy, // and static wrappers around the methods we export), a list of methods, // and a type description +namespace { // // RRType // -// The s_* Class simply covers one instantiation of the object -class s_RRType : public PyObject { -public: - const RRType* rrtype; -}; - // // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other @@ -84,6 +81,8 @@ static PyObject* RRType_IXFR(s_RRType *self); static PyObject* RRType_AXFR(s_RRType *self); static PyObject* RRType_ANY(s_RRType *self); +typedef CPPPyObjectContainer RRTypeContainer; + // This list contains the actual set of functions we have in // python. Each entry has // 1. Python method name @@ -124,62 +123,6 @@ static PyMethodDef RRType_methods[] = { { NULL, NULL, 0, NULL } }; -// This defines the complete type for reflection in python and -// parsing of PyObject* to s_RRType -// Most of the functions are not actually implemented and NULL here. -static PyTypeObject rrtype_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.RRType", - sizeof(s_RRType), // tp_basicsize - 0, // tp_itemsize - (destructor)RRType_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - RRType_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The RRType class encapsulates DNS resource record types.\n\n" - "This class manages the 16-bit integer type codes in quite a straightforward " - "way. The only non trivial task is to handle textual representations of " - "RR types, such as \"A\", \"AAAA\", or \"TYPE65534\".", - NULL, // tp_traverse - NULL, // tp_clear - (richcmpfunc)RRType_richcmp, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - RRType_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)RRType_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - static int RRType_init(s_RRType* self, PyObject* args) { const char* s; @@ -194,7 +137,7 @@ RRType_init(s_RRType* self, PyObject* args) { // (the way to do exceptions is to set PyErr and return -1) try { if (PyArg_ParseTuple(args, "s", &s)) { - self->rrtype = new RRType(s); + self->cppobj = new RRType(s); return (0); } else if (PyArg_ParseTuple(args, "l", &i)) { PyErr_Clear(); @@ -202,7 +145,7 @@ RRType_init(s_RRType* self, PyObject* args) { PyErr_SetString(PyExc_ValueError, "RR Type number out of range"); return (-1); } - self->rrtype = new RRType(i); + self->cppobj = new RRType(i); return (0); } else if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { Py_ssize_t size = PySequence_Size(bytes); @@ -212,7 +155,7 @@ RRType_init(s_RRType* self, PyObject* args) { return (result); } InputBuffer ib(&data[0], size); - self->rrtype = new RRType(ib); + self->cppobj = new RRType(ib); PyErr_Clear(); return (0); } @@ -238,15 +181,15 @@ RRType_init(s_RRType* self, PyObject* args) { static void RRType_destroy(s_RRType* self) { - delete self->rrtype; - self->rrtype = NULL; + delete self->cppobj; + self->cppobj = NULL; Py_TYPE(self)->tp_free(self); } static PyObject* RRType_toText(s_RRType* self) { // Py_BuildValue makes python objects from native data - return (Py_BuildValue("s", self->rrtype->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } static PyObject* @@ -265,7 +208,7 @@ RRType_toWire(s_RRType* self, PyObject* args) { PyObject* bytes_o = bytes; OutputBuffer buffer(2); - self->rrtype->toWire(buffer); + self->cppobj->toWire(buffer); PyObject* n = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); PyObject* result = PySequence_InPlaceConcat(bytes_o, n); // We need to release the object we temporarily created here @@ -273,7 +216,7 @@ RRType_toWire(s_RRType* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->rrtype->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->messagerenderer); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -286,10 +229,10 @@ RRType_toWire(s_RRType* self, PyObject* args) { static PyObject* RRType_getCode(s_RRType* self) { - return (Py_BuildValue("I", self->rrtype->getCode())); + return (Py_BuildValue("I", self->cppobj->getCode())); } -static PyObject* +static PyObject* RRType_richcmp(s_RRType* self, s_RRType* other, int op) { bool c; @@ -301,24 +244,24 @@ RRType_richcmp(s_RRType* self, s_RRType* other, int op) { switch (op) { case Py_LT: - c = *self->rrtype < *other->rrtype; + c = *self->cppobj < *other->cppobj; break; case Py_LE: - c = *self->rrtype < *other->rrtype || - *self->rrtype == *other->rrtype; + c = *self->cppobj < *other->cppobj || + *self->cppobj == *other->cppobj; break; case Py_EQ: - c = *self->rrtype == *other->rrtype; + c = *self->cppobj == *other->cppobj; break; case Py_NE: - c = *self->rrtype != *other->rrtype; + c = *self->cppobj != *other->cppobj; break; case Py_GT: - c = *other->rrtype < *self->rrtype; + c = *other->cppobj < *self->cppobj; break; case Py_GE: - c = *other->rrtype < *self->rrtype || - *self->rrtype == *other->rrtype; + c = *other->cppobj < *self->cppobj || + *self->cppobj == *other->cppobj; break; default: PyErr_SetString(PyExc_IndexError, @@ -337,7 +280,7 @@ RRType_richcmp(s_RRType* self, s_RRType* other, int op) { static PyObject* RRType_createStatic(RRType stc) { s_RRType* ret = PyObject_New(s_RRType, &rrtype_type); if (ret != NULL) { - ret->rrtype = new RRType(stc); + ret->cppobj = new RRType(stc); } return (ret); } @@ -437,9 +380,70 @@ RRType_ANY(s_RRType*) { return (RRType_createStatic(RRType::ANY())); } +} // end anonymous namespace -// end of RRType +namespace isc { +namespace dns { +namespace python { +PyObject* po_InvalidRRType; +PyObject* po_IncompleteRRType; + +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_RRType +// Most of the functions are not actually implemented and NULL here. +PyTypeObject rrtype_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.RRType", + sizeof(s_RRType), // tp_basicsize + 0, // tp_itemsize + (destructor)RRType_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + RRType_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The RRType class encapsulates DNS resource record types.\n\n" + "This class manages the 16-bit integer type codes in quite a straightforward " + "way. The only non trivial task is to handle textual representations of " + "RR types, such as \"A\", \"AAAA\", or \"TYPE65534\".", + NULL, // tp_traverse + NULL, // tp_clear + (richcmpfunc)RRType_richcmp, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + RRType_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)RRType_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; // Module Initialization, all statics are initialized here bool @@ -459,6 +463,30 @@ initModulePart_RRType(PyObject* mod) { Py_INCREF(&rrtype_type); PyModule_AddObject(mod, "RRType", reinterpret_cast(&rrtype_type)); - + return (true); } + +PyObject* +createRRTypeObject(const RRType& source) { + RRTypeContainer container = PyObject_New(s_RRType, &rrtype_type); + container.set(new RRType(source)); + return (container.release()); +} + + +bool +PyRRType_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &rrtype_type)); +} + +const RRType& +PyRRType_ToRRType(PyObject* rrtype_obj) { + s_RRType* rrtype = static_cast(rrtype_obj); + return (*rrtype->cppobj); +} + + +} // end namespace python +} // end namespace dns +} // end namespace isc diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h new file mode 100644 index 0000000000..009dd013d9 --- /dev/null +++ b/src/lib/dns/python/rrtype_python.h @@ -0,0 +1,84 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_RRTYPE_H +#define __PYTHON_RRTYPE_H 1 + +#include + +#include + +#include + +namespace isc { +namespace dns { +class RRType; + +namespace python { + +// +// Declaration of the custom exceptions +// Initialization and addition of these go in the module init at the +// end +// +extern PyObject* po_InvalidRRType; +extern PyObject* po_IncompleteRRType; + +// The s_* Class simply covers one instantiation of the object +class s_RRType : public PyObject { +public: + const RRType* cppobj; +}; + + +extern PyTypeObject rrtype_type; + +bool initModulePart_RRType(PyObject* mod); + +/// This is A simple shortcut to create a python RRType object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called with in a try block +/// followed by necessary setup for python exception. +PyObject* createRRTypeObject(const RRType& source); + +/// \brief Checks if the given python object is a RRType object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type RRType, false otherwise +bool PyRRType_Check(PyObject* obj); + +/// \brief Returns a reference to the RRType object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type RRType; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyRRType_Check() +/// +/// \note This is not a copy; if the RRType is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param rrtype_obj The rrtype object to convert +const RRType& PyRRType_ToRRType(PyObject* rrtype_obj); + + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_RRTYPE_H + +// Local Variables: +// mode: c++ +// End: From ece8bd155e646869b10fd08817ee7cd71c699c61 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 13 Sep 2011 12:15:38 +0200 Subject: [PATCH 705/974] [1245] clean up headers --- src/lib/dns/python/edns_python.h | 2 -- src/lib/dns/python/message_python.h | 2 -- src/lib/dns/python/messagerenderer_python.h | 10 ++++------ src/lib/dns/python/name_python.h | 5 +---- src/lib/dns/python/opcode_python.h | 2 -- src/lib/dns/python/pydnspp_common.h | 2 -- src/lib/dns/python/question_python.h | 2 -- src/lib/dns/python/rcode_python.h | 4 ++-- src/lib/dns/python/rdata_python.h | 3 --- src/lib/dns/python/rrclass_python.h | 4 ---- src/lib/dns/python/rrttl_python.h | 2 -- src/lib/dns/python/rrtype_python.h | 4 ---- src/lib/dns/python/tsig_python.h | 4 ++-- src/lib/dns/python/tsigerror_python.h | 4 ++-- src/lib/dns/python/tsigkey_python.h | 5 ++--- src/lib/dns/python/tsigrecord_python.h | 4 ++-- 16 files changed, 15 insertions(+), 44 deletions(-) diff --git a/src/lib/dns/python/edns_python.h b/src/lib/dns/python/edns_python.h index 23c229dc80..94b082004f 100644 --- a/src/lib/dns/python/edns_python.h +++ b/src/lib/dns/python/edns_python.h @@ -19,8 +19,6 @@ #include -#include - namespace isc { namespace dns { namespace python { diff --git a/src/lib/dns/python/message_python.h b/src/lib/dns/python/message_python.h index 91b1040c64..325eb8e14b 100644 --- a/src/lib/dns/python/message_python.h +++ b/src/lib/dns/python/message_python.h @@ -19,8 +19,6 @@ #include -#include - namespace isc { namespace dns { namespace python { diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h index 3bb096ed6c..cc28e437f5 100644 --- a/src/lib/dns/python/messagerenderer_python.h +++ b/src/lib/dns/python/messagerenderer_python.h @@ -17,13 +17,11 @@ #include -namespace isc { -namespace util { -class OutputBuffer; -} -namespace dns { -class MessageRenderer; +#include +#include +namespace isc { +namespace dns { namespace python { // The s_* Class simply covers one instantiation of the object. diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h index 27d79c23ba..4a4b611318 100644 --- a/src/lib/dns/python/name_python.h +++ b/src/lib/dns/python/name_python.h @@ -17,13 +17,10 @@ #include -#include +#include namespace isc { namespace dns { -class NameComparisonResult; -class Name; - namespace python { // diff --git a/src/lib/dns/python/opcode_python.h b/src/lib/dns/python/opcode_python.h index e1d826ad18..79860e4c2b 100644 --- a/src/lib/dns/python/opcode_python.h +++ b/src/lib/dns/python/opcode_python.h @@ -19,8 +19,6 @@ #include -#include - namespace isc { namespace dns { namespace python { diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h index ed90998ccc..8092b086d4 100644 --- a/src/lib/dns/python/pydnspp_common.h +++ b/src/lib/dns/python/pydnspp_common.h @@ -20,8 +20,6 @@ #include #include -#include - namespace isc { namespace dns { namespace python { diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h index 9ec563bc7c..a4ee5371a5 100644 --- a/src/lib/dns/python/question_python.h +++ b/src/lib/dns/python/question_python.h @@ -19,8 +19,6 @@ #include -#include - namespace isc { namespace dns { namespace python { diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h index 9b5e699e85..5d9b91dfaa 100644 --- a/src/lib/dns/python/rcode_python.h +++ b/src/lib/dns/python/rcode_python.h @@ -17,10 +17,10 @@ #include +#include + namespace isc { namespace dns { -class Rcode; - namespace python { // The s_* Class simply covers one instantiation of the object. diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h index 79fbd7de46..e9c86678c4 100644 --- a/src/lib/dns/python/rdata_python.h +++ b/src/lib/dns/python/rdata_python.h @@ -19,11 +19,8 @@ #include -#include - namespace isc { namespace dns { - namespace python { // diff --git a/src/lib/dns/python/rrclass_python.h b/src/lib/dns/python/rrclass_python.h index e4e372c35e..61b741fc01 100644 --- a/src/lib/dns/python/rrclass_python.h +++ b/src/lib/dns/python/rrclass_python.h @@ -17,14 +17,10 @@ #include -#include - #include namespace isc { namespace dns { -class RRClass; - namespace python { extern PyObject* po_InvalidRRClass; diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h index d59e18b7e5..2b77540f6d 100644 --- a/src/lib/dns/python/rrttl_python.h +++ b/src/lib/dns/python/rrttl_python.h @@ -19,8 +19,6 @@ #include -#include - namespace isc { namespace dns { namespace python { diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h index 009dd013d9..aad13d6c41 100644 --- a/src/lib/dns/python/rrtype_python.h +++ b/src/lib/dns/python/rrtype_python.h @@ -17,14 +17,10 @@ #include -#include - #include namespace isc { namespace dns { -class RRType; - namespace python { // diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h index f9b4f7b94a..1985c8226f 100644 --- a/src/lib/dns/python/tsig_python.h +++ b/src/lib/dns/python/tsig_python.h @@ -17,10 +17,10 @@ #include +#include + namespace isc { namespace dns { -class TSIGContext; - namespace python { // The s_* Class simply covers one instantiation of the object diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h index 735a48007f..9cb1b5dfe9 100644 --- a/src/lib/dns/python/tsigerror_python.h +++ b/src/lib/dns/python/tsigerror_python.h @@ -17,10 +17,10 @@ #include +#include + namespace isc { namespace dns { -class TSIGError; - namespace python { // The s_* Class simply covers one instantiation of the object diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h index 51b3ae7a0c..980edc5329 100644 --- a/src/lib/dns/python/tsigkey_python.h +++ b/src/lib/dns/python/tsigkey_python.h @@ -17,11 +17,10 @@ #include +#include + namespace isc { namespace dns { -class TSIGKey; -class TSIGKeyRing; - namespace python { // The s_* Class simply covers one instantiation of the object diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h index e0a3526a13..ef9669b33a 100644 --- a/src/lib/dns/python/tsigrecord_python.h +++ b/src/lib/dns/python/tsigrecord_python.h @@ -17,10 +17,10 @@ #include +#include + namespace isc { namespace dns { -class TSIGRecord; - namespace python { // The s_* Class simply covers one instantiation of the object From 4d39f72b87677c194d282a9e93de67dc0adfb4f3 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 13 Sep 2011 12:25:15 +0200 Subject: [PATCH 706/974] [1245] cleanup of some comments in .cc files A lot of the comments were leftovers from when this was all one big source file, and in individual files the comments were either wrong or superfluous (since the that was commented is self-explanatory with the context now) --- src/lib/dns/python/edns_python.cc | 18 ------------- src/lib/dns/python/message_python.cc | 18 ------------- src/lib/dns/python/messagerenderer_python.cc | 27 ++------------------ src/lib/dns/python/name_python.cc | 13 +--------- src/lib/dns/python/opcode_python.cc | 15 ----------- src/lib/dns/python/question_python.cc | 14 ---------- src/lib/dns/python/rcode_python.cc | 19 ++------------ src/lib/dns/python/rdata_python.cc | 13 ---------- src/lib/dns/python/rrclass_python.cc | 13 ---------- src/lib/dns/python/rrset_python.cc | 15 +---------- src/lib/dns/python/rrttl_python.cc | 21 +-------------- src/lib/dns/python/rrtype_python.cc | 18 ------------- 12 files changed, 7 insertions(+), 197 deletions(-) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index 9781a603bd..79154656c0 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -34,23 +34,7 @@ using namespace isc::util; using namespace isc::dns::rdata; using namespace isc::dns::python; -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - namespace { -// -// EDNS -// - -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// // General creation and destruction int EDNS_init(s_EDNS* self, PyObject* args); @@ -306,7 +290,6 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) { } } // end of anonymous namespace -// end of EDNS namespace isc { namespace dns { @@ -366,7 +349,6 @@ PyTypeObject edns_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here bool initModulePart_EDNS(PyObject* mod) { // We initialize the static description object with PyType_Ready(), diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 5b7cf48c84..23b95d583b 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -40,24 +40,6 @@ using namespace isc::util; namespace { -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// Message -// - -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// - -// General creation and destruction int Message_init(s_Message* self, PyObject* args); void Message_destroy(s_Message* self); diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc index e6f5d3e259..3aa2deef44 100644 --- a/src/lib/dns/python/messagerenderer_python.cc +++ b/src/lib/dns/python/messagerenderer_python.cc @@ -25,13 +25,6 @@ using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; -// MessageRenderer - -s_MessageRenderer::s_MessageRenderer() : outputbuffer(NULL), - messagerenderer(NULL) -{ -} - namespace { int MessageRenderer_init(s_MessageRenderer* self); void MessageRenderer_destroy(s_MessageRenderer* self); @@ -174,7 +167,6 @@ MessageRenderer_clear(s_MessageRenderer* self) { } } // end of unnamed namespace -// end of MessageRenderer namespace isc { namespace dns { namespace python { @@ -233,27 +225,12 @@ PyTypeObject messagerenderer_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here -bool -initModulePart_MessageRenderer(PyObject* mod) { - // Add the exceptions to the module - - // Add the enums to the module - - // Add the constants to the module - - // Add the classes to the module - // We initialize the static description object with PyType_Ready(), - // then add it to the module - - // NameComparisonResult +bool initModulePart_MessageRenderer(PyObject* mod) { if (PyType_Ready(&messagerenderer_type) < 0) { return (false); } Py_INCREF(&messagerenderer_type); - // Class variables - // These are added to the tp_dict of the type object addClassVariable(messagerenderer_type, "CASE_INSENSITIVE", Py_BuildValue("I", MessageRenderer::CASE_INSENSITIVE)); addClassVariable(messagerenderer_type, "CASE_SENSITIVE", @@ -261,7 +238,7 @@ initModulePart_MessageRenderer(PyObject* mod) { PyModule_AddObject(mod, "MessageRenderer", reinterpret_cast(&messagerenderer_type)); - + return (true); } } // namespace python diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index 63f22debc5..2546f6ab59 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -25,20 +25,12 @@ #include "messagerenderer_python.h" #include "name_python.h" -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; using namespace isc::util::python; namespace { -// NameComparisonResult int NameComparisonResult_init(s_NameComparisonResult*, PyObject*); void NameComparisonResult_destroy(s_NameComparisonResult* self); @@ -84,9 +76,7 @@ PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self) { return (Py_BuildValue("I", self->cppobj->getRelation())); } -// end of NameComparisonResult -// Name // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer NameContainer; @@ -495,7 +485,7 @@ Name_isWildCard(s_Name* self) { Py_RETURN_FALSE; } } -// end of Name + } // end of unnamed namespace namespace isc { @@ -634,7 +624,6 @@ PyTypeObject name_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here bool initModulePart_Name(PyObject* mod) { // Add the classes to the module diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index a9490a709e..14e2056b5b 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -24,21 +24,7 @@ using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; -// -// Declaration of the custom exceptions (None for this class) - -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - namespace { -// -// Opcode -// int Opcode_init(s_Opcode* const self, PyObject* args); void Opcode_destroy(s_Opcode* const self); @@ -344,7 +330,6 @@ PyTypeObject opcode_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here bool initModulePart_Opcode(PyObject* mod) { // We initialize the static description object with PyType_Ready(), diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index a4f2a9482d..d0c583f0b4 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -30,17 +30,8 @@ using namespace isc::dns::python; using namespace isc::util; using namespace isc; -// -// Question -// namespace { -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// - -// General creation and destruction static int Question_init(s_Question* self, PyObject* args); static void Question_destroy(s_Question* self); @@ -221,8 +212,6 @@ Question_toWire(s_Question* self, PyObject* args) { return (NULL); } -// end of Question - } // end of unnamed namespace namespace isc { @@ -283,9 +272,6 @@ PyTypeObject question_type = { 0 // tp_version_tag }; - - -// Module Initialization, all statics are initialized here bool initModulePart_Question(PyObject* mod) { // Add the exceptions to the module diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc index b594ad33b5..0208d98406 100644 --- a/src/lib/dns/python/rcode_python.cc +++ b/src/lib/dns/python/rcode_python.cc @@ -24,21 +24,6 @@ using namespace isc::dns; using namespace isc::dns::python; -// -// Declaration of the custom exceptions (None for this class) - -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// Rcode -// - // Trivial constructor. s_Rcode::s_Rcode() : cppobj(NULL), static_code(false) {} @@ -282,7 +267,7 @@ Rcode_BADVERS(const s_Rcode*) { return (Rcode_createStatic(Rcode::BADVERS())); } -PyObject* +PyObject* Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other, const int op) { @@ -376,7 +361,6 @@ PyTypeObject rcode_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here bool initModulePart_Rcode(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -429,6 +413,7 @@ initModulePart_Rcode(PyObject* mod) { return (true); } + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index 19dbd7e1ed..d57a8fb4c7 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -28,18 +28,6 @@ using namespace isc::dns::python; using namespace isc::util; using namespace isc::dns::rdata; -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// Rdata -// - namespace { // @@ -196,7 +184,6 @@ RData_richcmp(s_Rdata* self, s_Rdata* other, int op) { else Py_RETURN_FALSE; } -// end of Rdata } // end of unnamed namespace diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index 46cd87bd9f..eff36100d9 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -27,18 +27,6 @@ using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; using namespace isc::util::python; -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// RRClass -// - namespace { // @@ -269,7 +257,6 @@ static PyObject* RRClass_NONE(s_RRClass*) { static PyObject* RRClass_ANY(s_RRClass*) { return (RRClass_createStatic(RRClass::ANY())); } -// end of RRClass } // end anonymous namespace diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index c9513c24d3..cbaa99c37a 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -29,21 +29,12 @@ #include "rdata_python.h" #include "messagerenderer_python.h" -namespace { - -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; using namespace isc::util::python; -// RRset +namespace { // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer RRsetContainer; @@ -321,10 +312,6 @@ RRset_removeRRsig(s_RRset* self) { Py_RETURN_NONE; } - -// end of RRset - - } // end of unnamed namespace namespace isc { diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index 901ea4ebdb..645a1dc191 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -29,28 +29,10 @@ using namespace isc::dns::python; using namespace isc::util; namespace { -// -// Definition of the classes -// -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// RRTTL -// - -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// - -// General creation and destruction static int RRTTL_init(s_RRTTL* self, PyObject* args); static void RRTTL_destroy(s_RRTTL* self); -// These are the functions we export static PyObject* RRTTL_toText(s_RRTTL* self); // This is a second version of toText, we need one where the argument // is a PyObject*, for the str() function in python. @@ -229,9 +211,8 @@ RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op) { else Py_RETURN_FALSE; } -// end of RRTTL -} // end anonymous namespace +} // end anonymous namespace namespace isc { namespace dns { diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index e59fef49bb..5865623434 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -29,25 +29,8 @@ using namespace isc::dns::python; using namespace isc::util; using namespace isc::util::python; - -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description namespace { -// -// RRType -// - -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// - // General creation and destruction static int RRType_init(s_RRType* self, PyObject* args); static void RRType_destroy(s_RRType* self); @@ -445,7 +428,6 @@ PyTypeObject rrtype_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here bool initModulePart_RRType(PyObject* mod) { // Add the exceptions to the module From 51c4b53945599a72d550d7380c7107e11b467d5c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 13 Sep 2011 22:55:29 -0700 Subject: [PATCH 707/974] [1177] some minor, mainly editorial, changes: coding style, typo, simplify, constify, etc. --- src/lib/datasrc/database.cc | 6 ++-- src/lib/datasrc/sqlite3_accessor.cc | 1 - src/lib/datasrc/tests/database_unittest.cc | 34 ++++++++-------------- src/lib/datasrc/zone.h | 2 +- 4 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 08e642be23..8cc70920cc 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -316,8 +316,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // This variable is used to determine the difference between // NXDOMAIN and NXRRSET bool records_found = false; - bool glue_ok(options & FIND_GLUE_OK); - bool dnssec_data(options & FIND_DNSSEC); + bool glue_ok((options & FIND_GLUE_OK) != 0); + const bool dnssec_data((options & FIND_DNSSEC) != 0); bool get_cover(false); isc::dns::RRsetPtr result_rrset; ZoneFinder::Result result_status = SUCCESS; @@ -533,7 +533,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } } // This is the NXDOMAIN case (nothing found anywhere). If - // they wand DNSSEC data, try getting the NSEC record + // they want DNSSEC data, try getting the NSEC record if (dnssec_data && !records_found) { get_cover = true; } diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index a5ac4c7978..2093dcbea2 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -725,7 +725,6 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname) statements_[FIND_PREVIOUS_WRAP], 0), dbparameters_->db_); } sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); - if (rc == SQLITE_DONE) { // No NSEC records, this DB doesn't support DNSSEC isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC"); diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 35827f50be..4a8309c3cc 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1582,7 +1582,7 @@ TYPED_TEST(DatabaseClientTest, wildcard) { TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) { // The domain exists, but doesn't have this RRType - // So we should get it's NSEC + // So we should get its NSEC shared_ptr finder(this->getFinder()); this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG"); @@ -1591,15 +1591,14 @@ TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) { "FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("www.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), - isc::dns::RRTTL(3600), - ZoneFinder::NXRRSET, + this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_, this->expected_sig_rdatas_, Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC); } TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) { // The domain exists, but doesn't have this RRType - // So we should get it's NSEC + // So we should get its NSEC // // The user will have to query us again to get the correct // answer (eg. prove there's not an exact match) @@ -1613,8 +1612,7 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) { // Note that the NSEC name should NOT be synthesized. doFindTest(*finder, isc::dns::Name("a.wild.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), - isc::dns::RRTTL(3600), - ZoneFinder::WILDCARD_NXRRSET, + this->rrttl_, ZoneFinder::WILDCARD_NXRRSET, this->expected_rdatas_, this->expected_sig_rdatas_, Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC); } @@ -1629,8 +1627,7 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) { "FAKEFAKEFAKE"); doFindTest(*finder, isc::dns::Name("www1.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), - isc::dns::RRTTL(3600), - ZoneFinder::NXDOMAIN, + this->rrttl_, ZoneFinder::NXDOMAIN, this->expected_rdatas_, this->expected_sig_rdatas_, Name("www.example.org."), ZoneFinder::FIND_DNSSEC); @@ -1639,16 +1636,13 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) { if (!this->is_mock_) { return; // We don't make the real DB to throw } - this->expected_rdatas_.clear(); - this->expected_sig_rdatas_.clear(); EXPECT_NO_THROW(doFindTest(*finder, isc::dns::Name("notimplnsec.example.org."), isc::dns::RRType::TXT(), - isc::dns::RRType::NSEC(), - isc::dns::RRTTL(3600), ZoneFinder::NXDOMAIN, - this->expected_rdatas_, - this->expected_sig_rdatas_, - Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC)); + isc::dns::RRType::NSEC(), this->rrttl_, + ZoneFinder::NXDOMAIN, this->empty_rdatas_, + this->empty_rdatas_, Name::ROOT_NAME(), + ZoneFinder::FIND_DNSSEC)); } TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) { @@ -1660,8 +1654,7 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) { this->expected_rdatas_.push_back("empty.nonterminal.example.org. NSEC"); doFindTest(*finder, isc::dns::Name("nonterminal.example.org."), - isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), - isc::dns::RRTTL(3600), + isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), this->rrttl_, ZoneFinder::NXRRSET, // FIXME: Do we want to have specific code? this->expected_rdatas_, this->expected_sig_rdatas_, Name("l.example.org."), ZoneFinder::FIND_DNSSEC); @@ -1671,15 +1664,12 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) { if (!this->is_mock_) { return; // We don't make the real DB to throw } - this->expected_rdatas_.clear(); - this->expected_sig_rdatas_.clear(); EXPECT_NO_THROW(doFindTest(*finder, isc::dns::Name("here.wild.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), - isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, - this->expected_rdatas_, - this->expected_sig_rdatas_, + this->rrttl_, ZoneFinder::NXRRSET, + this->empty_rdatas_, this->empty_rdatas_, Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC)); } diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 61fd5fb733..14bf295808 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -212,7 +212,7 @@ public: /// \brief Get previous name in the zone /// /// Gets the previous name in the DNSSEC/NSEC order. This can be used - /// to find the correct NSEC records for proving nonexistenc + /// to find the correct NSEC records for proving nonexistence /// of domains. /// /// The concrete implementation might throw anything it thinks appropriate, From 9f441d72a245e3ccce2ee014adaa0ad62e7b0d29 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Wed, 14 Sep 2011 17:08:17 +0800 Subject: [PATCH 708/974] [trac1112] Add escaped characters support and unit tests to to characterstr class --- src/lib/dns/character_string.cc | 63 +++++++++++-- src/lib/dns/tests/Makefile.am | 1 + .../dns/tests/character_string_unittest.cc | 94 +++++++++++++++++++ 3 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 src/lib/dns/tests/character_string_unittest.cc diff --git a/src/lib/dns/character_string.cc b/src/lib/dns/character_string.cc index c65ddcce3b..ae2ad7a15a 100644 --- a/src/lib/dns/character_string.cc +++ b/src/lib/dns/character_string.cc @@ -21,6 +21,8 @@ using namespace isc::dns::rdata; namespace isc { namespace dns { +#define IS_DIGIT(c) (('0' <= (c)) && ((c) <= '9')) + std::string characterstr::getNextCharacterString(const std::string& input_str, std::string::const_iterator& input_iterator) @@ -36,27 +38,64 @@ characterstr::getNextCharacterString(const std::string& input_str, // Whether the is separated with double quotes (") bool quotes_separated = (*input_iterator == '"'); + // Whether the quotes are pared if the string is quotes separated + bool quotes_paired = false; if (quotes_separated) { ++input_iterator; } while(input_iterator < input_str.end()){ + // Escaped characters processing + if (*input_iterator == '\\') { + if (input_iterator + 1 == input_str.end()) { + isc_throw(InvalidRdataText, " ended \ + exceptionally."); + } else { + if (IS_DIGIT(*(input_iterator + 1))) { + // \DDD where each D is a digit. It its the octet + // corresponding to the decimal number described by DDD + if (input_iterator + 3 >= input_str.end()) { + isc_throw(InvalidRdataText, " ended \ + exceptionally."); + } else { + int n = 0; + ++input_iterator; + for (int i = 0; i < 3; ++i) { + if (('0' <= *input_iterator) && + (*input_iterator <= '9')) { + n = n*10 + (*input_iterator - '0'); + ++input_iterator; + } else { + isc_throw(InvalidRdataText, "Illegal decimal \ + escaping series"); + } + } + if (n > 255) { + isc_throw(InvalidRdataText, "Illegal octet \ + number"); + } + result.push_back(n); + continue; + } + } else { + ++input_iterator; + result.push_back(*input_iterator); + ++input_iterator; + continue; + } + } + } + if (quotes_separated) { // If the is seperated with quotes symbol and // another quotes symbol is encountered, it is the end of the // if (*input_iterator == '"') { - // Inside a " delimited string any character can occur, except - // for a " itself, which must be quoted using \ (back slash). - if (*(input_iterator - 1) == '\\') { - // pop the '\' character - result.resize(result.size() - 1); - } else { - ++input_iterator; - // Reach the end of character string - break; - } + quotes_paired = true; + ++input_iterator; + // Reach the end of character string + break; } } else if (*input_iterator == ' ') { // If the is not seperated with quotes symbol, @@ -73,6 +112,10 @@ characterstr::getNextCharacterString(const std::string& input_str, isc_throw(CharStringTooLong, " is too long"); } + if (quotes_separated && !quotes_paired) { + isc_throw(InvalidRdataText, "The quotes are not paired"); + } + return (result); } diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index d1a5025b58..5f90cea949 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -56,6 +56,7 @@ run_unittests_SOURCES += tsig_unittest.cc run_unittests_SOURCES += tsigerror_unittest.cc run_unittests_SOURCES += tsigkey_unittest.cc run_unittests_SOURCES += tsigrecord_unittest.cc +run_unittests_SOURCES += character_string_unittest.cc run_unittests_SOURCES += run_unittests.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) # We shouldn't need to include BOTAN_LDFLAGS here, but there diff --git a/src/lib/dns/tests/character_string_unittest.cc b/src/lib/dns/tests/character_string_unittest.cc new file mode 100644 index 0000000000..83dbf923a3 --- /dev/null +++ b/src/lib/dns/tests/character_string_unittest.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + + +#include + +#include +#include +#include + +using isc::UnitTestUtil; + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::dns::characterstr; +using namespace isc::dns::rdata; + +namespace { +class CharacterStringTest : public ::testing::Test { +}; + +class CharacterString { +public: + CharacterString(const string& str){ + string::const_iterator it = str.begin(); + characterStr_ = getNextCharacterString(str, it); + } + const string& str() const { return characterStr_; } +private: + string characterStr_; +}; + +TEST_F(CharacterStringTest, testNormalCase) { + CharacterString cstr1("foo"); + EXPECT_EQ(string("foo"), cstr1.str()); + + // Test that separated by space + CharacterString cstr2("foo bar"); + EXPECT_EQ(string("foo"), cstr2.str()); + + // Test that separated by quotes + CharacterString cstr3("\"foo bar\""); + EXPECT_EQ(string("foo bar"), cstr3.str()); + + // Test that not separate by quotes but ended with quotes + CharacterString cstr4("foo\""); + EXPECT_EQ(string("foo\""), cstr4.str()); +} + +TEST_F(CharacterStringTest, testBadCase) { + // The that started with quotes should also be ended + // with quotes + EXPECT_THROW(CharacterString cstr("\"foo"), InvalidRdataText); + + // The string length cannot exceed 255 characters + string str; + for (int i = 0; i < 257; ++i) { + str += 'A'; + } + EXPECT_THROW(CharacterString cstr(str), CharStringTooLong); +} + +TEST_F(CharacterStringTest, testEscapeCharacter) { + CharacterString cstr1("foo\\bar"); + EXPECT_EQ(string("foobar"), cstr1.str()); + + CharacterString cstr2("foo\\\\bar"); + EXPECT_EQ(string("foo\\bar"), cstr2.str()); + + CharacterString cstr3("fo\\111bar"); + EXPECT_EQ(string("foobar"), cstr3.str()); + + CharacterString cstr4("fo\\1112bar"); + EXPECT_EQ(string("foo2bar"), cstr4.str()); + + // There must be at least 3 digits followed by '\' + EXPECT_THROW(CharacterString cstr("foo\\98ar"), InvalidRdataText); + EXPECT_THROW(CharacterString cstr("foo\\9ar"), InvalidRdataText); + EXPECT_THROW(CharacterString cstr("foo\\98"), InvalidRdataText); +} + +} // namespace From c9ad781ebbaebb2e57956ac9eda542eaa88a743b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 14 Sep 2011 08:06:14 -0700 Subject: [PATCH 709/974] [1245] two small fixes: missing LIBRARY_PATH_PLACEHOLDER in some test's Makefile.am (usual MacOS nit); made sure PY_SSIZE_T_CLEAN is defined when 'y#' is used with PyArg_ParseTuple. For the latter, see http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers --- src/bin/cfgmgr/plugins/tests/Makefile.am | 2 +- src/bin/xfrin/tests/Makefile.am | 2 +- src/bin/xfrout/tests/Makefile.am | 2 +- src/lib/dns/python/message_python.cc | 1 + src/lib/dns/python/question_python.cc | 1 + src/lib/dns/python/tests/Makefile.am | 2 +- src/lib/dns/python/tsig_rdata_python.cc | 1 + src/lib/python/isc/acl/tests/Makefile.am | 2 +- src/lib/python/isc/notify/tests/Makefile.am | 2 +- 9 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/bin/cfgmgr/plugins/tests/Makefile.am b/src/bin/cfgmgr/plugins/tests/Makefile.am index 07b7a858c2..bda775b72b 100644 --- a/src/bin/cfgmgr/plugins/tests/Makefile.am +++ b/src/bin/cfgmgr/plugins/tests/Makefile.am @@ -7,7 +7,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am index 0f485aa2fd..e8319d594a 100644 --- a/src/bin/xfrin/tests/Makefile.am +++ b/src/bin/xfrin/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index 2f1e2eaef5..1b99e37b83 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -6,7 +6,7 @@ noinst_SCRIPTS = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 23b95d583b..cd945fb4f3 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -12,6 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#define PY_SSIZE_T_CLEAN #include #include diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index d0c583f0b4..234009e8c6 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -12,6 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#define PY_SSIZE_T_CLEAN #include #include #include diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am index 61d7df6e6a..d1273f3551 100644 --- a/src/lib/dns/python/tests/Makefile.am +++ b/src/lib/dns/python/tests/Makefile.am @@ -24,7 +24,7 @@ EXTRA_DIST += testutil.py # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc index 4e4f2879ed..ebaa0ca895 100644 --- a/src/lib/dns/python/tsig_rdata_python.cc +++ b/src/lib/dns/python/tsig_rdata_python.cc @@ -12,6 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#define PY_SSIZE_T_CLEAN #include #include diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am index 87781d72bc..42867b238f 100644 --- a/src/lib/python/isc/acl/tests/Makefile.am +++ b/src/lib/python/isc/acl/tests/Makefile.am @@ -7,7 +7,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/notify/tests/Makefile.am b/src/lib/python/isc/notify/tests/Makefile.am index 1427d93c12..00a8d3c976 100644 --- a/src/lib/python/isc/notify/tests/Makefile.am +++ b/src/lib/python/isc/notify/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS From 8ee5844e8dc3ec7d99a5890bdc85f54afd8886b6 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 14 Sep 2011 21:08:06 -0700 Subject: [PATCH 710/974] [1245] editorial cleanups: removed unnecesary 'static'; fixed typo in comments. --- src/lib/dns/python/rdata_python.cc | 28 +++---- src/lib/dns/python/rrclass_python.cc | 52 ++++++------ src/lib/dns/python/rrclass_python.h | 4 +- src/lib/dns/python/rrset_python.cc | 64 +++++++-------- src/lib/dns/python/rrset_python.h | 4 +- src/lib/dns/python/rrttl_python.cc | 30 +++---- src/lib/dns/python/rrtype_python.cc | 108 ++++++++++++------------- src/lib/dns/python/rrtype_python.h | 4 +- src/lib/util/python/wrapper_template.h | 6 +- 9 files changed, 149 insertions(+), 151 deletions(-) diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index d57a8fb4c7..8596d74a0a 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -36,16 +36,16 @@ namespace { // // General creation and destruction -static int Rdata_init(s_Rdata* self, PyObject* args); -static void Rdata_destroy(s_Rdata* self); +int Rdata_init(s_Rdata* self, PyObject* args); +void Rdata_destroy(s_Rdata* self); // These are the functions we export -static PyObject* Rdata_toText(s_Rdata* self); +PyObject* Rdata_toText(s_Rdata* self); // This is a second version of toText, we need one where the argument // is a PyObject*, for the str() function in python. -static PyObject* Rdata_str(PyObject* self); -static PyObject* Rdata_toWire(s_Rdata* self, PyObject* args); -static PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op); +PyObject* Rdata_str(PyObject* self); +PyObject* Rdata_toWire(s_Rdata* self, PyObject* args); +PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op); // This list contains the actual set of functions we have in // python. Each entry has @@ -53,7 +53,7 @@ static PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op); // 2. Our static function here // 3. Argument type // 4. Documentation -static PyMethodDef Rdata_methods[] = { +PyMethodDef Rdata_methods[] = { { "to_text", reinterpret_cast(Rdata_toText), METH_NOARGS, "Returns the string representation" }, { "to_wire", reinterpret_cast(Rdata_toWire), METH_VARARGS, @@ -66,7 +66,7 @@ static PyMethodDef Rdata_methods[] = { { NULL, NULL, 0, NULL } }; -static int +int Rdata_init(s_Rdata* self, PyObject* args) { s_RRType* rrtype; s_RRClass* rrclass; @@ -91,7 +91,7 @@ Rdata_init(s_Rdata* self, PyObject* args) { return (-1); } -static void +void Rdata_destroy(s_Rdata* self) { // Clear the shared_ptr so that its reference count is zero // before we call tp_free() (there is no direct release()) @@ -99,13 +99,13 @@ Rdata_destroy(s_Rdata* self) { Py_TYPE(self)->tp_free(self); } -static PyObject* +PyObject* Rdata_toText(s_Rdata* self) { // Py_BuildValue makes python objects from native data return (Py_BuildValue("s", self->rdata->toText().c_str())); } -static PyObject* +PyObject* Rdata_str(PyObject* self) { // Simply call the to_text method we already defined return (PyObject_CallMethod(self, @@ -113,7 +113,7 @@ Rdata_str(PyObject* self) { const_cast(""))); } -static PyObject* +PyObject* Rdata_toWire(s_Rdata* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; @@ -141,9 +141,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) { return (NULL); } - - -static PyObject* +PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op) { bool c; diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index eff36100d9..a2d4688654 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -35,24 +35,24 @@ namespace { // // General creation and destruction -static int RRClass_init(s_RRClass* self, PyObject* args); -static void RRClass_destroy(s_RRClass* self); +int RRClass_init(s_RRClass* self, PyObject* args); +void RRClass_destroy(s_RRClass* self); // These are the functions we export -static PyObject* RRClass_toText(s_RRClass* self); +PyObject* RRClass_toText(s_RRClass* self); // This is a second version of toText, we need one where the argument // is a PyObject*, for the str() function in python. -static PyObject* RRClass_str(PyObject* self); -static PyObject* RRClass_toWire(s_RRClass* self, PyObject* args); -static PyObject* RRClass_getCode(s_RRClass* self); -static PyObject* RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op); +PyObject* RRClass_str(PyObject* self); +PyObject* RRClass_toWire(s_RRClass* self, PyObject* args); +PyObject* RRClass_getCode(s_RRClass* self); +PyObject* RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op); // Static function for direct class creation -static PyObject* RRClass_IN(s_RRClass *self); -static PyObject* RRClass_CH(s_RRClass *self); -static PyObject* RRClass_HS(s_RRClass *self); -static PyObject* RRClass_NONE(s_RRClass *self); -static PyObject* RRClass_ANY(s_RRClass *self); +PyObject* RRClass_IN(s_RRClass *self); +PyObject* RRClass_CH(s_RRClass *self); +PyObject* RRClass_HS(s_RRClass *self); +PyObject* RRClass_NONE(s_RRClass *self); +PyObject* RRClass_ANY(s_RRClass *self); typedef CPPPyObjectContainer RRClassContainer; @@ -62,7 +62,7 @@ typedef CPPPyObjectContainer RRClassContainer; // 2. Our static function here // 3. Argument type // 4. Documentation -static PyMethodDef RRClass_methods[] = { +PyMethodDef RRClass_methods[] = { { "to_text", reinterpret_cast(RRClass_toText), METH_NOARGS, "Returns the string representation" }, { "to_wire", reinterpret_cast(RRClass_toWire), METH_VARARGS, @@ -82,7 +82,7 @@ static PyMethodDef RRClass_methods[] = { { NULL, NULL, 0, NULL } }; -static int +int RRClass_init(s_RRClass* self, PyObject* args) { const char* s; long i; @@ -131,20 +131,20 @@ RRClass_init(s_RRClass* self, PyObject* args) { return (-1); } -static void +void RRClass_destroy(s_RRClass* self) { delete self->cppobj; self->cppobj = NULL; Py_TYPE(self)->tp_free(self); } -static PyObject* +PyObject* RRClass_toText(s_RRClass* self) { // Py_BuildValue makes python objects from native data return (Py_BuildValue("s", self->cppobj->toText().c_str())); } -static PyObject* +PyObject* RRClass_str(PyObject* self) { // Simply call the to_text method we already defined return (PyObject_CallMethod(self, @@ -152,7 +152,7 @@ RRClass_str(PyObject* self) { const_cast(""))); } -static PyObject* +PyObject* RRClass_toWire(s_RRClass* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; @@ -180,12 +180,12 @@ RRClass_toWire(s_RRClass* self, PyObject* args) { return (NULL); } -static PyObject* +PyObject* RRClass_getCode(s_RRClass* self) { return (Py_BuildValue("I", self->cppobj->getCode())); } -static PyObject* +PyObject* RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op) { bool c; @@ -230,7 +230,7 @@ RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op) { // // Common function for RRClass_IN/CH/etc. // -static PyObject* RRClass_createStatic(RRClass stc) { +PyObject* RRClass_createStatic(RRClass stc) { s_RRClass* ret = PyObject_New(s_RRClass, &rrclass_type); if (ret != NULL) { ret->cppobj = new RRClass(stc); @@ -238,23 +238,23 @@ static PyObject* RRClass_createStatic(RRClass stc) { return (ret); } -static PyObject* RRClass_IN(s_RRClass*) { +PyObject* RRClass_IN(s_RRClass*) { return (RRClass_createStatic(RRClass::IN())); } -static PyObject* RRClass_CH(s_RRClass*) { +PyObject* RRClass_CH(s_RRClass*) { return (RRClass_createStatic(RRClass::CH())); } -static PyObject* RRClass_HS(s_RRClass*) { +PyObject* RRClass_HS(s_RRClass*) { return (RRClass_createStatic(RRClass::HS())); } -static PyObject* RRClass_NONE(s_RRClass*) { +PyObject* RRClass_NONE(s_RRClass*) { return (RRClass_createStatic(RRClass::NONE())); } -static PyObject* RRClass_ANY(s_RRClass*) { +PyObject* RRClass_ANY(s_RRClass*) { return (RRClass_createStatic(RRClass::ANY())); } diff --git a/src/lib/dns/python/rrclass_python.h b/src/lib/dns/python/rrclass_python.h index 61b741fc01..6ad733f6ef 100644 --- a/src/lib/dns/python/rrclass_python.h +++ b/src/lib/dns/python/rrclass_python.h @@ -41,12 +41,12 @@ extern PyTypeObject rrclass_type; bool initModulePart_RRClass(PyObject* mod); -/// This is A simple shortcut to create a python RRClass object (in the +/// This is a simple shortcut to create a python RRClass object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference /// counter of 1; if something goes wrong it throws an exception (it never /// returns a NULL pointer). -/// This function is expected to be called with in a try block +/// This function is expected to be called within a try block /// followed by necessary setup for python exception. PyObject* createRRClassObject(const RRClass& source); diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index cbaa99c37a..cdcd50fb9b 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -39,26 +39,26 @@ namespace { // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer RRsetContainer; -static int RRset_init(s_RRset* self, PyObject* args); -static void RRset_destroy(s_RRset* self); +int RRset_init(s_RRset* self, PyObject* args); +void RRset_destroy(s_RRset* self); -static PyObject* RRset_getRdataCount(s_RRset* self); -static PyObject* RRset_getName(s_RRset* self); -static PyObject* RRset_getClass(s_RRset* self); -static PyObject* RRset_getType(s_RRset* self); -static PyObject* RRset_getTTL(s_RRset* self); -static PyObject* RRset_setName(s_RRset* self, PyObject* args); -static PyObject* RRset_setTTL(s_RRset* self, PyObject* args); -static PyObject* RRset_toText(s_RRset* self); -static PyObject* RRset_str(PyObject* self); -static PyObject* RRset_toWire(s_RRset* self, PyObject* args); -static PyObject* RRset_addRdata(s_RRset* self, PyObject* args); -static PyObject* RRset_getRdata(s_RRset* self); -static PyObject* RRset_removeRRsig(s_RRset* self); +PyObject* RRset_getRdataCount(s_RRset* self); +PyObject* RRset_getName(s_RRset* self); +PyObject* RRset_getClass(s_RRset* self); +PyObject* RRset_getType(s_RRset* self); +PyObject* RRset_getTTL(s_RRset* self); +PyObject* RRset_setName(s_RRset* self, PyObject* args); +PyObject* RRset_setTTL(s_RRset* self, PyObject* args); +PyObject* RRset_toText(s_RRset* self); +PyObject* RRset_str(PyObject* self); +PyObject* RRset_toWire(s_RRset* self, PyObject* args); +PyObject* RRset_addRdata(s_RRset* self, PyObject* args); +PyObject* RRset_getRdata(s_RRset* self); +PyObject* RRset_removeRRsig(s_RRset* self); // TODO: iterator? -static PyMethodDef RRset_methods[] = { +PyMethodDef RRset_methods[] = { { "get_rdata_count", reinterpret_cast(RRset_getRdataCount), METH_NOARGS, "Returns the number of rdata fields." }, { "get_name", reinterpret_cast(RRset_getName), METH_NOARGS, @@ -91,7 +91,7 @@ static PyMethodDef RRset_methods[] = { { NULL, NULL, 0, NULL } }; -static int +int RRset_init(s_RRset* self, PyObject* args) { s_Name* name; s_RRClass* rrclass; @@ -112,7 +112,7 @@ RRset_init(s_RRset* self, PyObject* args) { return (-1); } -static void +void RRset_destroy(s_RRset* self) { // Clear the shared_ptr so that its reference count is zero // before we call tp_free() (there is no direct release()) @@ -120,12 +120,12 @@ RRset_destroy(s_RRset* self) { Py_TYPE(self)->tp_free(self); } -static PyObject* +PyObject* RRset_getRdataCount(s_RRset* self) { return (Py_BuildValue("I", self->rrset->getRdataCount())); } -static PyObject* +PyObject* RRset_getName(s_RRset* self) { s_Name* name; @@ -143,7 +143,7 @@ RRset_getName(s_RRset* self) { return (name); } -static PyObject* +PyObject* RRset_getClass(s_RRset* self) { s_RRClass* rrclass; @@ -160,7 +160,7 @@ RRset_getClass(s_RRset* self) { return (rrclass); } -static PyObject* +PyObject* RRset_getType(s_RRset* self) { s_RRType* rrtype; @@ -177,7 +177,7 @@ RRset_getType(s_RRset* self) { return (rrtype); } -static PyObject* +PyObject* RRset_getTTL(s_RRset* self) { s_RRTTL* rrttl; @@ -194,7 +194,7 @@ RRset_getTTL(s_RRset* self) { return (rrttl); } -static PyObject* +PyObject* RRset_setName(s_RRset* self, PyObject* args) { s_Name* name; if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) { @@ -204,7 +204,7 @@ RRset_setName(s_RRset* self, PyObject* args) { Py_RETURN_NONE; } -static PyObject* +PyObject* RRset_setTTL(s_RRset* self, PyObject* args) { s_RRTTL* rrttl; if (!PyArg_ParseTuple(args, "O!", &rrttl_type, &rrttl)) { @@ -214,7 +214,7 @@ RRset_setTTL(s_RRset* self, PyObject* args) { Py_RETURN_NONE; } -static PyObject* +PyObject* RRset_toText(s_RRset* self) { try { return (Py_BuildValue("s", self->rrset->toText().c_str())); @@ -224,7 +224,7 @@ RRset_toText(s_RRset* self) { } } -static PyObject* +PyObject* RRset_str(PyObject* self) { // Simply call the to_text method we already defined return (PyObject_CallMethod(self, @@ -232,7 +232,7 @@ RRset_str(PyObject* self) { const_cast(""))); } -static PyObject* +PyObject* RRset_toWire(s_RRset* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; @@ -266,7 +266,7 @@ RRset_toWire(s_RRset* self, PyObject* args) { return (NULL); } -static PyObject* +PyObject* RRset_addRdata(s_RRset* self, PyObject* args) { s_Rdata* rdata; if (!PyArg_ParseTuple(args, "O!", &rdata_type, &rdata)) { @@ -283,7 +283,7 @@ RRset_addRdata(s_RRset* self, PyObject* args) { } } -static PyObject* +PyObject* RRset_getRdata(s_RRset* self) { PyObject* list = PyList_New(0); @@ -306,7 +306,7 @@ RRset_getRdata(s_RRset* self) { return (list); } -static PyObject* +PyObject* RRset_removeRRsig(s_RRset* self) { self->rrset->removeRRsig(); Py_RETURN_NONE; @@ -443,7 +443,7 @@ createRRsetObject(const RRset& source) { if (sigs) { py_rrset->rrset->addRRsig(sigs); } - return py_rrset; + return (py_rrset); } catch (const std::bad_alloc&) { isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " "probably due to short memory"); diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h index e125207d25..e43de2858d 100644 --- a/src/lib/dns/python/rrset_python.h +++ b/src/lib/dns/python/rrset_python.h @@ -48,12 +48,12 @@ extern PyTypeObject rrset_type; bool initModulePart_RRset(PyObject* mod); -/// This is A simple shortcut to create a python RRset object (in the +/// This is a simple shortcut to create a python RRset object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference /// counter of 1; if something goes wrong it throws an exception (it never /// returns a NULL pointer). -/// This function is expected to be called with in a try block +/// This function is expected to be called within a try block /// followed by necessary setup for python exception. PyObject* createRRsetObject(const RRset& source); diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index 645a1dc191..e6bab0c227 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -30,16 +30,16 @@ using namespace isc::util; namespace { -static int RRTTL_init(s_RRTTL* self, PyObject* args); -static void RRTTL_destroy(s_RRTTL* self); +int RRTTL_init(s_RRTTL* self, PyObject* args); +void RRTTL_destroy(s_RRTTL* self); -static PyObject* RRTTL_toText(s_RRTTL* self); +PyObject* RRTTL_toText(s_RRTTL* self); // This is a second version of toText, we need one where the argument // is a PyObject*, for the str() function in python. -static PyObject* RRTTL_str(PyObject* self); -static PyObject* RRTTL_toWire(s_RRTTL* self, PyObject* args); -static PyObject* RRTTL_getValue(s_RRTTL* self); -static PyObject* RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op); +PyObject* RRTTL_str(PyObject* self); +PyObject* RRTTL_toWire(s_RRTTL* self, PyObject* args); +PyObject* RRTTL_getValue(s_RRTTL* self); +PyObject* RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op); // This list contains the actual set of functions we have in // python. Each entry has @@ -47,7 +47,7 @@ static PyObject* RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op); // 2. Our static function here // 3. Argument type // 4. Documentation -static PyMethodDef RRTTL_methods[] = { +PyMethodDef RRTTL_methods[] = { { "to_text", reinterpret_cast(RRTTL_toText), METH_NOARGS, "Returns the string representation" }, { "to_wire", reinterpret_cast(RRTTL_toWire), METH_VARARGS, @@ -62,7 +62,7 @@ static PyMethodDef RRTTL_methods[] = { { NULL, NULL, 0, NULL } }; -static int +int RRTTL_init(s_RRTTL* self, PyObject* args) { const char* s; long long i; @@ -119,20 +119,20 @@ RRTTL_init(s_RRTTL* self, PyObject* args) { return (-1); } -static void +void RRTTL_destroy(s_RRTTL* self) { delete self->rrttl; self->rrttl = NULL; Py_TYPE(self)->tp_free(self); } -static PyObject* +PyObject* RRTTL_toText(s_RRTTL* self) { // Py_BuildValue makes python objects from native data return (Py_BuildValue("s", self->rrttl->toText().c_str())); } -static PyObject* +PyObject* RRTTL_str(PyObject* self) { // Simply call the to_text method we already defined return (PyObject_CallMethod(self, @@ -140,7 +140,7 @@ RRTTL_str(PyObject* self) { const_cast(""))); } -static PyObject* +PyObject* RRTTL_toWire(s_RRTTL* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; @@ -169,12 +169,12 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) { return (NULL); } -static PyObject* +PyObject* RRTTL_getValue(s_RRTTL* self) { return (Py_BuildValue("I", self->rrttl->getValue())); } -static PyObject* +PyObject* RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op) { bool c = false; diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index 5865623434..c1bc25c991 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -32,37 +32,37 @@ using namespace isc::util::python; namespace { // General creation and destruction -static int RRType_init(s_RRType* self, PyObject* args); -static void RRType_destroy(s_RRType* self); +int RRType_init(s_RRType* self, PyObject* args); +void RRType_destroy(s_RRType* self); // These are the functions we export -static PyObject* +PyObject* RRType_toText(s_RRType* self); // This is a second version of toText, we need one where the argument // is a PyObject*, for the str() function in python. -static PyObject* RRType_str(PyObject* self); -static PyObject* RRType_toWire(s_RRType* self, PyObject* args); -static PyObject* RRType_getCode(s_RRType* self); -static PyObject* RRType_richcmp(s_RRType* self, s_RRType* other, int op); -static PyObject* RRType_NSEC3PARAM(s_RRType *self); -static PyObject* RRType_DNAME(s_RRType *self); -static PyObject* RRType_PTR(s_RRType *self); -static PyObject* RRType_MX(s_RRType *self); -static PyObject* RRType_DNSKEY(s_RRType *self); -static PyObject* RRType_TXT(s_RRType *self); -static PyObject* RRType_RRSIG(s_RRType *self); -static PyObject* RRType_NSEC(s_RRType *self); -static PyObject* RRType_AAAA(s_RRType *self); -static PyObject* RRType_DS(s_RRType *self); -static PyObject* RRType_OPT(s_RRType *self); -static PyObject* RRType_A(s_RRType *self); -static PyObject* RRType_NS(s_RRType *self); -static PyObject* RRType_CNAME(s_RRType *self); -static PyObject* RRType_SOA(s_RRType *self); -static PyObject* RRType_NSEC3(s_RRType *self); -static PyObject* RRType_IXFR(s_RRType *self); -static PyObject* RRType_AXFR(s_RRType *self); -static PyObject* RRType_ANY(s_RRType *self); +PyObject* RRType_str(PyObject* self); +PyObject* RRType_toWire(s_RRType* self, PyObject* args); +PyObject* RRType_getCode(s_RRType* self); +PyObject* RRType_richcmp(s_RRType* self, s_RRType* other, int op); +PyObject* RRType_NSEC3PARAM(s_RRType *self); +PyObject* RRType_DNAME(s_RRType *self); +PyObject* RRType_PTR(s_RRType *self); +PyObject* RRType_MX(s_RRType *self); +PyObject* RRType_DNSKEY(s_RRType *self); +PyObject* RRType_TXT(s_RRType *self); +PyObject* RRType_RRSIG(s_RRType *self); +PyObject* RRType_NSEC(s_RRType *self); +PyObject* RRType_AAAA(s_RRType *self); +PyObject* RRType_DS(s_RRType *self); +PyObject* RRType_OPT(s_RRType *self); +PyObject* RRType_A(s_RRType *self); +PyObject* RRType_NS(s_RRType *self); +PyObject* RRType_CNAME(s_RRType *self); +PyObject* RRType_SOA(s_RRType *self); +PyObject* RRType_NSEC3(s_RRType *self); +PyObject* RRType_IXFR(s_RRType *self); +PyObject* RRType_AXFR(s_RRType *self); +PyObject* RRType_ANY(s_RRType *self); typedef CPPPyObjectContainer RRTypeContainer; @@ -72,7 +72,7 @@ typedef CPPPyObjectContainer RRTypeContainer; // 2. Our static function here // 3. Argument type // 4. Documentation -static PyMethodDef RRType_methods[] = { +PyMethodDef RRType_methods[] = { { "to_text", reinterpret_cast(RRType_toText), METH_NOARGS, "Returns the string representation" }, { "to_wire", reinterpret_cast(RRType_toWire), METH_VARARGS, @@ -106,7 +106,7 @@ static PyMethodDef RRType_methods[] = { { NULL, NULL, 0, NULL } }; -static int +int RRType_init(s_RRType* self, PyObject* args) { const char* s; long i; @@ -162,27 +162,27 @@ RRType_init(s_RRType* self, PyObject* args) { return (-1); } -static void +void RRType_destroy(s_RRType* self) { delete self->cppobj; self->cppobj = NULL; Py_TYPE(self)->tp_free(self); } -static PyObject* +PyObject* RRType_toText(s_RRType* self) { // Py_BuildValue makes python objects from native data return (Py_BuildValue("s", self->cppobj->toText().c_str())); } -static PyObject* +PyObject* RRType_str(PyObject* self) { // Simply call the to_text method we already defined return (PyObject_CallMethod(self, const_cast("to_text"), const_cast(""))); } -static PyObject* +PyObject* RRType_toWire(s_RRType* self, PyObject* args) { PyObject* bytes; s_MessageRenderer* mr; @@ -210,12 +210,12 @@ RRType_toWire(s_RRType* self, PyObject* args) { return (NULL); } -static PyObject* +PyObject* RRType_getCode(s_RRType* self) { return (Py_BuildValue("I", self->cppobj->getCode())); } -static PyObject* +PyObject* RRType_richcmp(s_RRType* self, s_RRType* other, int op) { bool c; @@ -260,7 +260,7 @@ RRType_richcmp(s_RRType* self, s_RRType* other, int op) { // // Common function for RRType_A/NS/etc. // -static PyObject* RRType_createStatic(RRType stc) { +PyObject* RRType_createStatic(RRType stc) { s_RRType* ret = PyObject_New(s_RRType, &rrtype_type); if (ret != NULL) { ret->cppobj = new RRType(stc); @@ -268,97 +268,97 @@ static PyObject* RRType_createStatic(RRType stc) { return (ret); } -static PyObject* +PyObject* RRType_NSEC3PARAM(s_RRType*) { return (RRType_createStatic(RRType::NSEC3PARAM())); } -static PyObject* +PyObject* RRType_DNAME(s_RRType*) { return (RRType_createStatic(RRType::DNAME())); } -static PyObject* +PyObject* RRType_PTR(s_RRType*) { return (RRType_createStatic(RRType::PTR())); } -static PyObject* +PyObject* RRType_MX(s_RRType*) { return (RRType_createStatic(RRType::MX())); } -static PyObject* +PyObject* RRType_DNSKEY(s_RRType*) { return (RRType_createStatic(RRType::DNSKEY())); } -static PyObject* +PyObject* RRType_TXT(s_RRType*) { return (RRType_createStatic(RRType::TXT())); } -static PyObject* +PyObject* RRType_RRSIG(s_RRType*) { return (RRType_createStatic(RRType::RRSIG())); } -static PyObject* +PyObject* RRType_NSEC(s_RRType*) { return (RRType_createStatic(RRType::NSEC())); } -static PyObject* +PyObject* RRType_AAAA(s_RRType*) { return (RRType_createStatic(RRType::AAAA())); } -static PyObject* +PyObject* RRType_DS(s_RRType*) { return (RRType_createStatic(RRType::DS())); } -static PyObject* +PyObject* RRType_OPT(s_RRType*) { return (RRType_createStatic(RRType::OPT())); } -static PyObject* +PyObject* RRType_A(s_RRType*) { return (RRType_createStatic(RRType::A())); } -static PyObject* +PyObject* RRType_NS(s_RRType*) { return (RRType_createStatic(RRType::NS())); } -static PyObject* +PyObject* RRType_CNAME(s_RRType*) { return (RRType_createStatic(RRType::CNAME())); } -static PyObject* +PyObject* RRType_SOA(s_RRType*) { return (RRType_createStatic(RRType::SOA())); } -static PyObject* +PyObject* RRType_NSEC3(s_RRType*) { return (RRType_createStatic(RRType::NSEC3())); } -static PyObject* +PyObject* RRType_IXFR(s_RRType*) { return (RRType_createStatic(RRType::IXFR())); } -static PyObject* +PyObject* RRType_AXFR(s_RRType*) { return (RRType_createStatic(RRType::AXFR())); } -static PyObject* +PyObject* RRType_ANY(s_RRType*) { return (RRType_createStatic(RRType::ANY())); } diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h index aad13d6c41..5a69e04ccf 100644 --- a/src/lib/dns/python/rrtype_python.h +++ b/src/lib/dns/python/rrtype_python.h @@ -42,12 +42,12 @@ extern PyTypeObject rrtype_type; bool initModulePart_RRType(PyObject* mod); -/// This is A simple shortcut to create a python RRType object (in the +/// This is a simple shortcut to create a python RRType object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference /// counter of 1; if something goes wrong it throws an exception (it never /// returns a NULL pointer). -/// This function is expected to be called with in a try block +/// This function is expected to be called within a try block /// followed by necessary setup for python exception. PyObject* createRRTypeObject(const RRType& source); diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h index d68a658e55..be701e1b01 100644 --- a/src/lib/util/python/wrapper_template.h +++ b/src/lib/util/python/wrapper_template.h @@ -37,15 +37,15 @@ bool initModulePart_@CPPCLASS@(PyObject* mod); // Note: this utility function works only when @CPPCLASS@ is a copy // constructable. // And, it would only be useful when python binding needs to create this -// object frequently. Otherwise, it would (or should) probably better to +// object frequently. Otherwise, it would (or should) probably be better to // remove the declaration and definition of this function. // -/// This is A simple shortcut to create a python @CPPCLASS@ object (in the +/// This is a simple shortcut to create a python @CPPCLASS@ object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference /// counter of 1; if something goes wrong it throws an exception (it never /// returns a NULL pointer). -/// This function is expected to be called with in a try block +/// This function is expected to be called within a try block /// followed by necessary setup for python exception. PyObject* create@CPPCLASS@Object(const @CPPCLASS@& source); From 9c53309978b4a4bf684b3abbb853876c5413f875 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 15 Sep 2011 21:09:15 +0200 Subject: [PATCH 711/974] [1245] remove initModule from headers and move it to an internal namespace move the declaration of the functions to pydnspp.cc --- src/lib/dns/python/edns_python.cc | 2 + src/lib/dns/python/edns_python.h | 2 - src/lib/dns/python/message_python.cc | 2 + src/lib/dns/python/message_python.h | 2 - src/lib/dns/python/messagerenderer_python.cc | 3 ++ src/lib/dns/python/messagerenderer_python.h | 2 - src/lib/dns/python/name_python.cc | 2 + src/lib/dns/python/name_python.h | 2 - src/lib/dns/python/opcode_python.cc | 2 + src/lib/dns/python/opcode_python.h | 3 -- src/lib/dns/python/pydnspp.cc | 50 +++++++++++++------- src/lib/dns/python/question_python.cc | 2 + src/lib/dns/python/question_python.h | 2 - src/lib/dns/python/rcode_python.cc | 2 + src/lib/dns/python/rcode_python.h | 2 - src/lib/dns/python/rdata_python.cc | 2 + src/lib/dns/python/rdata_python.h | 2 - src/lib/dns/python/rrclass_python.cc | 2 + src/lib/dns/python/rrclass_python.h | 2 - src/lib/dns/python/rrset_python.cc | 2 + src/lib/dns/python/rrset_python.h | 2 - src/lib/dns/python/rrttl_python.cc | 2 + src/lib/dns/python/rrttl_python.h | 2 - src/lib/dns/python/rrtype_python.cc | 2 + src/lib/dns/python/rrtype_python.h | 2 - src/lib/dns/python/tsig_python.cc | 5 +- src/lib/dns/python/tsig_python.h | 2 - src/lib/dns/python/tsig_rdata_python.cc | 6 ++- src/lib/dns/python/tsig_rdata_python.h | 2 - src/lib/dns/python/tsigerror_python.cc | 6 ++- src/lib/dns/python/tsigerror_python.h | 2 - src/lib/dns/python/tsigkey_python.cc | 14 ++++-- src/lib/dns/python/tsigkey_python.h | 3 -- src/lib/dns/python/tsigrecord_python.cc | 4 +- src/lib/dns/python/tsigrecord_python.h | 2 - 35 files changed, 83 insertions(+), 63 deletions(-) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index 79154656c0..cab57be415 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -349,6 +349,7 @@ PyTypeObject edns_type = { 0 // tp_version_tag }; +namespace internal { bool initModulePart_EDNS(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -366,6 +367,7 @@ initModulePart_EDNS(PyObject* mod) { return (true); } +} // end namespace internal } // end namespace python } // end namespace dns diff --git a/src/lib/dns/python/edns_python.h b/src/lib/dns/python/edns_python.h index 94b082004f..4f90460e50 100644 --- a/src/lib/dns/python/edns_python.h +++ b/src/lib/dns/python/edns_python.h @@ -35,8 +35,6 @@ public: extern PyTypeObject edns_type; -bool initModulePart_EDNS(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index cd945fb4f3..214e254a38 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -750,6 +750,7 @@ PyTypeObject message_type = { }; // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_Message(PyObject* mod) { if (PyType_Ready(&message_type) < 0) { @@ -816,6 +817,7 @@ initModulePart_Message(PyObject* mod) { return (true); } +} // end namespace internal } // end python namespace } // end dns namespace diff --git a/src/lib/dns/python/message_python.h b/src/lib/dns/python/message_python.h index 325eb8e14b..1a86528e52 100644 --- a/src/lib/dns/python/message_python.h +++ b/src/lib/dns/python/message_python.h @@ -40,8 +40,6 @@ public: extern PyTypeObject message_type; -bool initModulePart_Message(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc index 3aa2deef44..f880a99576 100644 --- a/src/lib/dns/python/messagerenderer_python.cc +++ b/src/lib/dns/python/messagerenderer_python.cc @@ -225,6 +225,7 @@ PyTypeObject messagerenderer_type = { 0 // tp_version_tag }; +namespace internal { bool initModulePart_MessageRenderer(PyObject* mod) { if (PyType_Ready(&messagerenderer_type) < 0) { return (false); @@ -241,6 +242,8 @@ bool initModulePart_MessageRenderer(PyObject* mod) { return (true); } +} // end namespace internal + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h index cc28e437f5..18e0ef955b 100644 --- a/src/lib/dns/python/messagerenderer_python.h +++ b/src/lib/dns/python/messagerenderer_python.h @@ -38,8 +38,6 @@ public: extern PyTypeObject messagerenderer_type; -bool initModulePart_MessageRenderer(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index 2546f6ab59..76b9d7294b 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -624,6 +624,7 @@ PyTypeObject name_type = { 0 // tp_version_tag }; +namespace internal { bool initModulePart_Name(PyObject* mod) { // Add the classes to the module @@ -704,6 +705,7 @@ initModulePart_Name(PyObject* mod) { return (true); } +} // end namespace internal PyObject* createNameObject(const Name& source) { diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h index 4a4b611318..4a5df69ae2 100644 --- a/src/lib/dns/python/name_python.h +++ b/src/lib/dns/python/name_python.h @@ -61,8 +61,6 @@ public: extern PyTypeObject name_comparison_result_type; extern PyTypeObject name_type; -bool initModulePart_Name(PyObject* mod); - /// This is A simple shortcut to create a python Name object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index 14e2056b5b..ee4bce7dc1 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -330,6 +330,7 @@ PyTypeObject opcode_type = { 0 // tp_version_tag }; +namespace internal { bool initModulePart_Opcode(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -380,6 +381,7 @@ initModulePart_Opcode(PyObject* mod) { return (true); } +} // end namespace internal } // end python namespace } // end dns namespace diff --git a/src/lib/dns/python/opcode_python.h b/src/lib/dns/python/opcode_python.h index 79860e4c2b..84adf7a7ca 100644 --- a/src/lib/dns/python/opcode_python.h +++ b/src/lib/dns/python/opcode_python.h @@ -36,11 +36,8 @@ public: bool static_code; }; - extern PyTypeObject opcode_type; -bool initModulePart_Opcode(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 91ec3d11f9..e87d13b55e 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -38,25 +38,41 @@ #include #include "pydnspp_common.h" -#include "messagerenderer_python.h" -#include "name_python.h" -#include "rrclass_python.h" -#include "rrtype_python.h" -#include "rrttl_python.h" -#include "rdata_python.h" -#include "rcode_python.h" -#include "opcode_python.h" -#include "rrset_python.h" -#include "tsigkey_python.h" -#include "tsig_rdata_python.h" -#include "tsigerror_python.h" -#include "tsigrecord_python.h" -#include "tsig_python.h" -#include "question_python.h" -#include "message_python.h" -#include "edns_python.h" +/* Note that we do forward declarations of the initialization functions here, + * and these are not defined in headers (since they are not to be used in any + * other place */ +namespace isc { +namespace dns { +namespace python { +namespace internal { + +bool initModulePart_EDNS(PyObject* mod); +bool initModulePart_Message(PyObject* mod); +bool initModulePart_MessageRenderer(PyObject* mod); +bool initModulePart_Name(PyObject* mod); +bool initModulePart_Opcode(PyObject* mod); +bool initModulePart_Question(PyObject* mod); +bool initModulePart_Rcode(PyObject* mod); +bool initModulePart_Rdata(PyObject* mod); +bool initModulePart_RRClass(PyObject* mod); +bool initModulePart_RRset(PyObject* mod); +bool initModulePart_RRTTL(PyObject* mod); +bool initModulePart_RRType(PyObject* mod); +bool initModulePart_TSIGError(PyObject* mod); +bool initModulePart_TSIGKey(PyObject* mod); +bool initModulePart_TSIGKeyRing(PyObject* mod); +bool initModulePart_TSIGContext(PyObject* mod); +bool initModulePart_TSIG(PyObject* mod); +bool initModulePart_TSIGRecord(PyObject* mod); + +} +} // namespace python +} // namespace dns +} // namespace isc + using namespace isc::dns::python; +using namespace isc::dns::python::internal; // // Definition of the module diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index 234009e8c6..06aaf57c50 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -273,6 +273,7 @@ PyTypeObject question_type = { 0 // tp_version_tag }; +namespace internal { bool initModulePart_Question(PyObject* mod) { // Add the exceptions to the module @@ -289,6 +290,7 @@ initModulePart_Question(PyObject* mod) { return (true); } +} // end namespace internal } // end python namespace } // end dns namespace diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h index a4ee5371a5..b602f06c1e 100644 --- a/src/lib/dns/python/question_python.h +++ b/src/lib/dns/python/question_python.h @@ -37,8 +37,6 @@ public: extern PyTypeObject question_type; -bool initModulePart_Question(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc index 0208d98406..5cb7e8bfac 100644 --- a/src/lib/dns/python/rcode_python.cc +++ b/src/lib/dns/python/rcode_python.cc @@ -361,6 +361,7 @@ PyTypeObject rcode_type = { 0 // tp_version_tag }; +namespace internal { bool initModulePart_Rcode(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -413,6 +414,7 @@ initModulePart_Rcode(PyObject* mod) { return (true); } +} // end namespace internal } // namespace python } // namespace dns diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h index 5d9b91dfaa..68abd1c389 100644 --- a/src/lib/dns/python/rcode_python.h +++ b/src/lib/dns/python/rcode_python.h @@ -45,8 +45,6 @@ public: extern PyTypeObject rcode_type; -bool initModulePart_Rcode(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index 8596d74a0a..99575a218e 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -253,6 +253,7 @@ PyTypeObject rdata_type = { 0 // tp_version_tag }; +namespace internal { // Module Initialization, all statics are initialized here bool initModulePart_Rdata(PyObject* mod) { @@ -279,6 +280,7 @@ initModulePart_Rdata(PyObject* mod) { return (true); } +} // end namespace internal } // end python namespace } // end dns namespace diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h index e9c86678c4..3f09d13a5c 100644 --- a/src/lib/dns/python/rdata_python.h +++ b/src/lib/dns/python/rdata_python.h @@ -37,8 +37,6 @@ public: extern PyTypeObject rdata_type; -bool initModulePart_Rdata(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index a2d4688654..005541c286 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -331,6 +331,7 @@ PyTypeObject rrclass_type = { // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_RRClass(PyObject* mod) { // Add the exceptions to the module @@ -353,6 +354,7 @@ initModulePart_RRClass(PyObject* mod) { return (true); } +} // end namespace internal PyObject* createRRClassObject(const RRClass& source) { diff --git a/src/lib/dns/python/rrclass_python.h b/src/lib/dns/python/rrclass_python.h index 6ad733f6ef..b0c6289d90 100644 --- a/src/lib/dns/python/rrclass_python.h +++ b/src/lib/dns/python/rrclass_python.h @@ -39,8 +39,6 @@ public: extern PyTypeObject rrclass_type; -bool initModulePart_RRClass(PyObject* mod); - /// This is a simple shortcut to create a python RRClass object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index cdcd50fb9b..197fbdf221 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -393,6 +393,7 @@ PyTypeObject rrset_type = { // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_RRset(PyObject* mod) { // Add the exceptions to the module @@ -417,6 +418,7 @@ initModulePart_RRset(PyObject* mod) { return (true); } +} // end namespace internal PyObject* createRRsetObject(const RRset& source) { diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h index e43de2858d..6c083a35ad 100644 --- a/src/lib/dns/python/rrset_python.h +++ b/src/lib/dns/python/rrset_python.h @@ -46,8 +46,6 @@ public: extern PyTypeObject rrset_type; -bool initModulePart_RRset(PyObject* mod); - /// This is a simple shortcut to create a python RRset object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index e6bab0c227..4720bfbdda 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -285,6 +285,7 @@ PyTypeObject rrttl_type = { }; // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_RRTTL(PyObject* mod) { // Add the exceptions to the module @@ -305,6 +306,7 @@ initModulePart_RRTTL(PyObject* mod) { return (true); } +} // end namespace internal } // namespace python } // namespace dns diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h index 2b77540f6d..36f7a99dff 100644 --- a/src/lib/dns/python/rrttl_python.h +++ b/src/lib/dns/python/rrttl_python.h @@ -40,8 +40,6 @@ public: extern PyTypeObject rrttl_type; -bool initModulePart_RRTTL(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index c1bc25c991..fecd89c579 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -428,6 +428,7 @@ PyTypeObject rrtype_type = { 0 // tp_version_tag }; +namespace internal { bool initModulePart_RRType(PyObject* mod) { // Add the exceptions to the module @@ -448,6 +449,7 @@ initModulePart_RRType(PyObject* mod) { return (true); } +} // end namespace internal PyObject* createRRTypeObject(const RRType& source) { diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h index 5a69e04ccf..6aa4ffda68 100644 --- a/src/lib/dns/python/rrtype_python.h +++ b/src/lib/dns/python/rrtype_python.h @@ -40,8 +40,6 @@ public: extern PyTypeObject rrtype_type; -bool initModulePart_RRType(PyObject* mod); - /// This is a simple shortcut to create a python RRType object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc index db93a086d1..678cb17f33 100644 --- a/src/lib/dns/python/tsig_python.cc +++ b/src/lib/dns/python/tsig_python.cc @@ -264,7 +264,7 @@ PyTypeObject tsigcontext_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call NULL, // tp_str NULL, // tp_getattro @@ -308,6 +308,7 @@ PyTypeObject tsigcontext_type = { }; // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_TSIGContext(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -359,6 +360,8 @@ initModulePart_TSIGContext(PyObject* mod) { return (true); } +} // end namespace internal + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h index 1985c8226f..fd0f6a42b9 100644 --- a/src/lib/dns/python/tsig_python.h +++ b/src/lib/dns/python/tsig_python.h @@ -35,8 +35,6 @@ extern PyTypeObject tsigcontext_type; // Class specific exceptions extern PyObject* po_TSIGContextError; -bool initModulePart_TSIGContext(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc index ebaa0ca895..1bf8234b55 100644 --- a/src/lib/dns/python/tsig_rdata_python.cc +++ b/src/lib/dns/python/tsig_rdata_python.cc @@ -236,7 +236,7 @@ TSIG_toWire(const s_TSIG* const self, PyObject* args) { self, args)); } -PyObject* +PyObject* TSIG_richcmp(const s_TSIG* const self, const s_TSIG* const other, const int op) @@ -303,7 +303,7 @@ PyTypeObject tsig_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call TSIG_str, // tp_str NULL, // tp_getattro @@ -342,6 +342,7 @@ PyTypeObject tsig_type = { }; // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_TSIG(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -358,6 +359,7 @@ initModulePart_TSIG(PyObject* mod) { return (true); } +} // end namespace internal PyObject* createTSIGObject(const any::TSIG& source) { diff --git a/src/lib/dns/python/tsig_rdata_python.h b/src/lib/dns/python/tsig_rdata_python.h index e5e0c6cbb0..48a2d6a200 100644 --- a/src/lib/dns/python/tsig_rdata_python.h +++ b/src/lib/dns/python/tsig_rdata_python.h @@ -36,8 +36,6 @@ public: extern PyTypeObject tsig_type; -bool initModulePart_TSIG(PyObject* mod); - /// This is A simple shortcut to create a python TSIG object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc index 0ad471649a..53d294c603 100644 --- a/src/lib/dns/python/tsigerror_python.cc +++ b/src/lib/dns/python/tsigerror_python.cc @@ -190,7 +190,7 @@ TSIGError_toRcode(const s_TSIGError* const self) { return (NULL); } -PyObject* +PyObject* TSIGError_richcmp(const s_TSIGError* const self, const s_TSIGError* const other, const int op) @@ -252,7 +252,7 @@ PyTypeObject tsigerror_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call // THIS MAY HAVE TO BE CHANGED TO NULL: TSIGError_str, // tp_str @@ -300,6 +300,7 @@ installTSIGErrorConstant(const char* name, const TSIGError& val) { } // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_TSIGError(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -358,6 +359,7 @@ initModulePart_TSIGError(PyObject* mod) { return (true); } +} // end namespace internal PyObject* createTSIGErrorObject(const TSIGError& source) { diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h index 9cb1b5dfe9..da028a07fa 100644 --- a/src/lib/dns/python/tsigerror_python.h +++ b/src/lib/dns/python/tsigerror_python.h @@ -32,8 +32,6 @@ public: extern PyTypeObject tsigerror_type; -bool initModulePart_TSIGError(PyObject* mod); - /// This is A simple shortcut to create a python TSIGError object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc index f0906cb449..abd412f76b 100644 --- a/src/lib/dns/python/tsigkey_python.cc +++ b/src/lib/dns/python/tsigkey_python.cc @@ -196,7 +196,7 @@ PyTypeObject tsigkey_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call NULL, // tp_str NULL, // tp_getattro @@ -234,6 +234,7 @@ PyTypeObject tsigkey_type = { }; // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_TSIGKey(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -276,6 +277,8 @@ initModulePart_TSIGKey(PyObject* mod) { return (true); } +} // end namespace internal + } // namespace python } // namespace dns } // namespace isc @@ -329,7 +332,7 @@ TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) { "Invalid arguments to TSIGKeyRing constructor"); return (-1); } - + self->cppobj = new(nothrow) TSIGKeyRing(); if (self->cppobj == NULL) { PyErr_SetString(po_IscException, "Allocating TSIGKeyRing failed"); @@ -354,7 +357,7 @@ TSIGKeyRing_size(const s_TSIGKeyRing* const self) { PyObject* TSIGKeyRing_add(const s_TSIGKeyRing* const self, PyObject* args) { s_TSIGKey* tsigkey; - + if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey)) { try { const TSIGKeyRing::Result result = @@ -436,7 +439,7 @@ PyTypeObject tsigkeyring_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call NULL, // tp_str NULL, // tp_getattro @@ -472,6 +475,7 @@ PyTypeObject tsigkeyring_type = { 0 // tp_version_tag }; +namespace internal { bool initModulePart_TSIGKeyRing(PyObject* mod) { if (PyType_Ready(&tsigkeyring_type) < 0) { @@ -494,6 +498,8 @@ initModulePart_TSIGKeyRing(PyObject* mod) { return (true); } +} // end namespace internal + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h index 980edc5329..f9b9573dbe 100644 --- a/src/lib/dns/python/tsigkey_python.h +++ b/src/lib/dns/python/tsigkey_python.h @@ -39,9 +39,6 @@ public: extern PyTypeObject tsigkey_type; extern PyTypeObject tsigkeyring_type; -bool initModulePart_TSIGKey(PyObject* mod); -bool initModulePart_TSIGKeyRing(PyObject* mod); - } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc index 8a78b5e3a4..974cb22a6b 100644 --- a/src/lib/dns/python/tsigrecord_python.cc +++ b/src/lib/dns/python/tsigrecord_python.cc @@ -226,7 +226,7 @@ PyTypeObject tsigrecord_type = { NULL, // tp_as_number NULL, // tp_as_sequence NULL, // tp_as_mapping - NULL, // tp_hash + NULL, // tp_hash NULL, // tp_call TSIGRecord_str, // tp_str NULL, // tp_getattro @@ -263,6 +263,7 @@ PyTypeObject tsigrecord_type = { }; // Module Initialization, all statics are initialized here +namespace internal { bool initModulePart_TSIGRecord(PyObject* mod) { // We initialize the static description object with PyType_Ready(), @@ -298,6 +299,7 @@ initModulePart_TSIGRecord(PyObject* mod) { return (true); } +} // end namespace internal PyObject* createTSIGRecordObject(const TSIGRecord& source) { diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h index ef9669b33a..3b027184c9 100644 --- a/src/lib/dns/python/tsigrecord_python.h +++ b/src/lib/dns/python/tsigrecord_python.h @@ -32,8 +32,6 @@ public: extern PyTypeObject tsigrecord_type; -bool initModulePart_TSIGRecord(PyObject* mod); - /// This is A simple shortcut to create a python TSIGRecord object (in the /// form of a pointer to PyObject) with minimal exception safety. /// On success, it returns a valid pointer to PyObject with a reference From fbe4ee1f76237fdd586638ce1ded4c6e5bd0bf1d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 15 Sep 2011 21:50:02 +0200 Subject: [PATCH 712/974] [1245] rename internal object to cppobj for consistency --- src/lib/dns/python/edns_python.cc | 38 +++++------ src/lib/dns/python/edns_python.h | 2 +- src/lib/dns/python/message_python.cc | 70 ++++++++++---------- src/lib/dns/python/message_python.h | 2 +- src/lib/dns/python/messagerenderer_python.cc | 28 ++++---- src/lib/dns/python/messagerenderer_python.h | 2 +- src/lib/dns/python/name_python.cc | 2 +- src/lib/dns/python/opcode_python.cc | 18 ++--- src/lib/dns/python/opcode_python.h | 4 +- src/lib/dns/python/pydnspp.cc | 8 --- src/lib/dns/python/pydnspp_towire.h | 2 +- src/lib/dns/python/question_python.cc | 24 +++---- src/lib/dns/python/question_python.h | 2 +- src/lib/dns/python/rdata_python.cc | 28 ++++---- src/lib/dns/python/rdata_python.h | 2 +- src/lib/dns/python/rrclass_python.cc | 2 +- src/lib/dns/python/rrset_python.cc | 47 ++++++------- src/lib/dns/python/rrset_python.h | 2 +- src/lib/dns/python/rrttl_python.cc | 34 +++++----- src/lib/dns/python/rrttl_python.h | 2 +- src/lib/dns/python/rrtype_python.cc | 2 +- 21 files changed, 157 insertions(+), 164 deletions(-) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index cab57be415..0ad31f06e1 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -124,7 +124,7 @@ EDNS_init(s_EDNS* self, PyObject* args) { if (PyArg_ParseTuple(args, "|b", &version)) { try { - self->edns = new EDNS(version); + self->cppobj = new EDNS(version); } catch (const isc::InvalidParameter& ex) { PyErr_SetString(po_InvalidParameter, ex.what()); return (-1); @@ -140,10 +140,10 @@ EDNS_init(s_EDNS* self, PyObject* args) { // in this context so that we can share the try-catch logic with // EDNS_createFromRR() (see below). uint8_t extended_rcode; - self->edns = createFromRR(*name->cppobj, *rrclass->cppobj, - *rrtype->cppobj, *rrttl->rrttl, - *rdata->rdata, extended_rcode); - return (self->edns != NULL ? 0 : -1); + self->cppobj = createFromRR(*name->cppobj, *rrclass->cppobj, + *rrtype->cppobj, *rrttl->cppobj, + *rdata->cppobj, extended_rcode); + return (self->cppobj != NULL ? 0 : -1); } PyErr_Clear(); @@ -154,15 +154,15 @@ EDNS_init(s_EDNS* self, PyObject* args) { void EDNS_destroy(s_EDNS* const self) { - delete self->edns; - self->edns = NULL; + delete self->cppobj; + self->cppobj = NULL; Py_TYPE(self)->tp_free(self); } PyObject* EDNS_toText(const s_EDNS* const self) { // Py_BuildValue makes python objects from native data - return (Py_BuildValue("s", self->edns->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } PyObject* @@ -184,7 +184,7 @@ EDNS_toWire(const s_EDNS* const self, PyObject* args) { PyObject* bytes_o = bytes; OutputBuffer buffer(0); - self->edns->toWire(buffer, extended_rcode); + self->cppobj->toWire(buffer, extended_rcode); PyObject* rd_bytes = PyBytes_FromStringAndSize( static_cast(buffer.getData()), buffer.getLength()); PyObject* result = PySequence_InPlaceConcat(bytes_o, rd_bytes); @@ -194,7 +194,7 @@ EDNS_toWire(const s_EDNS* const self, PyObject* args) { return (result); } else if (PyArg_ParseTuple(args, "O!b", &messagerenderer_type, &renderer, &extended_rcode)) { - const unsigned int n = self->edns->toWire(*renderer->messagerenderer, + const unsigned int n = self->cppobj->toWire(*renderer->cppobj, extended_rcode); return (Py_BuildValue("I", n)); @@ -206,12 +206,12 @@ EDNS_toWire(const s_EDNS* const self, PyObject* args) { PyObject* EDNS_getVersion(const s_EDNS* const self) { - return (Py_BuildValue("B", self->edns->getVersion())); + return (Py_BuildValue("B", self->cppobj->getVersion())); } PyObject* EDNS_getDNSSECAwareness(const s_EDNS* const self) { - if (self->edns->getDNSSECAwareness()) { + if (self->cppobj->getDNSSECAwareness()) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; @@ -224,13 +224,13 @@ EDNS_setDNSSECAwareness(s_EDNS* self, PyObject* args) { if (!PyArg_ParseTuple(args, "O!", &PyBool_Type, &b)) { return (NULL); } - self->edns->setDNSSECAwareness(b == Py_True); + self->cppobj->setDNSSECAwareness(b == Py_True); Py_RETURN_NONE; } PyObject* EDNS_getUDPSize(const s_EDNS* const self) { - return (Py_BuildValue("I", self->edns->getUDPSize())); + return (Py_BuildValue("I", self->cppobj->getUDPSize())); } PyObject* @@ -247,7 +247,7 @@ EDNS_setUDPSize(s_EDNS* self, PyObject* args) { "UDP size is not an unsigned 16-bit integer"); return (NULL); } - self->edns->setUDPSize(size); + self->cppobj->setUDPSize(size); Py_RETURN_NONE; } @@ -271,10 +271,10 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) { return (NULL); } - edns_obj->edns = createFromRR(*name->cppobj, *rrclass->cppobj, - *rrtype->cppobj, *rrttl->rrttl, - *rdata->rdata, extended_rcode); - if (edns_obj->edns != NULL) { + edns_obj->cppobj = createFromRR(*name->cppobj, *rrclass->cppobj, + *rrtype->cppobj, *rrttl->cppobj, + *rdata->cppobj, extended_rcode); + if (edns_obj->cppobj != NULL) { PyObject* extrcode_obj = Py_BuildValue("B", extended_rcode); return (Py_BuildValue("OO", edns_obj, extrcode_obj)); } diff --git a/src/lib/dns/python/edns_python.h b/src/lib/dns/python/edns_python.h index 4f90460e50..8ee2c646da 100644 --- a/src/lib/dns/python/edns_python.h +++ b/src/lib/dns/python/edns_python.h @@ -30,7 +30,7 @@ namespace python { // class s_EDNS : public PyObject { public: - EDNS* edns; + EDNS* cppobj; }; extern PyTypeObject edns_type; diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 214e254a38..f30f52460a 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -171,10 +171,10 @@ Message_init(s_Message* self, PyObject* args) { if (PyArg_ParseTuple(args, "i", &i)) { PyErr_Clear(); if (i == Message::PARSE) { - self->message = new Message(Message::PARSE); + self->cppobj = new Message(Message::PARSE); return (0); } else if (i == Message::RENDER) { - self->message = new Message(Message::RENDER); + self->cppobj = new Message(Message::RENDER); return (0); } else { PyErr_SetString(PyExc_TypeError, "Message mode must be Message.PARSE or Message.RENDER"); @@ -189,8 +189,8 @@ Message_init(s_Message* self, PyObject* args) { void Message_destroy(s_Message* self) { - delete self->message; - self->message = NULL; + delete self->cppobj; + self->cppobj = NULL; Py_TYPE(self)->tp_free(self); } @@ -204,7 +204,7 @@ Message_getHeaderFlag(s_Message* self, PyObject* args) { return (NULL); } - if (self->message->getHeaderFlag( + if (self->cppobj->getHeaderFlag( static_cast(messageflag))) { Py_RETURN_TRUE; } else { @@ -229,7 +229,7 @@ Message_setHeaderFlag(s_Message* self, PyObject* args) { } try { - self->message->setHeaderFlag( + self->cppobj->setHeaderFlag( static_cast(messageflag), on == Py_True); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { @@ -245,7 +245,7 @@ Message_setHeaderFlag(s_Message* self, PyObject* args) { PyObject* Message_getQid(s_Message* self) { - return (Py_BuildValue("I", self->message->getQid())); + return (Py_BuildValue("I", self->cppobj->getQid())); } PyObject* @@ -264,7 +264,7 @@ Message_setQid(s_Message* self, PyObject* args) { } try { - self->message->setQid(id); + self->cppobj->setQid(id); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -280,7 +280,7 @@ Message_getRcode(s_Message* self) { if (rcode != NULL) { rcode->cppobj = NULL; try { - rcode->cppobj = new Rcode(self->message->getRcode()); + rcode->cppobj = new Rcode(self->cppobj->getRcode()); } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); } catch (...) { @@ -302,7 +302,7 @@ Message_setRcode(s_Message* self, PyObject* args) { return (NULL); } try { - self->message->setRcode(*rcode->cppobj); + self->cppobj->setRcode(*rcode->cppobj); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -316,15 +316,15 @@ Message_getOpcode(s_Message* self) { opcode = static_cast(opcode_type.tp_alloc(&opcode_type, 0)); if (opcode != NULL) { - opcode->opcode = NULL; + opcode->cppobj = NULL; try { - opcode->opcode = new Opcode(self->message->getOpcode()); + opcode->cppobj = new Opcode(self->cppobj->getOpcode()); } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); } catch (...) { PyErr_SetString(po_IscException, "Unexpected exception"); } - if (opcode->opcode == NULL) { + if (opcode->cppobj == NULL) { Py_DECREF(opcode); return (NULL); } @@ -340,7 +340,7 @@ Message_setOpcode(s_Message* self, PyObject* args) { return (NULL); } try { - self->message->setOpcode(*opcode->opcode); + self->cppobj->setOpcode(*opcode->cppobj); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -352,7 +352,7 @@ PyObject* Message_getEDNS(s_Message* self) { s_EDNS* edns; EDNS* edns_body; - ConstEDNSPtr src = self->message->getEDNS(); + ConstEDNSPtr src = self->cppobj->getEDNS(); if (!src) { Py_RETURN_NONE; @@ -362,7 +362,7 @@ Message_getEDNS(s_Message* self) { } edns = static_cast(opcode_type.tp_alloc(&edns_type, 0)); if (edns != NULL) { - edns->edns = edns_body; + edns->cppobj = edns_body; } return (edns); @@ -375,7 +375,7 @@ Message_setEDNS(s_Message* self, PyObject* args) { return (NULL); } try { - self->message->setEDNS(EDNSPtr(new EDNS(*edns->edns))); + self->cppobj->setEDNS(EDNSPtr(new EDNS(*edns->cppobj))); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -386,7 +386,7 @@ Message_setEDNS(s_Message* self, PyObject* args) { PyObject* Message_getTSIGRecord(s_Message* self) { try { - const TSIGRecord* tsig_record = self->message->getTSIGRecord(); + const TSIGRecord* tsig_record = self->cppobj->getTSIGRecord(); if (tsig_record == NULL) { Py_RETURN_NONE; @@ -416,7 +416,7 @@ Message_getRRCount(s_Message* self, PyObject* args) { return (NULL); } try { - return (Py_BuildValue("I", self->message->getRRCount( + return (Py_BuildValue("I", self->cppobj->getRRCount( static_cast(section)))); } catch (const isc::OutOfRange& ex) { PyErr_SetString(PyExc_OverflowError, ex.what()); @@ -429,8 +429,8 @@ PyObject* Message_getQuestion(s_Message* self) { QuestionIterator qi, qi_end; try { - qi = self->message->beginQuestion(); - qi_end = self->message->endQuestion(); + qi = self->cppobj->beginQuestion(); + qi_end = self->cppobj->endQuestion(); } catch (const InvalidMessageSection& ex) { PyErr_SetString(po_InvalidMessageSection, ex.what()); return (NULL); @@ -453,7 +453,7 @@ Message_getQuestion(s_Message* self) { Py_DECREF(list); return (NULL); } - question->question = *qi; + question->cppobj = *qi; if (PyList_Append(list, question) == -1) { Py_DECREF(question); Py_DECREF(list); @@ -475,9 +475,9 @@ Message_getSection(s_Message* self, PyObject* args) { } RRsetIterator rrsi, rrsi_end; try { - rrsi = self->message->beginSection( + rrsi = self->cppobj->beginSection( static_cast(section)); - rrsi_end = self->message->endSection( + rrsi_end = self->cppobj->endSection( static_cast(section)); } catch (const isc::OutOfRange& ex) { PyErr_SetString(PyExc_OverflowError, ex.what()); @@ -503,7 +503,7 @@ Message_getSection(s_Message* self, PyObject* args) { Py_DECREF(list); return (NULL); } - rrset->rrset = *rrsi; + rrset->cppobj = *rrsi; if (PyList_Append(list, rrset) == -1) { Py_DECREF(rrset); Py_DECREF(list); @@ -529,7 +529,7 @@ Message_addQuestion(s_Message* self, PyObject* args) { return (NULL); } - self->message->addQuestion(question->question); + self->cppobj->addQuestion(question->cppobj); Py_RETURN_NONE; } @@ -545,8 +545,8 @@ Message_addRRset(s_Message* self, PyObject* args) { } try { - self->message->addRRset(static_cast(section), - rrset->rrset, sign == Py_True); + self->cppobj->addRRset(static_cast(section), + rrset->cppobj, sign == Py_True); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -567,10 +567,10 @@ Message_clear(s_Message* self, PyObject* args) { if (PyArg_ParseTuple(args, "i", &i)) { PyErr_Clear(); if (i == Message::PARSE) { - self->message->clear(Message::PARSE); + self->cppobj->clear(Message::PARSE); Py_RETURN_NONE; } else if (i == Message::RENDER) { - self->message->clear(Message::RENDER); + self->cppobj->clear(Message::RENDER); Py_RETURN_NONE; } else { PyErr_SetString(PyExc_TypeError, @@ -584,7 +584,7 @@ Message_clear(s_Message* self, PyObject* args) { PyObject* Message_makeResponse(s_Message* self) { - self->message->makeResponse(); + self->cppobj->makeResponse(); Py_RETURN_NONE; } @@ -592,7 +592,7 @@ PyObject* Message_toText(s_Message* self) { // Py_BuildValue makes python objects from native data try { - return (Py_BuildValue("s", self->message->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } catch (const InvalidMessageOperation& imo) { PyErr_Clear(); PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -620,9 +620,9 @@ Message_toWire(s_Message* self, PyObject* args) { &tsigcontext_type, &tsig_ctx)) { try { if (tsig_ctx == NULL) { - self->message->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->cppobj); } else { - self->message->toWire(*mr->messagerenderer, *tsig_ctx->cppobj); + self->cppobj->toWire(*mr->cppobj, *tsig_ctx->cppobj); } // If we return NULL it is seen as an error, so use this for // None returns @@ -663,7 +663,7 @@ Message_fromWire(s_Message* self, PyObject* args) { InputBuffer inbuf(b, len); try { - self->message->fromWire(inbuf); + self->cppobj->fromWire(inbuf); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); diff --git a/src/lib/dns/python/message_python.h b/src/lib/dns/python/message_python.h index 1a86528e52..4ab79f0cd5 100644 --- a/src/lib/dns/python/message_python.h +++ b/src/lib/dns/python/message_python.h @@ -35,7 +35,7 @@ extern PyObject* po_InvalidMessageUDPSize; class s_Message : public PyObject { public: - isc::dns::Message* message; + isc::dns::Message* cppobj; }; extern PyTypeObject message_type; diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc index f880a99576..a0c5788f4a 100644 --- a/src/lib/dns/python/messagerenderer_python.cc +++ b/src/lib/dns/python/messagerenderer_python.cc @@ -65,15 +65,15 @@ PyMethodDef MessageRenderer_methods[] = { int MessageRenderer_init(s_MessageRenderer* self) { self->outputbuffer = new OutputBuffer(4096); - self->messagerenderer = new MessageRenderer(*self->outputbuffer); + self->cppobj = new MessageRenderer(*self->outputbuffer); return (0); } void MessageRenderer_destroy(s_MessageRenderer* self) { - delete self->messagerenderer; + delete self->cppobj; delete self->outputbuffer; - self->messagerenderer = NULL; + self->cppobj = NULL; self->outputbuffer = NULL; Py_TYPE(self)->tp_free(self); } @@ -81,18 +81,18 @@ MessageRenderer_destroy(s_MessageRenderer* self) { PyObject* MessageRenderer_getData(s_MessageRenderer* self) { return (Py_BuildValue("y#", - self->messagerenderer->getData(), - self->messagerenderer->getLength())); + self->cppobj->getData(), + self->cppobj->getLength())); } PyObject* MessageRenderer_getLength(s_MessageRenderer* self) { - return (Py_BuildValue("I", self->messagerenderer->getLength())); + return (Py_BuildValue("I", self->cppobj->getLength())); } PyObject* MessageRenderer_isTruncated(s_MessageRenderer* self) { - if (self->messagerenderer->isTruncated()) { + if (self->cppobj->isTruncated()) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; @@ -101,17 +101,17 @@ MessageRenderer_isTruncated(s_MessageRenderer* self) { PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self) { - return (Py_BuildValue("I", self->messagerenderer->getLengthLimit())); + return (Py_BuildValue("I", self->cppobj->getLengthLimit())); } PyObject* MessageRenderer_getCompressMode(s_MessageRenderer* self) { - return (Py_BuildValue("I", self->messagerenderer->getCompressMode())); + return (Py_BuildValue("I", self->cppobj->getCompressMode())); } PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self) { - self->messagerenderer->setTruncated(); + self->cppobj->setTruncated(); Py_RETURN_NONE; } @@ -131,7 +131,7 @@ MessageRenderer_setLengthLimit(s_MessageRenderer* self, "MessageRenderer length limit out of range"); return (NULL); } - self->messagerenderer->setLengthLimit(lengthlimit); + self->cppobj->setLengthLimit(lengthlimit); Py_RETURN_NONE; } @@ -145,12 +145,12 @@ MessageRenderer_setCompressMode(s_MessageRenderer* self, } if (mode == MessageRenderer::CASE_INSENSITIVE) { - self->messagerenderer->setCompressMode(MessageRenderer::CASE_INSENSITIVE); + self->cppobj->setCompressMode(MessageRenderer::CASE_INSENSITIVE); // If we return NULL it is seen as an error, so use this for // None returns, it also applies to CASE_SENSITIVE. Py_RETURN_NONE; } else if (mode == MessageRenderer::CASE_SENSITIVE) { - self->messagerenderer->setCompressMode(MessageRenderer::CASE_SENSITIVE); + self->cppobj->setCompressMode(MessageRenderer::CASE_SENSITIVE); Py_RETURN_NONE; } else { PyErr_SetString(PyExc_TypeError, @@ -162,7 +162,7 @@ MessageRenderer_setCompressMode(s_MessageRenderer* self, PyObject* MessageRenderer_clear(s_MessageRenderer* self) { - self->messagerenderer->clear(); + self->cppobj->clear(); Py_RETURN_NONE; } } // end of unnamed namespace diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h index 18e0ef955b..22e20fd8b4 100644 --- a/src/lib/dns/python/messagerenderer_python.h +++ b/src/lib/dns/python/messagerenderer_python.h @@ -33,7 +33,7 @@ class s_MessageRenderer : public PyObject { public: s_MessageRenderer(); isc::util::OutputBuffer* outputbuffer; - MessageRenderer* messagerenderer; + MessageRenderer* cppobj; }; extern PyTypeObject messagerenderer_type; diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index 76b9d7294b..38c353dd0b 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -296,7 +296,7 @@ Name_toWire(s_Name* self, PyObject* args) { Py_DECREF(name_bytes); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->cppobj); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index ee4bce7dc1..eeae14a7d0 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -97,7 +97,7 @@ Opcode_init(s_Opcode* const self, PyObject* args) { uint8_t code = 0; if (PyArg_ParseTuple(args, "b", &code)) { try { - self->opcode = new Opcode(code); + self->cppobj = new Opcode(code); self->static_code = false; } catch (const isc::OutOfRange& ex) { PyErr_SetString(PyExc_OverflowError, ex.what()); @@ -118,22 +118,22 @@ Opcode_init(s_Opcode* const self, PyObject* args) { void Opcode_destroy(s_Opcode* const self) { // Depending on whether we created the rcode or are referring - // to a global static one, we do or do not delete self->opcode here + // to a global static one, we do or do not delete self->cppobj here if (!self->static_code) { - delete self->opcode; + delete self->cppobj; } - self->opcode = NULL; + self->cppobj = NULL; Py_TYPE(self)->tp_free(self); } PyObject* Opcode_getCode(const s_Opcode* const self) { - return (Py_BuildValue("I", self->opcode->getCode())); + return (Py_BuildValue("I", self->cppobj->getCode())); } PyObject* Opcode_toText(const s_Opcode* const self) { - return (Py_BuildValue("s", self->opcode->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } PyObject* @@ -148,7 +148,7 @@ PyObject* Opcode_createStatic(const Opcode& opcode) { s_Opcode* ret = PyObject_New(s_Opcode, &opcode_type); if (ret != NULL) { - ret->opcode = &opcode; + ret->cppobj = &opcode; ret->static_code = true; } return (ret); @@ -255,10 +255,10 @@ Opcode_richcmp(const s_Opcode* const self, const s_Opcode* const other, PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode"); return (NULL); case Py_EQ: - c = (*self->opcode == *other->opcode); + c = (*self->cppobj == *other->cppobj); break; case Py_NE: - c = (*self->opcode != *other->opcode); + c = (*self->cppobj != *other->cppobj); break; case Py_GT: PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode"); diff --git a/src/lib/dns/python/opcode_python.h b/src/lib/dns/python/opcode_python.h index 84adf7a7ca..76a0bab0bb 100644 --- a/src/lib/dns/python/opcode_python.h +++ b/src/lib/dns/python/opcode_python.h @@ -31,8 +31,8 @@ namespace python { class s_Opcode : public PyObject { public: - s_Opcode() : opcode(NULL), static_code(false) {} - const isc::dns::Opcode* opcode; + s_Opcode() : cppobj(NULL), static_code(false) {} + const isc::dns::Opcode* cppobj; bool static_code; }; diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index e87d13b55e..71be32c472 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -29,14 +29,6 @@ #include -#include - -#include - -#include -#include -#include - #include "pydnspp_common.h" /* Note that we do forward declarations of the initialization functions here, * and these are not defined in headers (since they are not to be used in any diff --git a/src/lib/dns/python/pydnspp_towire.h b/src/lib/dns/python/pydnspp_towire.h index 66362a0e1a..5b3c15be72 100644 --- a/src/lib/dns/python/pydnspp_towire.h +++ b/src/lib/dns/python/pydnspp_towire.h @@ -96,7 +96,7 @@ toWireWrapper(const PYSTRUCT* const self, PyObject* args) { s_MessageRenderer* renderer; if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &renderer)) { const unsigned int n = TOWIRECALLER(*self->cppobj)( - *renderer->messagerenderer); + *renderer->cppobj); return (Py_BuildValue("I", n)); } diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index 06aaf57c50..95da01a283 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -92,15 +92,15 @@ Question_init(s_Question* self, PyObject* args) { &rrclass_type, &rrclass, &rrtype_type, &rrtype )) { - self->question = QuestionPtr(new Question(*name->cppobj, - *rrclass->cppobj, - *rrtype->cppobj)); + self->cppobj = QuestionPtr(new Question(*name->cppobj, + *rrclass->cppobj, + *rrtype->cppobj)); return (0); } else if (PyArg_ParseTuple(args, "y#|I", &b, &len, &position)) { PyErr_Clear(); InputBuffer inbuf(b, len); inbuf.setPosition(position); - self->question = QuestionPtr(new Question(inbuf)); + self->cppobj = QuestionPtr(new Question(inbuf)); return (0); } } catch (const DNSMessageFORMERR& dmfe) { @@ -117,7 +117,7 @@ Question_init(s_Question* self, PyObject* args) { return (-1); } - self->question = QuestionPtr(); + self->cppobj = QuestionPtr(); PyErr_Clear(); PyErr_SetString(PyExc_TypeError, @@ -127,7 +127,7 @@ Question_init(s_Question* self, PyObject* args) { static void Question_destroy(s_Question* self) { - self->question.reset(); + self->cppobj.reset(); Py_TYPE(self)->tp_free(self); } @@ -138,7 +138,7 @@ Question_getName(s_Question* self) { // is this the best way to do this? name = static_cast(name_type.tp_alloc(&name_type, 0)); if (name != NULL) { - name->cppobj = new Name(self->question->getName()); + name->cppobj = new Name(self->cppobj->getName()); } return (name); @@ -150,7 +150,7 @@ Question_getType(s_Question* self) { rrtype = static_cast(rrtype_type.tp_alloc(&rrtype_type, 0)); if (rrtype != NULL) { - rrtype->cppobj = new RRType(self->question->getType()); + rrtype->cppobj = new RRType(self->cppobj->getType()); } return (rrtype); @@ -162,7 +162,7 @@ Question_getClass(s_Question* self) { rrclass = static_cast(rrclass_type.tp_alloc(&rrclass_type, 0)); if (rrclass != NULL) { - rrclass->cppobj = new RRClass(self->question->getClass()); + rrclass->cppobj = new RRClass(self->cppobj->getClass()); } return (rrclass); @@ -172,7 +172,7 @@ Question_getClass(s_Question* self) { static PyObject* Question_toText(s_Question* self) { // Py_BuildValue makes python objects from native data - return (Py_BuildValue("s", self->question->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } static PyObject* @@ -193,7 +193,7 @@ Question_toWire(s_Question* self, PyObject* args) { // Max length is Name::MAX_WIRE + rrclass (2) + rrtype (2) OutputBuffer buffer(Name::MAX_WIRE + 4); - self->question->toWire(buffer); + self->cppobj->toWire(buffer); PyObject* n = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); PyObject* result = PySequence_InPlaceConcat(bytes_o, n); @@ -202,7 +202,7 @@ Question_toWire(s_Question* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->question->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->cppobj); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h index b602f06c1e..ac43c3f707 100644 --- a/src/lib/dns/python/question_python.h +++ b/src/lib/dns/python/question_python.h @@ -32,7 +32,7 @@ extern PyObject* po_EmptyQuestion; class s_Question : public PyObject { public: - isc::dns::QuestionPtr question; + isc::dns::QuestionPtr cppobj; }; extern PyTypeObject question_type; diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index 99575a218e..9186be6de5 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -78,12 +78,12 @@ Rdata_init(s_Rdata* self, PyObject* args) { if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype, &rrclass_type, &rrclass, &s)) { - self->rdata = createRdata(*rrtype->cppobj, *rrclass->cppobj, s); + self->cppobj = createRdata(*rrtype->cppobj, *rrclass->cppobj, s); return (0); } else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype, &rrclass_type, &rrclass, &data, &len)) { InputBuffer input_buffer(data, len); - self->rdata = createRdata(*rrtype->cppobj, *rrclass->cppobj, + self->cppobj = createRdata(*rrtype->cppobj, *rrclass->cppobj, input_buffer, len); return (0); } @@ -95,14 +95,14 @@ void Rdata_destroy(s_Rdata* self) { // Clear the shared_ptr so that its reference count is zero // before we call tp_free() (there is no direct release()) - self->rdata.reset(); + self->cppobj.reset(); Py_TYPE(self)->tp_free(self); } PyObject* Rdata_toText(s_Rdata* self) { // Py_BuildValue makes python objects from native data - return (Py_BuildValue("s", self->rdata->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } PyObject* @@ -122,7 +122,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) { PyObject* bytes_o = bytes; OutputBuffer buffer(4); - self->rdata->toWire(buffer); + self->cppobj->toWire(buffer); PyObject* rd_bytes = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); PyObject* result = PySequence_InPlaceConcat(bytes_o, rd_bytes); // We need to release the object we temporarily created here @@ -130,7 +130,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) { Py_DECREF(rd_bytes); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->rdata->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->cppobj); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -153,24 +153,24 @@ RData_richcmp(s_Rdata* self, s_Rdata* other, int op) { switch (op) { case Py_LT: - c = self->rdata->compare(*other->rdata) < 0; + c = self->cppobj->compare(*other->cppobj) < 0; break; case Py_LE: - c = self->rdata->compare(*other->rdata) < 0 || - self->rdata->compare(*other->rdata) == 0; + c = self->cppobj->compare(*other->cppobj) < 0 || + self->cppobj->compare(*other->cppobj) == 0; break; case Py_EQ: - c = self->rdata->compare(*other->rdata) == 0; + c = self->cppobj->compare(*other->cppobj) == 0; break; case Py_NE: - c = self->rdata->compare(*other->rdata) != 0; + c = self->cppobj->compare(*other->cppobj) != 0; break; case Py_GT: - c = self->rdata->compare(*other->rdata) > 0; + c = self->cppobj->compare(*other->cppobj) > 0; break; case Py_GE: - c = self->rdata->compare(*other->rdata) > 0 || - self->rdata->compare(*other->rdata) == 0; + c = self->cppobj->compare(*other->cppobj) > 0 || + self->cppobj->compare(*other->cppobj) == 0; break; default: PyErr_SetString(PyExc_IndexError, diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h index 3f09d13a5c..2324384057 100644 --- a/src/lib/dns/python/rdata_python.h +++ b/src/lib/dns/python/rdata_python.h @@ -32,7 +32,7 @@ extern PyObject* po_EmptyRdata; class s_Rdata : public PyObject { public: - isc::dns::rdata::RdataPtr rdata; + isc::dns::rdata::RdataPtr cppobj; }; extern PyTypeObject rdata_type; diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index 005541c286..0e2df03cbb 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -169,7 +169,7 @@ RRClass_toWire(s_RRClass* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->cppobj); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index 197fbdf221..4e9ca4367a 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -103,12 +103,12 @@ RRset_init(s_RRset* self, PyObject* args) { &rrtype_type, &rrtype, &rrttl_type, &rrttl )) { - self->rrset = RRsetPtr(new RRset(*name->cppobj, *rrclass->cppobj, - *rrtype->cppobj, *rrttl->rrttl)); + self->cppobj = RRsetPtr(new RRset(*name->cppobj, *rrclass->cppobj, + *rrtype->cppobj, *rrttl->cppobj)); return (0); } - self->rrset = RRsetPtr(); + self->cppobj = RRsetPtr(); return (-1); } @@ -116,13 +116,13 @@ void RRset_destroy(s_RRset* self) { // Clear the shared_ptr so that its reference count is zero // before we call tp_free() (there is no direct release()) - self->rrset.reset(); + self->cppobj.reset(); Py_TYPE(self)->tp_free(self); } PyObject* RRset_getRdataCount(s_RRset* self) { - return (Py_BuildValue("I", self->rrset->getRdataCount())); + return (Py_BuildValue("I", self->cppobj->getRdataCount())); } PyObject* @@ -132,7 +132,7 @@ RRset_getName(s_RRset* self) { // is this the best way to do this? name = static_cast(name_type.tp_alloc(&name_type, 0)); if (name != NULL) { - name->cppobj = new Name(self->rrset->getName()); + name->cppobj = new Name(self->cppobj->getName()); if (name->cppobj == NULL) { Py_DECREF(name); @@ -149,7 +149,7 @@ RRset_getClass(s_RRset* self) { rrclass = static_cast(rrclass_type.tp_alloc(&rrclass_type, 0)); if (rrclass != NULL) { - rrclass->cppobj = new RRClass(self->rrset->getClass()); + rrclass->cppobj = new RRClass(self->cppobj->getClass()); if (rrclass->cppobj == NULL) { Py_DECREF(rrclass); @@ -166,7 +166,7 @@ RRset_getType(s_RRset* self) { rrtype = static_cast(rrtype_type.tp_alloc(&rrtype_type, 0)); if (rrtype != NULL) { - rrtype->cppobj = new RRType(self->rrset->getType()); + rrtype->cppobj = new RRType(self->cppobj->getType()); if (rrtype->cppobj == NULL) { Py_DECREF(rrtype); @@ -183,8 +183,8 @@ RRset_getTTL(s_RRset* self) { rrttl = static_cast(rrttl_type.tp_alloc(&rrttl_type, 0)); if (rrttl != NULL) { - rrttl->rrttl = new RRTTL(self->rrset->getTTL()); - if (rrttl->rrttl == NULL) + rrttl->cppobj = new RRTTL(self->cppobj->getTTL()); + if (rrttl->cppobj == NULL) { Py_DECREF(rrttl); return (NULL); @@ -200,7 +200,7 @@ RRset_setName(s_RRset* self, PyObject* args) { if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) { return (NULL); } - self->rrset->setName(*name->cppobj); + self->cppobj->setName(*name->cppobj); Py_RETURN_NONE; } @@ -210,14 +210,14 @@ RRset_setTTL(s_RRset* self, PyObject* args) { if (!PyArg_ParseTuple(args, "O!", &rrttl_type, &rrttl)) { return (NULL); } - self->rrset->setTTL(*rrttl->rrttl); + self->cppobj->setTTL(*rrttl->cppobj); Py_RETURN_NONE; } PyObject* RRset_toText(s_RRset* self) { try { - return (Py_BuildValue("s", self->rrset->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } catch (const EmptyRRset& ers) { PyErr_SetString(po_EmptyRRset, ers.what()); return (NULL); @@ -242,7 +242,7 @@ RRset_toWire(s_RRset* self, PyObject* args) { PyObject* bytes_o = bytes; OutputBuffer buffer(4096); - self->rrset->toWire(buffer); + self->cppobj->toWire(buffer); PyObject* n = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); PyObject* result = PySequence_InPlaceConcat(bytes_o, n); // We need to release the object we temporarily created here @@ -250,7 +250,7 @@ RRset_toWire(s_RRset* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->rrset->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->cppobj); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -273,7 +273,7 @@ RRset_addRdata(s_RRset* self, PyObject* args) { return (NULL); } try { - self->rrset->addRdata(*rdata->rdata); + self->cppobj->addRdata(*rdata->cppobj); Py_RETURN_NONE; } catch (const std::bad_cast&) { PyErr_Clear(); @@ -287,7 +287,7 @@ PyObject* RRset_getRdata(s_RRset* self) { PyObject* list = PyList_New(0); - RdataIteratorPtr it = self->rrset->getRdataIterator(); + RdataIteratorPtr it = self->cppobj->getRdataIterator(); for (; !it->isLast(); it->next()) { s_Rdata *rds = static_cast(rdata_type.tp_alloc(&rdata_type, 0)); @@ -296,7 +296,8 @@ RRset_getRdata(s_RRset* self) { // make this a bit weird, so we create a new one with // the data available const rdata::Rdata *rd = &it->getCurrent(); - rds->rdata = createRdata(self->rrset->getType(), self->rrset->getClass(), *rd); + rds->cppobj = createRdata(self->cppobj->getType(), + self->cppobj->getClass(), *rd); PyList_Append(list, rds); } else { return (NULL); @@ -308,7 +309,7 @@ RRset_getRdata(s_RRset* self) { PyObject* RRset_removeRRsig(s_RRset* self) { - self->rrset->removeRRsig(); + self->cppobj->removeRRsig(); Py_RETURN_NONE; } @@ -432,18 +433,18 @@ createRRsetObject(const RRset& source) { // RRsets are noncopyable, so as a workaround we recreate a new one // and copy over all content try { - py_rrset->rrset = isc::dns::RRsetPtr( + py_rrset->cppobj = isc::dns::RRsetPtr( new isc::dns::RRset(source.getName(), source.getClass(), source.getType(), source.getTTL())); isc::dns::RdataIteratorPtr rdata_it(source.getRdataIterator()); for (rdata_it->first(); !rdata_it->isLast(); rdata_it->next()) { - py_rrset->rrset->addRdata(rdata_it->getCurrent()); + py_rrset->cppobj->addRdata(rdata_it->getCurrent()); } isc::dns::RRsetPtr sigs = source.getRRsig(); if (sigs) { - py_rrset->rrset->addRRsig(sigs); + py_rrset->cppobj->addRRsig(sigs); } return (py_rrset); } catch (const std::bad_alloc&) { @@ -460,7 +461,7 @@ PyRRset_Check(PyObject* obj) { RRset& PyRRset_ToRRset(PyObject* rrset_obj) { s_RRset* rrset = static_cast(rrset_obj); - return (*rrset->rrset); + return (*rrset->cppobj); } diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h index 6c083a35ad..1f8130c072 100644 --- a/src/lib/dns/python/rrset_python.h +++ b/src/lib/dns/python/rrset_python.h @@ -41,7 +41,7 @@ extern PyObject* po_EmptyRRset; // rrset is destroyed later class s_RRset : public PyObject { public: - isc::dns::RRsetPtr rrset; + isc::dns::RRsetPtr cppobj; }; extern PyTypeObject rrset_type; diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index 4720bfbdda..bdefe567b7 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -76,7 +76,7 @@ RRTTL_init(s_RRTTL* self, PyObject* args) { // (the way to do exceptions is to set PyErr and return -1) try { if (PyArg_ParseTuple(args, "s", &s)) { - self->rrttl = new RRTTL(s); + self->cppobj = new RRTTL(s); return (0); } else if (PyArg_ParseTuple(args, "L", &i)) { PyErr_Clear(); @@ -84,7 +84,7 @@ RRTTL_init(s_RRTTL* self, PyObject* args) { PyErr_SetString(PyExc_ValueError, "RR TTL number out of range"); return (-1); } - self->rrttl = new RRTTL(i); + self->cppobj = new RRTTL(i); return (0); } else if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { @@ -95,7 +95,7 @@ RRTTL_init(s_RRTTL* self, PyObject* args) { return (result); } InputBuffer ib(&data[0], size); - self->rrttl = new RRTTL(ib); + self->cppobj = new RRTTL(ib); PyErr_Clear(); return (0); } @@ -121,15 +121,15 @@ RRTTL_init(s_RRTTL* self, PyObject* args) { void RRTTL_destroy(s_RRTTL* self) { - delete self->rrttl; - self->rrttl = NULL; + delete self->cppobj; + self->cppobj = NULL; Py_TYPE(self)->tp_free(self); } PyObject* RRTTL_toText(s_RRTTL* self) { // Py_BuildValue makes python objects from native data - return (Py_BuildValue("s", self->rrttl->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } PyObject* @@ -149,7 +149,7 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) { PyObject* bytes_o = bytes; OutputBuffer buffer(4); - self->rrttl->toWire(buffer); + self->cppobj->toWire(buffer); PyObject* n = PyBytes_FromStringAndSize(static_cast(buffer.getData()), buffer.getLength()); PyObject* result = PySequence_InPlaceConcat(bytes_o, n); @@ -158,7 +158,7 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->rrttl->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->cppobj); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -171,7 +171,7 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) { PyObject* RRTTL_getValue(s_RRTTL* self) { - return (Py_BuildValue("I", self->rrttl->getValue())); + return (Py_BuildValue("I", self->cppobj->getValue())); } PyObject* @@ -186,24 +186,24 @@ RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op) { switch (op) { case Py_LT: - c = *self->rrttl < *other->rrttl; + c = *self->cppobj < *other->cppobj; break; case Py_LE: - c = *self->rrttl < *other->rrttl || - *self->rrttl == *other->rrttl; + c = *self->cppobj < *other->cppobj || + *self->cppobj == *other->cppobj; break; case Py_EQ: - c = *self->rrttl == *other->rrttl; + c = *self->cppobj == *other->cppobj; break; case Py_NE: - c = *self->rrttl != *other->rrttl; + c = *self->cppobj != *other->cppobj; break; case Py_GT: - c = *other->rrttl < *self->rrttl; + c = *other->cppobj < *self->cppobj; break; case Py_GE: - c = *other->rrttl < *self->rrttl || - *self->rrttl == *other->rrttl; + c = *other->cppobj < *self->cppobj || + *self->cppobj == *other->cppobj; break; } if (c) diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h index 36f7a99dff..03811c94e4 100644 --- a/src/lib/dns/python/rrttl_python.h +++ b/src/lib/dns/python/rrttl_python.h @@ -34,7 +34,7 @@ extern PyObject* po_IncompleteRRTTL; // The s_* Class simply covers one instantiation of the object class s_RRTTL : public PyObject { public: - isc::dns::RRTTL* rrttl; + isc::dns::RRTTL* cppobj; }; diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index fecd89c579..83ece188a2 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -199,7 +199,7 @@ RRType_toWire(s_RRType* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->messagerenderer); + self->cppobj->toWire(*mr->cppobj); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; From 18083458382473b414a3fc7f57623d2241f487ef Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 16 Sep 2011 01:35:40 +0200 Subject: [PATCH 713/974] [1245] move all s_ decls to inside the .cc Added a lot of conversion functions for that --- src/lib/dns/python/edns_python.cc | 72 ++++++++--- src/lib/dns/python/edns_python.h | 37 ++++-- src/lib/dns/python/message_python.cc | 119 ++++++------------ src/lib/dns/python/message_python.h | 5 - src/lib/dns/python/messagerenderer_python.cc | 51 +++++++- src/lib/dns/python/messagerenderer_python.h | 39 ++++-- src/lib/dns/python/name_python.cc | 23 +++- src/lib/dns/python/name_python.h | 16 +-- src/lib/dns/python/opcode_python.cc | 30 +++++ src/lib/dns/python/opcode_python.h | 40 ++++-- src/lib/dns/python/pydnspp_towire.h | 4 +- src/lib/dns/python/question_python.cc | 72 +++++------ src/lib/dns/python/question_python.h | 32 ++++- src/lib/dns/python/rcode_python.cc | 46 ++++++- src/lib/dns/python/rcode_python.h | 47 ++++--- src/lib/dns/python/rdata_python.cc | 47 +++++-- src/lib/dns/python/rdata_python.h | 32 ++++- src/lib/dns/python/rrclass_python.cc | 16 ++- src/lib/dns/python/rrclass_python.h | 13 +- src/lib/dns/python/rrset_python.cc | 125 +++++++------------ src/lib/dns/python/rrset_python.h | 21 ++-- src/lib/dns/python/rrttl_python.cc | 33 ++++- src/lib/dns/python/rrttl_python.h | 34 +++-- src/lib/dns/python/rrtype_python.cc | 14 ++- src/lib/dns/python/rrtype_python.h | 9 +- src/lib/dns/python/tsig_python.cc | 45 ++++--- src/lib/dns/python/tsig_python.h | 26 ++-- src/lib/dns/python/tsig_rdata_python.cc | 24 +++- src/lib/dns/python/tsig_rdata_python.h | 25 ++-- src/lib/dns/python/tsigerror_python.cc | 22 ++-- src/lib/dns/python/tsigerror_python.h | 8 +- src/lib/dns/python/tsigkey_python.cc | 61 ++++++--- src/lib/dns/python/tsigkey_python.h | 49 ++++++-- src/lib/dns/python/tsigrecord_python.cc | 29 ++++- src/lib/dns/python/tsigrecord_python.h | 25 ++-- 35 files changed, 823 insertions(+), 468 deletions(-) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index 0ad31f06e1..b1073d8a51 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include "edns_python.h" #include "name_python.h" @@ -30,12 +31,20 @@ #include "pydnspp_common.h" using namespace isc::dns; -using namespace isc::util; using namespace isc::dns::rdata; using namespace isc::dns::python; +using namespace isc::util; +using namespace isc::util::python; namespace { +class s_EDNS : public PyObject { +public: + EDNS* cppobj; +}; + +typedef CPPPyObjectContainer EDNSContainer; + // General creation and destruction int EDNS_init(s_EDNS* self, PyObject* args); void EDNS_destroy(s_EDNS* self); @@ -116,11 +125,11 @@ createFromRR(const Name& name, const RRClass& rrclass, const RRType& rrtype, int EDNS_init(s_EDNS* self, PyObject* args) { uint8_t version = EDNS::SUPPORTED_VERSION; - const s_Name* name; - const s_RRClass* rrclass; - const s_RRType* rrtype; - const s_RRTTL* rrttl; - const s_Rdata* rdata; + const PyObject* name; + const PyObject* rrclass; + const PyObject* rrtype; + const PyObject* rrttl; + const PyObject* rdata; if (PyArg_ParseTuple(args, "|b", &version)) { try { @@ -140,9 +149,11 @@ EDNS_init(s_EDNS* self, PyObject* args) { // in this context so that we can share the try-catch logic with // EDNS_createFromRR() (see below). uint8_t extended_rcode; - self->cppobj = createFromRR(*name->cppobj, *rrclass->cppobj, - *rrtype->cppobj, *rrttl->cppobj, - *rdata->cppobj, extended_rcode); + self->cppobj = createFromRR(PyName_ToName(name), + PyRRClass_ToRRClass(rrclass), + PyRRType_ToRRType(rrtype), + PyRRTTL_ToRRTTL(rrttl), + PyRdata_ToRdata(rdata), extended_rcode); return (self->cppobj != NULL ? 0 : -1); } @@ -177,7 +188,7 @@ PyObject* EDNS_toWire(const s_EDNS* const self, PyObject* args) { PyObject* bytes; uint8_t extended_rcode; - s_MessageRenderer* renderer; + PyObject* renderer; if (PyArg_ParseTuple(args, "Ob", &bytes, &extended_rcode) && PySequence_Check(bytes)) { @@ -194,8 +205,8 @@ EDNS_toWire(const s_EDNS* const self, PyObject* args) { return (result); } else if (PyArg_ParseTuple(args, "O!b", &messagerenderer_type, &renderer, &extended_rcode)) { - const unsigned int n = self->cppobj->toWire(*renderer->cppobj, - extended_rcode); + const unsigned int n = self->cppobj->toWire( + PyMessageRenderer_ToMessageRenderer(renderer), extended_rcode); return (Py_BuildValue("I", n)); } @@ -253,11 +264,11 @@ EDNS_setUDPSize(s_EDNS* self, PyObject* args) { PyObject* EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) { - const s_Name* name; - const s_RRClass* rrclass; - const s_RRType* rrtype; - const s_RRTTL* rrttl; - const s_Rdata* rdata; + const PyObject* name; + const PyObject* rrclass; + const PyObject* rrtype; + const PyObject* rrttl; + const PyObject* rdata; s_EDNS* edns_obj = NULL; assert(null_self == NULL); @@ -271,9 +282,12 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) { return (NULL); } - edns_obj->cppobj = createFromRR(*name->cppobj, *rrclass->cppobj, - *rrtype->cppobj, *rrttl->cppobj, - *rdata->cppobj, extended_rcode); + edns_obj->cppobj = createFromRR(PyName_ToName(name), + PyRRClass_ToRRClass(rrclass), + PyRRType_ToRRType(rrtype), + PyRRTTL_ToRRTTL(rrttl), + PyRdata_ToRdata(rdata), + extended_rcode); if (edns_obj->cppobj != NULL) { PyObject* extrcode_obj = Py_BuildValue("B", extended_rcode); return (Py_BuildValue("OO", edns_obj, extrcode_obj)); @@ -369,6 +383,24 @@ initModulePart_EDNS(PyObject* mod) { } } // end namespace internal +PyObject* +createEDNSObject(const EDNS& source) { + EDNSContainer container = PyObject_New(s_EDNS, &edns_type); + container.set(new EDNS(source)); + return (container.release()); +} + +bool +PyEDNS_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &edns_type)); +} + +const EDNS& +PyEDNS_ToEDNS(const PyObject* edns_obj) { + const s_EDNS* edns = static_cast(edns_obj); + return (*edns->cppobj); +} + } // end namespace python } // end namespace dns } // end namespace isc diff --git a/src/lib/dns/python/edns_python.h b/src/lib/dns/python/edns_python.h index 8ee2c646da..a6d3c38bb2 100644 --- a/src/lib/dns/python/edns_python.h +++ b/src/lib/dns/python/edns_python.h @@ -23,18 +23,35 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// -class s_EDNS : public PyObject { -public: - EDNS* cppobj; -}; - extern PyTypeObject edns_type; +/// This is a simple shortcut to create a python EDNS object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called within a try block +/// followed by necessary setup for python exception. +PyObject* createEDNSObject(const EDNS& source); + +/// \brief Checks if the given python object is a EDNS object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type EDNS, false otherwise +bool PyEDNS_Check(PyObject* obj); + +/// \brief Returns a reference to the EDNS object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type EDNS; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyEDNS_Check() +/// +/// \note This is not a copy; if the EDNS is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param edns_obj The edns object to convert +const EDNS& PyEDNS_ToEDNS(const PyObject* edns_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index f30f52460a..0ac1aabadf 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -40,6 +40,10 @@ using namespace isc::dns::python; using namespace isc::util; namespace { +class s_Message : public PyObject { +public: + isc::dns::Message* cppobj; +}; int Message_init(s_Message* self, PyObject* args); void Message_destroy(s_Message* self); @@ -274,35 +278,25 @@ Message_setQid(s_Message* self, PyObject* args) { PyObject* Message_getRcode(s_Message* self) { - s_Rcode* rcode; - - rcode = static_cast(rcode_type.tp_alloc(&rcode_type, 0)); - if (rcode != NULL) { - rcode->cppobj = NULL; - try { - rcode->cppobj = new Rcode(self->cppobj->getRcode()); - } catch (const InvalidMessageOperation& imo) { - PyErr_SetString(po_InvalidMessageOperation, imo.what()); - } catch (...) { - PyErr_SetString(po_IscException, "Unexpected exception"); - } - if (rcode->cppobj == NULL) { - Py_DECREF(rcode); - return (NULL); - } + try { + return (createRcodeObject(self->cppobj->getRcode())); + } catch (const InvalidMessageOperation& imo) { + PyErr_SetString(po_InvalidMessageOperation, imo.what()); + return (NULL); + } catch (...) { + PyErr_SetString(po_IscException, "Unexpected exception"); + return (NULL); } - - return (rcode); } PyObject* Message_setRcode(s_Message* self, PyObject* args) { - s_Rcode* rcode; + PyObject* rcode; if (!PyArg_ParseTuple(args, "O!", &rcode_type, &rcode)) { return (NULL); } try { - self->cppobj->setRcode(*rcode->cppobj); + self->cppobj->setRcode(PyRcode_ToRcode(rcode)); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -312,35 +306,25 @@ Message_setRcode(s_Message* self, PyObject* args) { PyObject* Message_getOpcode(s_Message* self) { - s_Opcode* opcode; - - opcode = static_cast(opcode_type.tp_alloc(&opcode_type, 0)); - if (opcode != NULL) { - opcode->cppobj = NULL; - try { - opcode->cppobj = new Opcode(self->cppobj->getOpcode()); - } catch (const InvalidMessageOperation& imo) { - PyErr_SetString(po_InvalidMessageOperation, imo.what()); - } catch (...) { - PyErr_SetString(po_IscException, "Unexpected exception"); - } - if (opcode->cppobj == NULL) { - Py_DECREF(opcode); - return (NULL); - } + try { + return (createOpcodeObject(self->cppobj->getOpcode())); + } catch (const InvalidMessageOperation& imo) { + PyErr_SetString(po_InvalidMessageOperation, imo.what()); + return (NULL); + } catch (...) { + PyErr_SetString(po_IscException, "Unexpected exception"); + return (NULL); } - - return (opcode); } PyObject* Message_setOpcode(s_Message* self, PyObject* args) { - s_Opcode* opcode; + PyObject* opcode; if (!PyArg_ParseTuple(args, "O!", &opcode_type, &opcode)) { return (NULL); } try { - self->cppobj->setOpcode(*opcode->cppobj); + self->cppobj->setOpcode(PyOpcode_ToOpcode(opcode)); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -350,32 +334,21 @@ Message_setOpcode(s_Message* self, PyObject* args) { PyObject* Message_getEDNS(s_Message* self) { - s_EDNS* edns; - EDNS* edns_body; ConstEDNSPtr src = self->cppobj->getEDNS(); - if (!src) { Py_RETURN_NONE; } - if ((edns_body = new(nothrow) EDNS(*src)) == NULL) { - return (PyErr_NoMemory()); - } - edns = static_cast(opcode_type.tp_alloc(&edns_type, 0)); - if (edns != NULL) { - edns->cppobj = edns_body; - } - - return (edns); + return (createEDNSObject(*src)); } PyObject* Message_setEDNS(s_Message* self, PyObject* args) { - s_EDNS* edns; + PyObject* edns; if (!PyArg_ParseTuple(args, "O!", &edns_type, &edns)) { return (NULL); } try { - self->cppobj->setEDNS(EDNSPtr(new EDNS(*edns->cppobj))); + self->cppobj->setEDNS(EDNSPtr(new EDNS(PyEDNS_ToEDNS(edns)))); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -446,20 +419,10 @@ Message_getQuestion(s_Message* self) { } for (; qi != qi_end; ++qi) { - s_Question *question = static_cast( - question_type.tp_alloc(&question_type, 0)); - if (question == NULL) { - Py_DECREF(question); + if (PyList_Append(list, createQuestionObject(**qi)) == -1) { Py_DECREF(list); return (NULL); } - question->cppobj = *qi; - if (PyList_Append(list, question) == -1) { - Py_DECREF(question); - Py_DECREF(list); - return (NULL); - } - Py_DECREF(question); } return (list); } @@ -496,14 +459,7 @@ Message_getSection(s_Message* self, PyObject* args) { return (NULL); } for (; rrsi != rrsi_end; ++rrsi) { - s_RRset *rrset = static_cast( - rrset_type.tp_alloc(&rrset_type, 0)); - if (rrset == NULL) { - Py_DECREF(rrset); - Py_DECREF(list); - return (NULL); - } - rrset->cppobj = *rrsi; + PyObject* rrset = createRRsetObject(**rrsi); if (PyList_Append(list, rrset) == -1) { Py_DECREF(rrset); Py_DECREF(list); @@ -523,13 +479,13 @@ Message_getSection(s_Message* self, PyObject* args) { //static PyObject* Message_addQuestion(s_Message* self, PyObject* args); PyObject* Message_addQuestion(s_Message* self, PyObject* args) { - s_Question *question; + PyObject* question; if (!PyArg_ParseTuple(args, "O!", &question_type, &question)) { return (NULL); } - self->cppobj->addQuestion(question->cppobj); + self->cppobj->addQuestion(PyQuestion_ToQuestion(question)); Py_RETURN_NONE; } @@ -538,7 +494,7 @@ PyObject* Message_addRRset(s_Message* self, PyObject* args) { PyObject *sign = Py_False; int section; - s_RRset* rrset; + PyObject* rrset; if (!PyArg_ParseTuple(args, "iO!|O!", §ion, &rrset_type, &rrset, &PyBool_Type, &sign)) { return (NULL); @@ -546,7 +502,7 @@ Message_addRRset(s_Message* self, PyObject* args) { try { self->cppobj->addRRset(static_cast(section), - rrset->cppobj, sign == Py_True); + PyRRset_ToRRsetPtr(rrset), sign == Py_True); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -613,16 +569,17 @@ Message_str(PyObject* self) { PyObject* Message_toWire(s_Message* self, PyObject* args) { - s_MessageRenderer* mr; - s_TSIGContext* tsig_ctx = NULL; + PyObject* mr; + PyObject* tsig_ctx = NULL; if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr, &tsigcontext_type, &tsig_ctx)) { try { if (tsig_ctx == NULL) { - self->cppobj->toWire(*mr->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr)); } else { - self->cppobj->toWire(*mr->cppobj, *tsig_ctx->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr), + PyTSIGContext_ToTSIGContext(tsig_ctx)); } // If we return NULL it is seen as an error, so use this for // None returns diff --git a/src/lib/dns/python/message_python.h b/src/lib/dns/python/message_python.h index 4ab79f0cd5..be1adb9d1b 100644 --- a/src/lib/dns/python/message_python.h +++ b/src/lib/dns/python/message_python.h @@ -33,11 +33,6 @@ extern PyObject* po_InvalidMessageSection; extern PyObject* po_InvalidMessageOperation; extern PyObject* po_InvalidMessageUDPSize; -class s_Message : public PyObject { -public: - isc::dns::Message* cppobj; -}; - extern PyTypeObject message_type; } // namespace python diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc index a0c5788f4a..2c475d8f2e 100644 --- a/src/lib/dns/python/messagerenderer_python.cc +++ b/src/lib/dns/python/messagerenderer_python.cc @@ -17,6 +17,7 @@ #include #include +#include #include "pydnspp_common.h" #include "messagerenderer_python.h" @@ -24,8 +25,21 @@ using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; +using namespace isc::util::python; namespace { +// The s_* Class simply covers one instantiation of the object. +// +// since we don't use *Buffer in the python version (but work with +// the already existing bytearray type where we use these custom buffers +// in C++, we need to keep track of one here. +class s_MessageRenderer : public PyObject { +public: + s_MessageRenderer(); + isc::util::OutputBuffer* outputbuffer; + MessageRenderer* cppobj; +}; + int MessageRenderer_init(s_MessageRenderer* self); void MessageRenderer_destroy(s_MessageRenderer* self); @@ -81,7 +95,7 @@ MessageRenderer_destroy(s_MessageRenderer* self) { PyObject* MessageRenderer_getData(s_MessageRenderer* self) { return (Py_BuildValue("y#", - self->cppobj->getData(), + self->cppobj->getData(), self->cppobj->getLength())); } @@ -244,6 +258,41 @@ bool initModulePart_MessageRenderer(PyObject* mod) { } } // end namespace internal +PyObject* +createMessageRendererObject(const MessageRenderer& source) { + // should we copy? can we? + // copy the existing buffer into a new one, then create a new renderer with + // that buffer + s_MessageRenderer* mr = static_cast( + messagerenderer_type.tp_alloc(&messagerenderer_type, 0)); + if (mr == NULL) { + isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " + "probably due to short memory"); + } + try { + mr->outputbuffer = new OutputBuffer(4096); + mr->outputbuffer->writeData(source.getData(), source.getLength()); + mr->cppobj = new MessageRenderer(*mr->outputbuffer); + + return (mr); + } catch (const std::bad_alloc&) { + isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " + "probably due to short memory"); + } +} + +bool +PyMessageRenderer_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &messagerenderer_type)); +} + +MessageRenderer& +PyMessageRenderer_ToMessageRenderer(PyObject* messagerenderer_obj) { + s_MessageRenderer* messagerenderer = static_cast(messagerenderer_obj); + return (*messagerenderer->cppobj); +} + + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h index 22e20fd8b4..2f51853717 100644 --- a/src/lib/dns/python/messagerenderer_python.h +++ b/src/lib/dns/python/messagerenderer_python.h @@ -24,20 +24,35 @@ namespace isc { namespace dns { namespace python { -// The s_* Class simply covers one instantiation of the object. -// -// since we don't use *Buffer in the python version (but work with -// the already existing bytearray type where we use these custom buffers -// in C++, we need to keep track of one here. -class s_MessageRenderer : public PyObject { -public: - s_MessageRenderer(); - isc::util::OutputBuffer* outputbuffer; - MessageRenderer* cppobj; -}; - extern PyTypeObject messagerenderer_type; +/// This is a simple shortcut to create a python MessageRenderer object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called within a try block +/// followed by necessary setup for python exception. +PyObject* createMessageRendererObject(const MessageRenderer& source); + +/// \brief Checks if the given python object is a MessageRenderer object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type MessageRenderer, false otherwise +bool PyMessageRenderer_Check(PyObject* obj); + +/// \brief Returns a reference to the MessageRenderer object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type MessageRenderer; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyMessageRenderer_Check() +/// +/// \note This is not a copy; if the MessageRenderer is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param messagerenderer_obj The messagerenderer object to convert +MessageRenderer& PyMessageRenderer_ToMessageRenderer(PyObject* messagerenderer_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index 38c353dd0b..40c7673e2c 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -31,6 +31,19 @@ using namespace isc::util; using namespace isc::util::python; namespace { +// The s_* Class simply covers one instantiation of the object. +class s_NameComparisonResult : public PyObject { +public: + s_NameComparisonResult() : cppobj(NULL) {} + NameComparisonResult* cppobj; +}; + +class s_Name : public PyObject { +public: + s_Name() : cppobj(NULL), position(0) {} + Name* cppobj; + size_t position; +}; int NameComparisonResult_init(s_NameComparisonResult*, PyObject*); void NameComparisonResult_destroy(s_NameComparisonResult* self); @@ -282,7 +295,7 @@ Name_str(PyObject* self) { PyObject* Name_toWire(s_Name* self, PyObject* args) { PyObject* bytes; - s_MessageRenderer* mr; + PyObject* mr; if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; @@ -296,7 +309,7 @@ Name_toWire(s_Name* self, PyObject* args) { Py_DECREF(name_bytes); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr)); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -719,9 +732,9 @@ PyName_Check(PyObject* obj) { return (PyObject_TypeCheck(obj, &name_type)); } -Name& -PyName_ToName(PyObject* name_obj) { - s_Name* name = static_cast(name_obj); +const Name& +PyName_ToName(const PyObject* name_obj) { + const s_Name* name = static_cast(name_obj); return (*name->cppobj); } diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h index 4a5df69ae2..3667c35094 100644 --- a/src/lib/dns/python/name_python.h +++ b/src/lib/dns/python/name_python.h @@ -44,20 +44,6 @@ extern PyObject* po_DNSMessageFORMERR; // extern PyObject* po_NameRelation; -// The s_* Class simply covers one instantiation of the object. -class s_NameComparisonResult : public PyObject { -public: - s_NameComparisonResult() : cppobj(NULL) {} - NameComparisonResult* cppobj; -}; - -class s_Name : public PyObject { -public: - s_Name() : cppobj(NULL), position(0) {} - Name* cppobj; - size_t position; -}; - extern PyTypeObject name_comparison_result_type; extern PyTypeObject name_type; @@ -86,7 +72,7 @@ bool PyName_Check(PyObject* obj); /// may be destroyed, the caller must copy it itself. /// /// \param name_obj The name object to convert -Name& PyName_ToName(PyObject* name_obj); +const Name& PyName_ToName(const PyObject* name_obj); } // namespace python } // namespace dns diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index eeae14a7d0..154745793b 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -15,6 +15,7 @@ #include #include +#include #include "pydnspp_common.h" #include "opcode_python.h" @@ -23,9 +24,19 @@ using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; +using namespace isc::util::python; namespace { +class s_Opcode : public PyObject { +public: + s_Opcode() : cppobj(NULL), static_code(false) {} + const isc::dns::Opcode* cppobj; + bool static_code; +}; + +typedef CPPPyObjectContainer OpcodeContainer; + int Opcode_init(s_Opcode* const self, PyObject* args); void Opcode_destroy(s_Opcode* const self); @@ -383,6 +394,25 @@ initModulePart_Opcode(PyObject* mod) { } } // end namespace internal + +PyObject* +createOpcodeObject(const Opcode& source) { + OpcodeContainer container = PyObject_New(s_Opcode, &opcode_type); + container.set(new Opcode(source)); + return (container.release()); +} + +bool +PyOpcode_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &opcode_type)); +} + +const Opcode& +PyOpcode_ToOpcode(const PyObject* opcode_obj) { + const s_Opcode* opcode = static_cast(opcode_obj); + return (*opcode->cppobj); +} + } // end python namespace } // end dns namespace } // end isc namespace diff --git a/src/lib/dns/python/opcode_python.h b/src/lib/dns/python/opcode_python.h index 76a0bab0bb..04ee7e7b02 100644 --- a/src/lib/dns/python/opcode_python.h +++ b/src/lib/dns/python/opcode_python.h @@ -23,21 +23,35 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// - -class s_Opcode : public PyObject { -public: - s_Opcode() : cppobj(NULL), static_code(false) {} - const isc::dns::Opcode* cppobj; - bool static_code; -}; - extern PyTypeObject opcode_type; +/// This is a simple shortcut to create a python Opcode object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called within a try block +/// followed by necessary setup for python exception. +PyObject* createOpcodeObject(const Opcode& source); + +/// \brief Checks if the given python object is a Opcode object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type Opcode, false otherwise +bool PyOpcode_Check(PyObject* obj); + +/// \brief Returns a reference to the Opcode object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type Opcode; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyOpcode_Check() +/// +/// \note This is not a copy; if the Opcode is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param opcode_obj The opcode object to convert +const Opcode& PyOpcode_ToOpcode(const PyObject* opcode_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/pydnspp_towire.h b/src/lib/dns/python/pydnspp_towire.h index 5b3c15be72..e987a29814 100644 --- a/src/lib/dns/python/pydnspp_towire.h +++ b/src/lib/dns/python/pydnspp_towire.h @@ -93,10 +93,10 @@ toWireWrapper(const PYSTRUCT* const self, PyObject* args) { } // To MessageRenderer version - s_MessageRenderer* renderer; + PyObject* renderer; if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &renderer)) { const unsigned int n = TOWIRECALLER(*self->cppobj)( - *renderer->cppobj); + PyMessageRenderer_ToMessageRenderer(renderer)); return (Py_BuildValue("I", n)); } diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index 95da01a283..953a0c3672 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -32,6 +32,10 @@ using namespace isc::util; using namespace isc; namespace { +class s_Question : public PyObject { +public: + isc::dns::QuestionPtr cppobj; +}; static int Question_init(s_Question* self, PyObject* args); static void Question_destroy(s_Question* self); @@ -79,9 +83,9 @@ Question_init(s_Question* self, PyObject* args) { // that if we try several like here. Otherwise the *next* python // call will suddenly appear to throw an exception. // (the way to do exceptions is to set PyErr and return -1) - s_Name* name; - s_RRClass* rrclass; - s_RRType* rrtype; + PyObject* name; + PyObject* rrclass; + PyObject* rrtype; const char* b; Py_ssize_t len; @@ -89,12 +93,12 @@ Question_init(s_Question* self, PyObject* args) { try { if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &name, - &rrclass_type, &rrclass, - &rrtype_type, &rrtype + &rrclass_type, &rrclass, + &rrtype_type, &rrtype )) { - self->cppobj = QuestionPtr(new Question(*name->cppobj, - *rrclass->cppobj, - *rrtype->cppobj)); + self->cppobj = QuestionPtr(new Question(PyName_ToName(name), + PyRRClass_ToRRClass(rrclass), + PyRRType_ToRRType(rrtype))); return (0); } else if (PyArg_ParseTuple(args, "y#|I", &b, &len, &position)) { PyErr_Clear(); @@ -133,42 +137,19 @@ Question_destroy(s_Question* self) { static PyObject* Question_getName(s_Question* self) { - s_Name* name; - - // is this the best way to do this? - name = static_cast(name_type.tp_alloc(&name_type, 0)); - if (name != NULL) { - name->cppobj = new Name(self->cppobj->getName()); - } - - return (name); + return (createNameObject(self->cppobj->getName())); } static PyObject* Question_getType(s_Question* self) { - s_RRType* rrtype; - - rrtype = static_cast(rrtype_type.tp_alloc(&rrtype_type, 0)); - if (rrtype != NULL) { - rrtype->cppobj = new RRType(self->cppobj->getType()); - } - - return (rrtype); + return (createRRTypeObject(self->cppobj->getType())); } static PyObject* Question_getClass(s_Question* self) { - s_RRClass* rrclass; - - rrclass = static_cast(rrclass_type.tp_alloc(&rrclass_type, 0)); - if (rrclass != NULL) { - rrclass->cppobj = new RRClass(self->cppobj->getClass()); - } - - return (rrclass); + return (createRRClassObject(self->cppobj->getClass())); } - static PyObject* Question_toText(s_Question* self) { // Py_BuildValue makes python objects from native data @@ -186,7 +167,7 @@ Question_str(PyObject* self) { static PyObject* Question_toWire(s_Question* self, PyObject* args) { PyObject* bytes; - s_MessageRenderer* mr; + PyObject* mr; if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; @@ -202,7 +183,7 @@ Question_toWire(s_Question* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr)); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -292,6 +273,25 @@ initModulePart_Question(PyObject* mod) { } } // end namespace internal +PyObject* +createQuestionObject(const Question& source) { + s_Question* question = + static_cast(question_type.tp_alloc(&question_type, 0)); + question->cppobj = QuestionPtr(new Question(source)); + return (question); +} + +bool +PyQuestion_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &question_type)); +} + +const Question& +PyQuestion_ToQuestion(const PyObject* question_obj) { + const s_Question* question = static_cast(question_obj); + return (*question->cppobj); +} + } // end python namespace } // end dns namespace } // end isc namespace diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h index ac43c3f707..1971b4b3d7 100644 --- a/src/lib/dns/python/question_python.h +++ b/src/lib/dns/python/question_python.h @@ -30,13 +30,35 @@ namespace python { // extern PyObject* po_EmptyQuestion; -class s_Question : public PyObject { -public: - isc::dns::QuestionPtr cppobj; -}; - extern PyTypeObject question_type; +/// This is a simple shortcut to create a python Question object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called within a try block +/// followed by necessary setup for python exception. +PyObject* createQuestionObject(const Question& source); + +/// \brief Checks if the given python object is a Question object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type Question, false otherwise +bool PyQuestion_Check(PyObject* obj); + +/// \brief Returns a reference to the Question object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type Question; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyQuestion_Check() +/// +/// \note This is not a copy; if the Question is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param question_obj The question object to convert +const Question& PyQuestion_ToQuestion(const PyObject* question_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc index 5cb7e8bfac..2e51003fc4 100644 --- a/src/lib/dns/python/rcode_python.cc +++ b/src/lib/dns/python/rcode_python.cc @@ -15,19 +15,39 @@ #include #include - #include +#include #include "pydnspp_common.h" #include "rcode_python.h" using namespace isc::dns; using namespace isc::dns::python; - -// Trivial constructor. -s_Rcode::s_Rcode() : cppobj(NULL), static_code(false) {} +using namespace isc::util::python; namespace { +// The s_* Class simply covers one instantiation of the object. +// +// We added a helper variable static_code here +// Since we can create Rcodes dynamically with Rcode(int), but also +// use the static globals (Rcode::NOERROR() etc), we use this +// variable to see if the code came from one of the latter, in which +// case Rcode_destroy should not free it (the other option is to +// allocate new Rcodes for every use of the static ones, but this +// seems more efficient). +// +// Follow-up note: we don't have to use the proxy function in the python lib; +// we can just define class specific constants directly (see TSIGError). +// We should make this cleanup later. +class s_Rcode : public PyObject { +public: + s_Rcode() : cppobj(NULL), static_code(false) {}; + const Rcode* cppobj; + bool static_code; +}; + +typedef CPPPyObjectContainer RcodeContainer; + int Rcode_init(s_Rcode* const self, PyObject* args); void Rcode_destroy(s_Rcode* const self); @@ -416,6 +436,24 @@ initModulePart_Rcode(PyObject* mod) { } } // end namespace internal +PyObject* +createRcodeObject(const Rcode& source) { + RcodeContainer container = PyObject_New(s_Rcode, &rcode_type); + container.set(new Rcode(source)); + return (container.release()); +} + +bool +PyRcode_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &rcode_type)); +} + +const Rcode& +PyRcode_ToRcode(const PyObject* rcode_obj) { + const s_Rcode* rcode = static_cast(rcode_obj); + return (*rcode->cppobj); +} + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h index 68abd1c389..89969c7299 100644 --- a/src/lib/dns/python/rcode_python.h +++ b/src/lib/dns/python/rcode_python.h @@ -23,28 +23,35 @@ namespace isc { namespace dns { namespace python { -// The s_* Class simply covers one instantiation of the object. -// -// We added a helper variable static_code here -// Since we can create Rcodes dynamically with Rcode(int), but also -// use the static globals (Rcode::NOERROR() etc), we use this -// variable to see if the code came from one of the latter, in which -// case Rcode_destroy should not free it (the other option is to -// allocate new Rcodes for every use of the static ones, but this -// seems more efficient). -// -// Follow-up note: we don't have to use the proxy function in the python lib; -// we can just define class specific constants directly (see TSIGError). -// We should make this cleanup later. -class s_Rcode : public PyObject { -public: - s_Rcode(); - const Rcode* cppobj; - bool static_code; -}; - extern PyTypeObject rcode_type; +/// This is a simple shortcut to create a python Rcode object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called within a try block +/// followed by necessary setup for python exception. +PyObject* createRcodeObject(const Rcode& source); + +/// \brief Checks if the given python object is a Rcode object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type Rcode, false otherwise +bool PyRcode_Check(PyObject* obj); + +/// \brief Returns a reference to the Rcode object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type Rcode; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyRcode_Check() +/// +/// \note This is not a copy; if the Rcode is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param rcode_obj The rcode object to convert +const Rcode& PyRcode_ToRcode(const PyObject* rcode_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index 9186be6de5..3167fcae70 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include "rdata_python.h" #include "rrtype_python.h" @@ -26,9 +27,16 @@ using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; +using namespace isc::util::python; using namespace isc::dns::rdata; namespace { +class s_Rdata : public PyObject { +public: + isc::dns::rdata::ConstRdataPtr cppobj; +}; + +typedef CPPPyObjectContainer RdataContainer; // // We declare the functions here, the definitions are below @@ -68,8 +76,8 @@ PyMethodDef Rdata_methods[] = { int Rdata_init(s_Rdata* self, PyObject* args) { - s_RRType* rrtype; - s_RRClass* rrclass; + PyObject* rrtype; + PyObject* rrclass; const char* s; const char* data; Py_ssize_t len; @@ -78,13 +86,15 @@ Rdata_init(s_Rdata* self, PyObject* args) { if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype, &rrclass_type, &rrclass, &s)) { - self->cppobj = createRdata(*rrtype->cppobj, *rrclass->cppobj, s); + self->cppobj = createRdata(PyRRType_ToRRType(rrtype), + PyRRClass_ToRRClass(rrclass), s); return (0); } else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype, &rrclass_type, &rrclass, &data, &len)) { InputBuffer input_buffer(data, len); - self->cppobj = createRdata(*rrtype->cppobj, *rrclass->cppobj, - input_buffer, len); + self->cppobj = createRdata(PyRRType_ToRRType(rrtype), + PyRRClass_ToRRClass(rrclass), + input_buffer, len); return (0); } @@ -116,7 +126,7 @@ Rdata_str(PyObject* self) { PyObject* Rdata_toWire(s_Rdata* self, PyObject* args) { PyObject* bytes; - s_MessageRenderer* mr; + PyObject* mr; if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; @@ -130,7 +140,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) { Py_DECREF(rd_bytes); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr)); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -282,6 +292,29 @@ initModulePart_Rdata(PyObject* mod) { } } // end namespace internal +PyObject* +createRdataObject(ConstRdataPtr source) { + s_Rdata* py_rdata = + static_cast(rdata_type.tp_alloc(&rdata_type, 0)); + if (py_rdata == NULL) { + isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " + "probably due to short memory"); + } + py_rdata->cppobj = source; + return (py_rdata); +} + +bool +PyRdata_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &rdata_type)); +} + +const Rdata& +PyRdata_ToRdata(const PyObject* rdata_obj) { + const s_Rdata* rdata = static_cast(rdata_obj); + return (*rdata->cppobj); +} + } // end python namespace } // end dns namespace } // end isc namespace diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h index 2324384057..8db68216d0 100644 --- a/src/lib/dns/python/rdata_python.h +++ b/src/lib/dns/python/rdata_python.h @@ -30,13 +30,35 @@ namespace python { // extern PyObject* po_EmptyRdata; -class s_Rdata : public PyObject { -public: - isc::dns::rdata::RdataPtr cppobj; -}; - extern PyTypeObject rdata_type; +/// This is a simple shortcut to create a python Rdata object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called within a try block +/// followed by necessary setup for python exception. +PyObject* createRdataObject(isc::dns::rdata::ConstRdataPtr source); + +/// \brief Checks if the given python object is a Rdata object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type Rdata, false otherwise +bool PyRdata_Check(PyObject* obj); + +/// \brief Returns a reference to the Rdata object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type Rdata; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyRdata_Check() +/// +/// \note This is not a copy; if the Rdata is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param rdata_obj The rdata object to convert +const isc::dns::rdata::Rdata& PyRdata_ToRdata(const PyObject* rdata_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index 0e2df03cbb..87ba32eb03 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -28,6 +28,12 @@ using namespace isc::dns::python; using namespace isc::util; using namespace isc::util::python; namespace { +// The s_* Class simply covers one instantiation of the object +class s_RRClass : public PyObject { +public: + s_RRClass() : cppobj(NULL) {}; + RRClass* cppobj; +}; // // We declare the functions here, the definitions are below @@ -155,7 +161,7 @@ RRClass_str(PyObject* self) { PyObject* RRClass_toWire(s_RRClass* self, PyObject* args) { PyObject* bytes; - s_MessageRenderer* mr; + PyObject* mr; if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; @@ -169,7 +175,7 @@ RRClass_toWire(s_RRClass* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr)); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -369,9 +375,9 @@ PyRRClass_Check(PyObject* obj) { return (PyObject_TypeCheck(obj, &rrclass_type)); } -RRClass& -PyRRClass_ToRRClassPtr(PyObject* rrclass_obj) { - s_RRClass* rrclass = static_cast(rrclass_obj); +const RRClass& +PyRRClass_ToRRClass(const PyObject* rrclass_obj) { + const s_RRClass* rrclass = static_cast(rrclass_obj); return (*rrclass->cppobj); } diff --git a/src/lib/dns/python/rrclass_python.h b/src/lib/dns/python/rrclass_python.h index b0c6289d90..f11b817ef9 100644 --- a/src/lib/dns/python/rrclass_python.h +++ b/src/lib/dns/python/rrclass_python.h @@ -26,17 +26,6 @@ namespace python { extern PyObject* po_InvalidRRClass; extern PyObject* po_IncompleteRRClass; -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// -// The s_* Class simply covers one instantiation of the object -class s_RRClass : public PyObject { -public: - RRClass* cppobj; -}; - extern PyTypeObject rrclass_type; /// This is a simple shortcut to create a python RRClass object (in the @@ -64,7 +53,7 @@ bool PyRRClass_Check(PyObject* obj); /// may be destroyed, the caller must copy it itself. /// /// \param rrclass_obj The rrclass object to convert -RRClass& PyRRClass_ToRRClass(PyObject* rrclass_obj); +const RRClass& PyRRClass_ToRRClass(const PyObject* rrclass_obj); } // namespace python diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index 4e9ca4367a..2c339204c9 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -36,6 +36,19 @@ using namespace isc::util::python; namespace { +// The s_* Class simply coverst one instantiation of the object + +// Using a shared_ptr here should not really be necessary (PyObject +// is already reference-counted), however internally on the cpp side, +// not doing so might result in problems, since we can't copy construct +// rdata field, adding them to rrsets results in a problem when the +// rrset is destroyed later +class s_RRset : public PyObject { +public: + isc::dns::RRsetPtr cppobj; +}; + + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer RRsetContainer; @@ -93,18 +106,20 @@ PyMethodDef RRset_methods[] = { int RRset_init(s_RRset* self, PyObject* args) { - s_Name* name; - s_RRClass* rrclass; - s_RRType* rrtype; - s_RRTTL* rrttl; + PyObject* name; + PyObject* rrclass; + PyObject* rrtype; + PyObject* rrttl; if (PyArg_ParseTuple(args, "O!O!O!O!", &name_type, &name, &rrclass_type, &rrclass, &rrtype_type, &rrtype, &rrttl_type, &rrttl )) { - self->cppobj = RRsetPtr(new RRset(*name->cppobj, *rrclass->cppobj, - *rrtype->cppobj, *rrttl->cppobj)); + self->cppobj = RRsetPtr(new RRset(PyName_ToName(name), + PyRRClass_ToRRClass(rrclass), + PyRRType_ToRRType(rrtype), + PyRRTTL_ToRRTTL(rrttl))); return (0); } @@ -127,90 +142,41 @@ RRset_getRdataCount(s_RRset* self) { PyObject* RRset_getName(s_RRset* self) { - s_Name* name; - - // is this the best way to do this? - name = static_cast(name_type.tp_alloc(&name_type, 0)); - if (name != NULL) { - name->cppobj = new Name(self->cppobj->getName()); - if (name->cppobj == NULL) - { - Py_DECREF(name); - return (NULL); - } - } - - return (name); + return (createNameObject(self->cppobj->getName())); } PyObject* RRset_getClass(s_RRset* self) { - s_RRClass* rrclass; - - rrclass = static_cast(rrclass_type.tp_alloc(&rrclass_type, 0)); - if (rrclass != NULL) { - rrclass->cppobj = new RRClass(self->cppobj->getClass()); - if (rrclass->cppobj == NULL) - { - Py_DECREF(rrclass); - return (NULL); - } - } - - return (rrclass); + return (createRRClassObject(self->cppobj->getClass())); } PyObject* RRset_getType(s_RRset* self) { - s_RRType* rrtype; - - rrtype = static_cast(rrtype_type.tp_alloc(&rrtype_type, 0)); - if (rrtype != NULL) { - rrtype->cppobj = new RRType(self->cppobj->getType()); - if (rrtype->cppobj == NULL) - { - Py_DECREF(rrtype); - return (NULL); - } - } - - return (rrtype); + return (createRRTypeObject(self->cppobj->getType())); } PyObject* RRset_getTTL(s_RRset* self) { - s_RRTTL* rrttl; - - rrttl = static_cast(rrttl_type.tp_alloc(&rrttl_type, 0)); - if (rrttl != NULL) { - rrttl->cppobj = new RRTTL(self->cppobj->getTTL()); - if (rrttl->cppobj == NULL) - { - Py_DECREF(rrttl); - return (NULL); - } - } - - return (rrttl); + return (createRRTTLObject(self->cppobj->getTTL())); } PyObject* RRset_setName(s_RRset* self, PyObject* args) { - s_Name* name; + PyObject* name; if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) { return (NULL); } - self->cppobj->setName(*name->cppobj); + self->cppobj->setName(PyName_ToName(name)); Py_RETURN_NONE; } PyObject* RRset_setTTL(s_RRset* self, PyObject* args) { - s_RRTTL* rrttl; + PyObject* rrttl; if (!PyArg_ParseTuple(args, "O!", &rrttl_type, &rrttl)) { return (NULL); } - self->cppobj->setTTL(*rrttl->cppobj); + self->cppobj->setTTL(PyRRTTL_ToRRTTL(rrttl)); Py_RETURN_NONE; } @@ -235,7 +201,7 @@ RRset_str(PyObject* self) { PyObject* RRset_toWire(s_RRset* self, PyObject* args) { PyObject* bytes; - s_MessageRenderer* mr; + PyObject* mr; try { if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { @@ -250,7 +216,7 @@ RRset_toWire(s_RRset* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr)); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -268,12 +234,12 @@ RRset_toWire(s_RRset* self, PyObject* args) { PyObject* RRset_addRdata(s_RRset* self, PyObject* args) { - s_Rdata* rdata; + PyObject* rdata; if (!PyArg_ParseTuple(args, "O!", &rdata_type, &rdata)) { return (NULL); } try { - self->cppobj->addRdata(*rdata->cppobj); + self->cppobj->addRdata(PyRdata_ToRdata(rdata)); Py_RETURN_NONE; } catch (const std::bad_cast&) { PyErr_Clear(); @@ -290,18 +256,10 @@ RRset_getRdata(s_RRset* self) { RdataIteratorPtr it = self->cppobj->getRdataIterator(); for (; !it->isLast(); it->next()) { - s_Rdata *rds = static_cast(rdata_type.tp_alloc(&rdata_type, 0)); - if (rds != NULL) { - // hmz them iterators/shared_ptrs and private constructors - // make this a bit weird, so we create a new one with - // the data available - const rdata::Rdata *rd = &it->getCurrent(); - rds->cppobj = createRdata(self->cppobj->getType(), - self->cppobj->getClass(), *rd); - PyList_Append(list, rds); - } else { - return (NULL); - } + const rdata::Rdata *rd = &it->getCurrent(); + PyList_Append(list, + createRdataObject(createRdata(self->cppobj->getType(), + self->cppobj->getClass(), *rd))); } return (list); @@ -423,8 +381,8 @@ initModulePart_RRset(PyObject* mod) { PyObject* createRRsetObject(const RRset& source) { - isc::dns::python::s_RRset* py_rrset = static_cast( - isc::dns::python::rrset_type.tp_alloc(&isc::dns::python::rrset_type, 0)); + s_RRset* py_rrset = + static_cast(rrset_type.tp_alloc(&rrset_type, 0)); if (py_rrset == NULL) { isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " "probably due to short memory"); @@ -464,6 +422,11 @@ PyRRset_ToRRset(PyObject* rrset_obj) { return (*rrset->cppobj); } +RRsetPtr +PyRRset_ToRRsetPtr(PyObject* rrset_obj) { + s_RRset* rrset = static_cast(rrset_obj); + return (rrset->cppobj); +} } // end python namespace diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h index 1f8130c072..04d305f359 100644 --- a/src/lib/dns/python/rrset_python.h +++ b/src/lib/dns/python/rrset_python.h @@ -32,18 +32,6 @@ namespace python { // extern PyObject* po_EmptyRRset; -// The s_* Class simply coverst one instantiation of the object - -// Using a shared_ptr here should not really be necessary (PyObject -// is already reference-counted), however internally on the cpp side, -// not doing so might result in problems, since we can't copy construct -// rdata field, adding them to rrsets results in a problem when the -// rrset is destroyed later -class s_RRset : public PyObject { -public: - isc::dns::RRsetPtr cppobj; -}; - extern PyTypeObject rrset_type; /// This is a simple shortcut to create a python RRset object (in the @@ -73,6 +61,15 @@ bool PyRRset_Check(PyObject* obj); /// \param rrset_obj The rrset object to convert RRset& PyRRset_ToRRset(PyObject* rrset_obj); +/// \brief Returns the shared_ptr of the RRset object contained within the +/// given Python object. +/// +/// \note The given object MUST be of type RRset; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyRRset_Check() +/// +/// \param rrset_obj The rrset object to convert +RRsetPtr PyRRset_ToRRsetPtr(PyObject* rrset_obj); + } // namespace python } // namespace dns diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index bdefe567b7..bea1d179d9 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "rrttl_python.h" #include "pydnspp_common.h" @@ -27,11 +28,17 @@ using namespace std; using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; +using namespace isc::util::python; namespace { +// The s_* Class simply covers one instantiation of the object +class s_RRTTL : public PyObject { +public: + s_RRTTL() : cppobj(NULL) {}; + isc::dns::RRTTL* cppobj; +}; -int RRTTL_init(s_RRTTL* self, PyObject* args); -void RRTTL_destroy(s_RRTTL* self); +typedef CPPPyObjectContainer RRTTLContainer; PyObject* RRTTL_toText(s_RRTTL* self); // This is a second version of toText, we need one where the argument @@ -143,7 +150,7 @@ RRTTL_str(PyObject* self) { PyObject* RRTTL_toWire(s_RRTTL* self, PyObject* args) { PyObject* bytes; - s_MessageRenderer* mr; + PyObject* mr; if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; @@ -158,7 +165,7 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr)); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -308,6 +315,24 @@ initModulePart_RRTTL(PyObject* mod) { } } // end namespace internal +PyObject* +createRRTTLObject(const RRTTL& source) { + RRTTLContainer container = PyObject_New(s_RRTTL, &rrttl_type); + container.set(new RRTTL(source)); + return (container.release()); +} + +bool +PyRRTTL_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &rrttl_type)); +} + +const RRTTL& +PyRRTTL_ToRRTTL(const PyObject* rrttl_obj) { + const s_RRTTL* rrttl = static_cast(rrttl_obj); + return (*rrttl->cppobj); +} + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h index 03811c94e4..ba0f5f5b7b 100644 --- a/src/lib/dns/python/rrttl_python.h +++ b/src/lib/dns/python/rrttl_python.h @@ -31,15 +31,35 @@ namespace python { extern PyObject* po_InvalidRRTTL; extern PyObject* po_IncompleteRRTTL; -// The s_* Class simply covers one instantiation of the object -class s_RRTTL : public PyObject { -public: - isc::dns::RRTTL* cppobj; -}; - - extern PyTypeObject rrttl_type; +/// This is a simple shortcut to create a python RRTTL object (in the +/// form of a pointer to PyObject) with minimal exception safety. +/// On success, it returns a valid pointer to PyObject with a reference +/// counter of 1; if something goes wrong it throws an exception (it never +/// returns a NULL pointer). +/// This function is expected to be called within a try block +/// followed by necessary setup for python exception. +PyObject* createRRTTLObject(const RRTTL& source); + +/// \brief Checks if the given python object is a RRTTL object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type RRTTL, false otherwise +bool PyRRTTL_Check(PyObject* obj); + +/// \brief Returns a reference to the RRTTL object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type RRTTL; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyRRTTL_Check() +/// +/// \note This is not a copy; if the RRTTL is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param rrttl_obj The rrttl object to convert +const RRTTL& PyRRTTL_ToRRTTL(const PyObject* rrttl_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index 83ece188a2..6b6fcc8202 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -30,6 +30,11 @@ using namespace isc::util; using namespace isc::util::python; namespace { +// The s_* Class simply covers one instantiation of the object +class s_RRType : public PyObject { +public: + const RRType* cppobj; +}; // General creation and destruction int RRType_init(s_RRType* self, PyObject* args); @@ -185,7 +190,7 @@ RRType_str(PyObject* self) { PyObject* RRType_toWire(s_RRType* self, PyObject* args) { PyObject* bytes; - s_MessageRenderer* mr; + PyObject* mr; if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) { PyObject* bytes_o = bytes; @@ -199,7 +204,7 @@ RRType_toWire(s_RRType* self, PyObject* args) { Py_DECREF(n); return (result); } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { - self->cppobj->toWire(*mr->cppobj); + self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr)); // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -458,15 +463,14 @@ createRRTypeObject(const RRType& source) { return (container.release()); } - bool PyRRType_Check(PyObject* obj) { return (PyObject_TypeCheck(obj, &rrtype_type)); } const RRType& -PyRRType_ToRRType(PyObject* rrtype_obj) { - s_RRType* rrtype = static_cast(rrtype_obj); +PyRRType_ToRRType(const PyObject* rrtype_obj) { + const s_RRType* rrtype = static_cast(rrtype_obj); return (*rrtype->cppobj); } diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h index 6aa4ffda68..c251d9ef5f 100644 --- a/src/lib/dns/python/rrtype_python.h +++ b/src/lib/dns/python/rrtype_python.h @@ -31,13 +31,6 @@ namespace python { extern PyObject* po_InvalidRRType; extern PyObject* po_IncompleteRRType; -// The s_* Class simply covers one instantiation of the object -class s_RRType : public PyObject { -public: - const RRType* cppobj; -}; - - extern PyTypeObject rrtype_type; /// This is a simple shortcut to create a python RRType object (in the @@ -65,7 +58,7 @@ bool PyRRType_Check(PyObject* obj); /// may be destroyed, the caller must copy it itself. /// /// \param rrtype_obj The rrtype object to convert -const RRType& PyRRType_ToRRType(PyObject* rrtype_obj); +const RRType& PyRRType_ToRRType(const PyObject* rrtype_obj); } // namespace python diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc index 678cb17f33..9cc596e91b 100644 --- a/src/lib/dns/python/tsig_python.cc +++ b/src/lib/dns/python/tsig_python.cc @@ -48,12 +48,14 @@ using namespace isc::dns::python; // // TSIGContext // - -// Trivial constructor. -s_TSIGContext::s_TSIGContext() : cppobj(NULL) { -} - namespace { +// The s_* Class simply covers one instantiation of the object +class s_TSIGContext : public PyObject { +public: + s_TSIGContext() : cppobj(NULL) {}; + TSIGContext* cppobj; +}; + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer TSIGContextContainer; @@ -101,23 +103,23 @@ int TSIGContext_init(s_TSIGContext* self, PyObject* args) { try { // "From key" constructor - const s_TSIGKey* tsigkey_obj; + const PyObject* tsigkey_obj; if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) { - self->cppobj = new TSIGContext(*tsigkey_obj->cppobj); + self->cppobj = new TSIGContext(PyTSIGKey_ToTSIGKey(tsigkey_obj)); return (0); } // "From key param + keyring" constructor PyErr_Clear(); - const s_Name* keyname_obj; - const s_Name* algname_obj; - const s_TSIGKeyRing* keyring_obj; + const PyObject* keyname_obj; + const PyObject* algname_obj; + const PyObject* keyring_obj; if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &keyname_obj, &name_type, &algname_obj, &tsigkeyring_type, &keyring_obj)) { - self->cppobj = new TSIGContext(*keyname_obj->cppobj, - *algname_obj->cppobj, - *keyring_obj->cppobj); + self->cppobj = new TSIGContext(PyName_ToName(keyname_obj), + PyName_ToName(algname_obj), + PyTSIGKeyRing_ToTSIGKeyRing(keyring_obj)); return (0); } } catch (const exception& ex) { @@ -205,13 +207,13 @@ PyObject* TSIGContext_verify(s_TSIGContext* self, PyObject* args) { const char* data; Py_ssize_t data_len; - s_TSIGRecord* py_record; + PyObject* py_record; PyObject* py_maybe_none; - TSIGRecord* record; + const TSIGRecord* record; if (PyArg_ParseTuple(args, "O!y#", &tsigrecord_type, &py_record, &data, &data_len)) { - record = py_record->cppobj; + record = &PyTSIGRecord_ToTSIGRecord(py_record); } else if (PyArg_ParseTuple(args, "Oy#", &py_maybe_none, &data, &data_len)) { record = NULL; @@ -362,6 +364,17 @@ initModulePart_TSIGContext(PyObject* mod) { } } // end namespace internal +bool +PyTSIGContext_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &tsigcontext_type)); +} + +TSIGContext& +PyTSIGContext_ToTSIGContext(PyObject* tsigcontext_obj) { + s_TSIGContext* tsigcontext = static_cast(tsigcontext_obj); + return (*tsigcontext->cppobj); +} + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h index fd0f6a42b9..9be92bb1da 100644 --- a/src/lib/dns/python/tsig_python.h +++ b/src/lib/dns/python/tsig_python.h @@ -23,18 +23,30 @@ namespace isc { namespace dns { namespace python { -// The s_* Class simply covers one instantiation of the object -class s_TSIGContext : public PyObject { -public: - s_TSIGContext(); - TSIGContext* cppobj; -}; - extern PyTypeObject tsigcontext_type; // Class specific exceptions extern PyObject* po_TSIGContextError; +/// \brief Checks if the given python object is a TSIGContext object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type TSIGContext, false otherwise +bool PyTSIGContext_Check(PyObject* obj); + +/// \brief Returns a reference to the TSIGContext object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type TSIGContext; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyTSIGContext_Check() +/// +/// \note This is not a copy; if the TSIGContext is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param tsigcontext_obj The tsigcontext object to convert +TSIGContext& PyTSIGContext_ToTSIGContext(PyObject* tsigcontext_obj); + + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc index 1bf8234b55..2a32be80d8 100644 --- a/src/lib/dns/python/tsig_rdata_python.cc +++ b/src/lib/dns/python/tsig_rdata_python.cc @@ -45,11 +45,15 @@ using namespace isc::dns::python; // TSIG RDATA // -// Trivial constructor. -s_TSIG::s_TSIG() : cppobj(NULL) { -} - namespace { + // The s_* Class simply covers one instantiation of the object +class s_TSIG : public PyObject { +public: + s_TSIG() : cppobj(NULL) {}; + const rdata::any::TSIG* cppobj; +}; + + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer TSIGContainer; @@ -367,6 +371,18 @@ createTSIGObject(const any::TSIG& source) { container.set(new any::TSIG(source)); return (container.release()); } + +bool +PyTSIG_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &tsig_type)); +} + +const any::TSIG& +PyTSIG_ToTSIG(const PyObject* tsig_obj) { + const s_TSIG* tsig = static_cast(tsig_obj); + return (*tsig->cppobj); +} + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsig_rdata_python.h b/src/lib/dns/python/tsig_rdata_python.h index 48a2d6a200..cb39317074 100644 --- a/src/lib/dns/python/tsig_rdata_python.h +++ b/src/lib/dns/python/tsig_rdata_python.h @@ -27,13 +27,6 @@ class TSIG; namespace python { -// The s_* Class simply covers one instantiation of the object -class s_TSIG : public PyObject { -public: - s_TSIG(); - const rdata::any::TSIG* cppobj; -}; - extern PyTypeObject tsig_type; /// This is A simple shortcut to create a python TSIG object (in the @@ -45,6 +38,24 @@ extern PyTypeObject tsig_type; /// followed by necessary setup for python exception. PyObject* createTSIGObject(const rdata::any::TSIG& source); +/// \brief Checks if the given python object is a TSIG object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type TSIG, false otherwise +bool PyTSIG_Check(PyObject* obj); + +/// \brief Returns a reference to the TSIG object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type TSIG; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyTSIG_Check() +/// +/// \note This is not a copy; if the TSIG is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param tsig_obj The tsig object to convert +const rdata::any::TSIG& PyTSIG_ToTSIG(const PyObject* tsig_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc index 53d294c603..a3ff402748 100644 --- a/src/lib/dns/python/tsigerror_python.cc +++ b/src/lib/dns/python/tsigerror_python.cc @@ -42,14 +42,17 @@ using namespace isc::dns::python; // TSIGError // -// Trivial constructor. -s_TSIGError::s_TSIGError() : cppobj(NULL) { -} - // Import pydoc text #include "tsigerror_python_inc.cc" namespace { +// The s_* Class simply covers one instantiation of the object +class s_TSIGError : public PyObject { +public: + s_TSIGError() : cppobj(NULL) {}; + const TSIGError* cppobj; +}; + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer TSIGErrorContainer; @@ -107,9 +110,9 @@ TSIGError_init(s_TSIGError* self, PyObject* args) { // Constructor from Rcode PyErr_Clear(); - s_Rcode* py_rcode; + PyObject* py_rcode; if (PyArg_ParseTuple(args, "O!", &rcode_type, &py_rcode)) { - self->cppobj = new TSIGError(*py_rcode->cppobj); + self->cppobj = new TSIGError(PyRcode_ToRcode(py_rcode)); return (0); } } catch (const isc::OutOfRange& ex) { @@ -172,13 +175,8 @@ TSIGError_str(PyObject* self) { PyObject* TSIGError_toRcode(const s_TSIGError* const self) { - typedef CPPPyObjectContainer RcodePyObjectContainer; - try { - RcodePyObjectContainer rcode_container(PyObject_New(s_Rcode, - &rcode_type)); - rcode_container.set(new Rcode(self->cppobj->toRcode())); - return (rcode_container.release()); + return (createRcodeObject(self->cppobj->toRcode())); } catch (const exception& ex) { const string ex_what = "Failed to convert TSIGError to Rcode: " + string(ex.what()); diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h index da028a07fa..14391fffe3 100644 --- a/src/lib/dns/python/tsigerror_python.h +++ b/src/lib/dns/python/tsigerror_python.h @@ -23,13 +23,6 @@ namespace isc { namespace dns { namespace python { -// The s_* Class simply covers one instantiation of the object -class s_TSIGError : public PyObject { -public: - s_TSIGError(); - const TSIGError* cppobj; -}; - extern PyTypeObject tsigerror_type; /// This is A simple shortcut to create a python TSIGError object (in the @@ -40,6 +33,7 @@ extern PyTypeObject tsigerror_type; /// This function is expected to be called with in a try block /// followed by necessary setup for python exception. PyObject* createTSIGErrorObject(const TSIGError& source); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc index abd412f76b..8962bca944 100644 --- a/src/lib/dns/python/tsigkey_python.cc +++ b/src/lib/dns/python/tsigkey_python.cc @@ -43,11 +43,14 @@ using namespace isc::dns::python; // TSIGKey // -// The s_* Class simply covers one instantiation of the object - -s_TSIGKey::s_TSIGKey() : cppobj(NULL) {} - namespace { +// The s_* Class simply covers one instantiation of the object +class s_TSIGKey : public PyObject { +public: + s_TSIGKey() : cppobj(NULL) {}; + TSIGKey* cppobj; +}; + // // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other @@ -96,8 +99,8 @@ TSIGKey_init(s_TSIGKey* self, PyObject* args) { } PyErr_Clear(); - const s_Name* key_name; - const s_Name* algorithm_name; + const PyObject* key_name; + const PyObject* algorithm_name; PyObject* bytes_obj; const char* secret; Py_ssize_t secret_len; @@ -107,8 +110,8 @@ TSIGKey_init(s_TSIGKey* self, PyObject* args) { if (secret_len == 0) { secret = NULL; } - self->cppobj = new TSIGKey(*key_name->cppobj, - *algorithm_name->cppobj, + self->cppobj = new TSIGKey(PyName_ToName(key_name), + PyName_ToName(algorithm_name), secret, secret_len); return (0); } @@ -279,6 +282,17 @@ initModulePart_TSIGKey(PyObject* mod) { } } // end namespace internal +bool +PyTSIGKey_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &tsigkey_type)); +} + +const TSIGKey& +PyTSIGKey_ToTSIGKey(const PyObject* tsigkey_obj) { + const s_TSIGKey* tsigkey = static_cast(tsigkey_obj); + return (*tsigkey->cppobj); +} + } // namespace python } // namespace dns } // namespace isc @@ -294,9 +308,13 @@ initModulePart_TSIGKey(PyObject* mod) { // The s_* Class simply covers one instantiation of the object -s_TSIGKeyRing::s_TSIGKeyRing() : cppobj(NULL) {} - namespace { +class s_TSIGKeyRing : public PyObject { +public: + s_TSIGKeyRing() : cppobj(NULL) {}; + TSIGKeyRing* cppobj; +}; + // // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other @@ -377,11 +395,11 @@ TSIGKeyRing_add(const s_TSIGKeyRing* const self, PyObject* args) { PyObject* TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) { - s_Name* key_name; + PyObject* key_name; if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) { const TSIGKeyRing::Result result = - self->cppobj->remove(*key_name->cppobj); + self->cppobj->remove(PyName_ToName(key_name)); return (Py_BuildValue("I", result)); } @@ -393,13 +411,14 @@ TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) { PyObject* TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) { - s_Name* key_name; - s_Name* algorithm_name; + PyObject* key_name; + PyObject* algorithm_name; if (PyArg_ParseTuple(args, "O!O!", &name_type, &key_name, &name_type, &algorithm_name)) { const TSIGKeyRing::FindResult result = - self->cppobj->find(*key_name->cppobj, *algorithm_name->cppobj); + self->cppobj->find(PyName_ToName(key_name), + PyName_ToName(algorithm_name)); if (result.key != NULL) { s_TSIGKey* key = PyObject_New(s_TSIGKey, &tsigkey_type); if (key == NULL) { @@ -500,6 +519,18 @@ initModulePart_TSIGKeyRing(PyObject* mod) { } } // end namespace internal +bool +PyTSIGKeyRing_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &tsigkeyring_type)); +} + +const TSIGKeyRing& +PyTSIGKeyRing_ToTSIGKeyRing(const PyObject* tsigkeyring_obj) { + const s_TSIGKeyRing* tsigkeyring = + static_cast(tsigkeyring_obj); + return (*tsigkeyring->cppobj); +} + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h index f9b9573dbe..a634f4f2a0 100644 --- a/src/lib/dns/python/tsigkey_python.h +++ b/src/lib/dns/python/tsigkey_python.h @@ -23,22 +23,45 @@ namespace isc { namespace dns { namespace python { -// The s_* Class simply covers one instantiation of the object -class s_TSIGKey : public PyObject { -public: - s_TSIGKey(); - TSIGKey* cppobj; -}; - -class s_TSIGKeyRing : public PyObject { -public: - s_TSIGKeyRing(); - TSIGKeyRing* cppobj; -}; - extern PyTypeObject tsigkey_type; extern PyTypeObject tsigkeyring_type; +/// \brief Checks if the given python object is a TSIGKey object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type TSIGKey, false otherwise +bool PyTSIGKey_Check(PyObject* obj); + +/// \brief Returns a reference to the TSIGKey object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type TSIGKey; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyTSIGKey_Check() +/// +/// \note This is not a copy; if the TSIGKey is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param tsigkey_obj The tsigkey object to convert +const TSIGKey& PyTSIGKey_ToTSIGKey(const PyObject* tsigkey_obj); + +/// \brief Checks if the given python object is a TSIGKeyRing object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type TSIGKeyRing, false otherwise +bool PyTSIGKeyRing_Check(PyObject* obj); + +/// \brief Returns a reference to the TSIGKeyRing object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type TSIGKeyRing; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyTSIGKeyRing_Check() +/// +/// \note This is not a copy; if the TSIGKeyRing is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param tsigkeyring_obj The tsigkeyring object to convert +const TSIGKeyRing& PyTSIGKeyRing_ToTSIGKeyRing(const PyObject* tsigkeyring_obj); + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc index 974cb22a6b..f51e5db4d4 100644 --- a/src/lib/dns/python/tsigrecord_python.cc +++ b/src/lib/dns/python/tsigrecord_python.cc @@ -45,10 +45,15 @@ using namespace isc::dns::python; // // Trivial constructor. -s_TSIGRecord::s_TSIGRecord() : cppobj(NULL) { -} namespace { +// The s_* Class simply covers one instantiation of the object +class s_TSIGRecord : public PyObject { +public: + s_TSIGRecord() : cppobj(NULL) {}; + TSIGRecord* cppobj; +}; + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer TSIGRecordContainer; @@ -102,11 +107,12 @@ PyMethodDef TSIGRecord_methods[] = { int TSIGRecord_init(s_TSIGRecord* self, PyObject* args) { try { - const s_Name* py_name; - const s_TSIG* py_tsig; + const PyObject* py_name; + const PyObject* py_tsig; if (PyArg_ParseTuple(args, "O!O!", &name_type, &py_name, &tsig_type, &py_tsig)) { - self->cppobj = new TSIGRecord(*py_name->cppobj, *py_tsig->cppobj); + self->cppobj = new TSIGRecord(PyName_ToName(py_name), + PyTSIG_ToTSIG(py_tsig)); return (0); } } catch (const exception& ex) { @@ -308,6 +314,19 @@ createTSIGRecordObject(const TSIGRecord& source) { container.set(new TSIGRecord(source)); return (container.release()); } + +bool +PyTSIGRecord_Check(PyObject* obj) { + return (PyObject_TypeCheck(obj, &tsigrecord_type)); +} + +const TSIGRecord& +PyTSIGRecord_ToTSIGRecord(PyObject* tsigrecord_obj) { + s_TSIGRecord* tsigrecord = static_cast(tsigrecord_obj); + return (*tsigrecord->cppobj); +} + + } // namespace python } // namespace dns } // namespace isc diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h index 3b027184c9..d968c8e7df 100644 --- a/src/lib/dns/python/tsigrecord_python.h +++ b/src/lib/dns/python/tsigrecord_python.h @@ -23,13 +23,6 @@ namespace isc { namespace dns { namespace python { -// The s_* Class simply covers one instantiation of the object -class s_TSIGRecord : public PyObject { -public: - s_TSIGRecord(); - TSIGRecord* cppobj; -}; - extern PyTypeObject tsigrecord_type; /// This is A simple shortcut to create a python TSIGRecord object (in the @@ -41,6 +34,24 @@ extern PyTypeObject tsigrecord_type; /// followed by necessary setup for python exception. PyObject* createTSIGRecordObject(const TSIGRecord& source); +/// \brief Checks if the given python object is a TSIGRecord object +/// +/// \param obj The object to check the type of +/// \return true if the object is of type TSIGRecord, false otherwise +bool PyTSIGRecord_Check(PyObject* obj); + +/// \brief Returns a reference to the TSIGRecord object contained within the given +/// Python object. +/// +/// \note The given object MUST be of type TSIGRecord; this can be checked with +/// either the right call to ParseTuple("O!"), or with PyTSIGRecord_Check() +/// +/// \note This is not a copy; if the TSIGRecord is needed when the PyObject +/// may be destroyed, the caller must copy it itself. +/// +/// \param rrtype_obj The rrtype object to convert +const TSIGRecord& PyTSIGRecord_ToTSIGRecord(PyObject* tsigrecord_obj); + } // namespace python } // namespace dns } // namespace isc From b12f4e55007ee2e8130991f322e782bb31a8a289 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 16 Sep 2011 21:51:54 +0200 Subject: [PATCH 714/974] [1245] update makefile and fix createRRsetObject --- src/lib/dns/python/Makefile.am | 16 ++--------- src/lib/dns/python/rrset_python.cc | 44 ++++++++++++------------------ 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am index eb834be65d..4452e40816 100644 --- a/src/lib/dns/python/Makefile.am +++ b/src/lib/dns/python/Makefile.am @@ -5,7 +5,7 @@ AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CXXFLAGS = $(B10_CXXFLAGS) lib_LTLIBRARIES = libpydnspp.la -libpydnspp_la_SOURCES = pydnspp_common.cc pydnspp_towire.h +libpydnspp_la_SOURCES = pydnspp_common.cc pydnspp_common.h pydnspp_towire.h libpydnspp_la_SOURCES += name_python.cc name_python.h libpydnspp_la_SOURCES += rrset_python.cc rrset_python.h libpydnspp_la_SOURCES += rrclass_python.cc rrclass_python.h @@ -38,19 +38,7 @@ pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) pydnspp_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS) -# directly included from source files, so these don't have their own -# rules -EXTRA_DIST = pydnspp_common.h -EXTRA_DIST += edns_python.cc -EXTRA_DIST += message_python.cc -EXTRA_DIST += rrclass_python.cc -EXTRA_DIST += opcode_python.cc -EXTRA_DIST += rrset_python.cc -EXTRA_DIST += question_python.cc -EXTRA_DIST += rrttl_python.cc -EXTRA_DIST += rdata_python.cc -EXTRA_DIST += rrtype_python.cc -EXTRA_DIST += tsigerror_python_inc.cc +EXTRA_DIST = tsigerror_python_inc.cc # Python prefers .so, while some OSes (specifically MacOS) use a different # suffix for dynamic objects. -module is necessary to work this around. diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index 2c339204c9..a4e9d30a2b 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -48,10 +48,6 @@ public: isc::dns::RRsetPtr cppobj; }; - -// Shortcut type which would be convenient for adding class variables safely. -typedef CPPPyObjectContainer RRsetContainer; - int RRset_init(s_RRset* self, PyObject* args); void RRset_destroy(s_RRset* self); @@ -381,34 +377,30 @@ initModulePart_RRset(PyObject* mod) { PyObject* createRRsetObject(const RRset& source) { + + // RRsets are noncopyable, so as a workaround we recreate a new one + // and copy over all content + RRsetPtr new_rrset = isc::dns::RRsetPtr( + new isc::dns::RRset(source.getName(), source.getClass(), + source.getType(), source.getTTL())); + + isc::dns::RdataIteratorPtr rdata_it(source.getRdataIterator()); + for (rdata_it->first(); !rdata_it->isLast(); rdata_it->next()) { + new_rrset->addRdata(rdata_it->getCurrent()); + } + + isc::dns::RRsetPtr sigs = source.getRRsig(); + if (sigs) { + new_rrset->addRRsig(sigs); + } s_RRset* py_rrset = static_cast(rrset_type.tp_alloc(&rrset_type, 0)); if (py_rrset == NULL) { isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " "probably due to short memory"); } - - // RRsets are noncopyable, so as a workaround we recreate a new one - // and copy over all content - try { - py_rrset->cppobj = isc::dns::RRsetPtr( - new isc::dns::RRset(source.getName(), source.getClass(), - source.getType(), source.getTTL())); - - isc::dns::RdataIteratorPtr rdata_it(source.getRdataIterator()); - for (rdata_it->first(); !rdata_it->isLast(); rdata_it->next()) { - py_rrset->cppobj->addRdata(rdata_it->getCurrent()); - } - - isc::dns::RRsetPtr sigs = source.getRRsig(); - if (sigs) { - py_rrset->cppobj->addRRsig(sigs); - } - return (py_rrset); - } catch (const std::bad_alloc&) { - isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " - "probably due to short memory"); - } + py_rrset->cppobj = new_rrset; + return (py_rrset); } bool From 170a0661dfb17014a62cd2eeaaa99e408bc55a14 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 16 Sep 2011 22:01:23 +0200 Subject: [PATCH 715/974] [1245] Make CPPPyObjectContainer constructor explicit --- src/lib/dns/python/edns_python.cc | 2 +- src/lib/dns/python/name_python.cc | 2 +- src/lib/dns/python/opcode_python.cc | 2 +- src/lib/dns/python/rcode_python.cc | 2 +- src/lib/dns/python/rrclass_python.cc | 2 +- src/lib/dns/python/rrttl_python.cc | 2 +- src/lib/dns/python/rrtype_python.cc | 2 +- src/lib/dns/python/tsig_rdata_python.cc | 2 +- src/lib/dns/python/tsigerror_python.cc | 2 +- src/lib/dns/python/tsigrecord_python.cc | 3 +-- src/lib/util/python/pycppwrapper_util.h | 2 +- 11 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index b1073d8a51..3893c88a00 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -385,7 +385,7 @@ initModulePart_EDNS(PyObject* mod) { PyObject* createEDNSObject(const EDNS& source) { - EDNSContainer container = PyObject_New(s_EDNS, &edns_type); + EDNSContainer container(PyObject_New(s_EDNS, &edns_type)); container.set(new EDNS(source)); return (container.release()); } diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index 40c7673e2c..e05f44dbbf 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -722,7 +722,7 @@ initModulePart_Name(PyObject* mod) { PyObject* createNameObject(const Name& source) { - NameContainer container = PyObject_New(s_Name, &name_type); + NameContainer container(PyObject_New(s_Name, &name_type)); container.set(new Name(source)); return (container.release()); } diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index 154745793b..d9b55450d9 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -397,7 +397,7 @@ initModulePart_Opcode(PyObject* mod) { PyObject* createOpcodeObject(const Opcode& source) { - OpcodeContainer container = PyObject_New(s_Opcode, &opcode_type); + OpcodeContainer container(PyObject_New(s_Opcode, &opcode_type)); container.set(new Opcode(source)); return (container.release()); } diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc index 2e51003fc4..8c4a985fd4 100644 --- a/src/lib/dns/python/rcode_python.cc +++ b/src/lib/dns/python/rcode_python.cc @@ -438,7 +438,7 @@ initModulePart_Rcode(PyObject* mod) { PyObject* createRcodeObject(const Rcode& source) { - RcodeContainer container = PyObject_New(s_Rcode, &rcode_type); + RcodeContainer container(PyObject_New(s_Rcode, &rcode_type)); container.set(new Rcode(source)); return (container.release()); } diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index 87ba32eb03..d411896451 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -364,7 +364,7 @@ initModulePart_RRClass(PyObject* mod) { PyObject* createRRClassObject(const RRClass& source) { - RRClassContainer container = PyObject_New(s_RRClass, &rrclass_type); + RRClassContainer container(PyObject_New(s_RRClass, &rrclass_type)); container.set(new RRClass(source)); return (container.release()); } diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index bea1d179d9..1394c0f0fe 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -317,7 +317,7 @@ initModulePart_RRTTL(PyObject* mod) { PyObject* createRRTTLObject(const RRTTL& source) { - RRTTLContainer container = PyObject_New(s_RRTTL, &rrttl_type); + RRTTLContainer container(PyObject_New(s_RRTTL, &rrttl_type)); container.set(new RRTTL(source)); return (container.release()); } diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index 6b6fcc8202..d3479ffa9b 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -458,7 +458,7 @@ initModulePart_RRType(PyObject* mod) { PyObject* createRRTypeObject(const RRType& source) { - RRTypeContainer container = PyObject_New(s_RRType, &rrtype_type); + RRTypeContainer container(PyObject_New(s_RRType, &rrtype_type)); container.set(new RRType(source)); return (container.release()); } diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc index 2a32be80d8..ee316e00c7 100644 --- a/src/lib/dns/python/tsig_rdata_python.cc +++ b/src/lib/dns/python/tsig_rdata_python.cc @@ -367,7 +367,7 @@ initModulePart_TSIG(PyObject* mod) { PyObject* createTSIGObject(const any::TSIG& source) { - TSIGContainer container = PyObject_New(s_TSIG, &tsig_type); + TSIGContainer container(PyObject_New(s_TSIG, &tsig_type)); container.set(new any::TSIG(source)); return (container.release()); } diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc index a3ff402748..ba22c059c2 100644 --- a/src/lib/dns/python/tsigerror_python.cc +++ b/src/lib/dns/python/tsigerror_python.cc @@ -361,7 +361,7 @@ initModulePart_TSIGError(PyObject* mod) { PyObject* createTSIGErrorObject(const TSIGError& source) { - TSIGErrorContainer container = PyObject_New(s_TSIGError, &tsigerror_type); + TSIGErrorContainer container(PyObject_New(s_TSIGError, &tsigerror_type)); container.set(new TSIGError(source)); return (container.release()); } diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc index f51e5db4d4..c1322296cc 100644 --- a/src/lib/dns/python/tsigrecord_python.cc +++ b/src/lib/dns/python/tsigrecord_python.cc @@ -309,8 +309,7 @@ initModulePart_TSIGRecord(PyObject* mod) { PyObject* createTSIGRecordObject(const TSIGRecord& source) { - TSIGRecordContainer container = PyObject_New(s_TSIGRecord, - &tsigrecord_type); + TSIGRecordContainer container(PyObject_New(s_TSIGRecord, &tsigrecord_type)); container.set(new TSIGRecord(source)); return (container.release()); } diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h index 3f396e2b1d..462e7150cb 100644 --- a/src/lib/util/python/pycppwrapper_util.h +++ b/src/lib/util/python/pycppwrapper_util.h @@ -293,7 +293,7 @@ protected: /// \c PyObject_New() to the caller. template struct CPPPyObjectContainer : public PyObjectContainer { - CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {} + explicit CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {} // This method associates a C++ object with the corresponding python // object enclosed in this class. From 0ea828cb5c74b0f9a254aeab2c7d31ff214371e5 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 16 Sep 2011 22:52:11 +0200 Subject: [PATCH 716/974] [1245] raise exception in _Check() if obj is NULL --- src/lib/dns/python/edns_python.cc | 3 +++ src/lib/dns/python/edns_python.h | 2 ++ src/lib/dns/python/messagerenderer_python.cc | 3 +++ src/lib/dns/python/messagerenderer_python.h | 2 ++ src/lib/dns/python/name_python.cc | 3 +++ src/lib/dns/python/name_python.h | 2 ++ src/lib/dns/python/opcode_python.cc | 3 +++ src/lib/dns/python/opcode_python.h | 2 ++ src/lib/dns/python/question_python.cc | 3 +++ src/lib/dns/python/question_python.h | 2 ++ src/lib/dns/python/rcode_python.cc | 3 +++ src/lib/dns/python/rcode_python.h | 2 ++ src/lib/dns/python/rdata_python.cc | 3 +++ src/lib/dns/python/rdata_python.h | 2 ++ src/lib/dns/python/rrclass_python.cc | 3 +++ src/lib/dns/python/rrclass_python.h | 2 ++ src/lib/dns/python/rrset_python.cc | 3 +++ src/lib/dns/python/rrset_python.h | 2 ++ src/lib/dns/python/rrttl_python.cc | 3 +++ src/lib/dns/python/rrttl_python.h | 2 ++ src/lib/dns/python/rrtype_python.cc | 3 +++ src/lib/dns/python/rrtype_python.h | 2 ++ src/lib/dns/python/tsig_python.cc | 3 +++ src/lib/dns/python/tsig_python.h | 2 ++ src/lib/dns/python/tsig_rdata_python.cc | 3 +++ src/lib/dns/python/tsig_rdata_python.h | 2 ++ src/lib/dns/python/tsigkey_python.cc | 6 ++++++ src/lib/dns/python/tsigkey_python.h | 2 ++ src/lib/dns/python/tsigrecord_python.cc | 3 +++ src/lib/dns/python/tsigrecord_python.h | 2 ++ 30 files changed, 78 insertions(+) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index 3893c88a00..308802d3ed 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -392,6 +392,9 @@ createEDNSObject(const EDNS& source) { bool PyEDNS_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &edns_type)); } diff --git a/src/lib/dns/python/edns_python.h b/src/lib/dns/python/edns_python.h index a6d3c38bb2..81869df24c 100644 --- a/src/lib/dns/python/edns_python.h +++ b/src/lib/dns/python/edns_python.h @@ -36,6 +36,8 @@ PyObject* createEDNSObject(const EDNS& source); /// \brief Checks if the given python object is a EDNS object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type EDNS, false otherwise bool PyEDNS_Check(PyObject* obj); diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc index 2c475d8f2e..438dbb420d 100644 --- a/src/lib/dns/python/messagerenderer_python.cc +++ b/src/lib/dns/python/messagerenderer_python.cc @@ -283,6 +283,9 @@ createMessageRendererObject(const MessageRenderer& source) { bool PyMessageRenderer_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &messagerenderer_type)); } diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h index 2f51853717..bfd895ae62 100644 --- a/src/lib/dns/python/messagerenderer_python.h +++ b/src/lib/dns/python/messagerenderer_python.h @@ -37,6 +37,8 @@ PyObject* createMessageRendererObject(const MessageRenderer& source); /// \brief Checks if the given python object is a MessageRenderer object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type MessageRenderer, false otherwise bool PyMessageRenderer_Check(PyObject* obj); diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index e05f44dbbf..6e60ba9296 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -729,6 +729,9 @@ createNameObject(const Name& source) { bool PyName_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &name_type)); } diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h index 3667c35094..9160a22b21 100644 --- a/src/lib/dns/python/name_python.h +++ b/src/lib/dns/python/name_python.h @@ -58,6 +58,8 @@ PyObject* createNameObject(const Name& source); /// \brief Checks if the given python object is a Name object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type Name, false otherwise bool PyName_Check(PyObject* obj); diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index d9b55450d9..ea76d99e22 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -404,6 +404,9 @@ createOpcodeObject(const Opcode& source) { bool PyOpcode_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &opcode_type)); } diff --git a/src/lib/dns/python/opcode_python.h b/src/lib/dns/python/opcode_python.h index 04ee7e7b02..72865883a9 100644 --- a/src/lib/dns/python/opcode_python.h +++ b/src/lib/dns/python/opcode_python.h @@ -36,6 +36,8 @@ PyObject* createOpcodeObject(const Opcode& source); /// \brief Checks if the given python object is a Opcode object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type Opcode, false otherwise bool PyOpcode_Check(PyObject* obj); diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index 953a0c3672..c075f97ab0 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -283,6 +283,9 @@ createQuestionObject(const Question& source) { bool PyQuestion_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &question_type)); } diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h index 1971b4b3d7..575f0b7489 100644 --- a/src/lib/dns/python/question_python.h +++ b/src/lib/dns/python/question_python.h @@ -43,6 +43,8 @@ PyObject* createQuestionObject(const Question& source); /// \brief Checks if the given python object is a Question object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type Question, false otherwise bool PyQuestion_Check(PyObject* obj); diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc index 8c4a985fd4..cc7948351c 100644 --- a/src/lib/dns/python/rcode_python.cc +++ b/src/lib/dns/python/rcode_python.cc @@ -445,6 +445,9 @@ createRcodeObject(const Rcode& source) { bool PyRcode_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &rcode_type)); } diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h index 89969c7299..a2de2798aa 100644 --- a/src/lib/dns/python/rcode_python.h +++ b/src/lib/dns/python/rcode_python.h @@ -36,6 +36,8 @@ PyObject* createRcodeObject(const Rcode& source); /// \brief Checks if the given python object is a Rcode object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type Rcode, false otherwise bool PyRcode_Check(PyObject* obj); diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index 3167fcae70..a82fb97698 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -306,6 +306,9 @@ createRdataObject(ConstRdataPtr source) { bool PyRdata_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &rdata_type)); } diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h index 8db68216d0..ca54d13788 100644 --- a/src/lib/dns/python/rdata_python.h +++ b/src/lib/dns/python/rdata_python.h @@ -43,6 +43,8 @@ PyObject* createRdataObject(isc::dns::rdata::ConstRdataPtr source); /// \brief Checks if the given python object is a Rdata object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type Rdata, false otherwise bool PyRdata_Check(PyObject* obj); diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index d411896451..423ab4207f 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -372,6 +372,9 @@ createRRClassObject(const RRClass& source) { bool PyRRClass_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &rrclass_type)); } diff --git a/src/lib/dns/python/rrclass_python.h b/src/lib/dns/python/rrclass_python.h index f11b817ef9..dc84cdb68f 100644 --- a/src/lib/dns/python/rrclass_python.h +++ b/src/lib/dns/python/rrclass_python.h @@ -39,6 +39,8 @@ PyObject* createRRClassObject(const RRClass& source); /// \brief Checks if the given python object is a RRClass object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type RRClass, false otherwise bool PyRRClass_Check(PyObject* obj); diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index a4e9d30a2b..4ae1baee50 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -405,6 +405,9 @@ createRRsetObject(const RRset& source) { bool PyRRset_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &rrset_type)); } diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h index 04d305f359..6e56685a36 100644 --- a/src/lib/dns/python/rrset_python.h +++ b/src/lib/dns/python/rrset_python.h @@ -45,6 +45,8 @@ PyObject* createRRsetObject(const RRset& source); /// \brief Checks if the given python object is a RRset object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type RRset, false otherwise bool PyRRset_Check(PyObject* obj); diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index 1394c0f0fe..84610f83e7 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -324,6 +324,9 @@ createRRTTLObject(const RRTTL& source) { bool PyRRTTL_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &rrttl_type)); } diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h index ba0f5f5b7b..c5b2b77b52 100644 --- a/src/lib/dns/python/rrttl_python.h +++ b/src/lib/dns/python/rrttl_python.h @@ -44,6 +44,8 @@ PyObject* createRRTTLObject(const RRTTL& source); /// \brief Checks if the given python object is a RRTTL object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type RRTTL, false otherwise bool PyRRTTL_Check(PyObject* obj); diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index d3479ffa9b..c85bf154ad 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -465,6 +465,9 @@ createRRTypeObject(const RRType& source) { bool PyRRType_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &rrtype_type)); } diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h index c251d9ef5f..6915427237 100644 --- a/src/lib/dns/python/rrtype_python.h +++ b/src/lib/dns/python/rrtype_python.h @@ -44,6 +44,8 @@ PyObject* createRRTypeObject(const RRType& source); /// \brief Checks if the given python object is a RRType object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type RRType, false otherwise bool PyRRType_Check(PyObject* obj); diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc index 9cc596e91b..11f084c329 100644 --- a/src/lib/dns/python/tsig_python.cc +++ b/src/lib/dns/python/tsig_python.cc @@ -366,6 +366,9 @@ initModulePart_TSIGContext(PyObject* mod) { bool PyTSIGContext_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &tsigcontext_type)); } diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h index 9be92bb1da..b76002dd8d 100644 --- a/src/lib/dns/python/tsig_python.h +++ b/src/lib/dns/python/tsig_python.h @@ -30,6 +30,8 @@ extern PyObject* po_TSIGContextError; /// \brief Checks if the given python object is a TSIGContext object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type TSIGContext, false otherwise bool PyTSIGContext_Check(PyObject* obj); diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc index ee316e00c7..9b953a731b 100644 --- a/src/lib/dns/python/tsig_rdata_python.cc +++ b/src/lib/dns/python/tsig_rdata_python.cc @@ -374,6 +374,9 @@ createTSIGObject(const any::TSIG& source) { bool PyTSIG_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &tsig_type)); } diff --git a/src/lib/dns/python/tsig_rdata_python.h b/src/lib/dns/python/tsig_rdata_python.h index cb39317074..a84d9e89a0 100644 --- a/src/lib/dns/python/tsig_rdata_python.h +++ b/src/lib/dns/python/tsig_rdata_python.h @@ -40,6 +40,8 @@ PyObject* createTSIGObject(const rdata::any::TSIG& source); /// \brief Checks if the given python object is a TSIG object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type TSIG, false otherwise bool PyTSIG_Check(PyObject* obj); diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc index 8962bca944..32d060b7f3 100644 --- a/src/lib/dns/python/tsigkey_python.cc +++ b/src/lib/dns/python/tsigkey_python.cc @@ -284,6 +284,9 @@ initModulePart_TSIGKey(PyObject* mod) { bool PyTSIGKey_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &tsigkey_type)); } @@ -521,6 +524,9 @@ initModulePart_TSIGKeyRing(PyObject* mod) { bool PyTSIGKeyRing_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &tsigkeyring_type)); } diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h index a634f4f2a0..1807f5415c 100644 --- a/src/lib/dns/python/tsigkey_python.h +++ b/src/lib/dns/python/tsigkey_python.h @@ -28,6 +28,8 @@ extern PyTypeObject tsigkeyring_type; /// \brief Checks if the given python object is a TSIGKey object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type TSIGKey, false otherwise bool PyTSIGKey_Check(PyObject* obj); diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc index c1322296cc..68d57e5504 100644 --- a/src/lib/dns/python/tsigrecord_python.cc +++ b/src/lib/dns/python/tsigrecord_python.cc @@ -316,6 +316,9 @@ createTSIGRecordObject(const TSIGRecord& source) { bool PyTSIGRecord_Check(PyObject* obj) { + if (obj == NULL) { + isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck"); + } return (PyObject_TypeCheck(obj, &tsigrecord_type)); } diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h index d968c8e7df..9b392c3f4b 100644 --- a/src/lib/dns/python/tsigrecord_python.h +++ b/src/lib/dns/python/tsigrecord_python.h @@ -36,6 +36,8 @@ PyObject* createTSIGRecordObject(const TSIGRecord& source); /// \brief Checks if the given python object is a TSIGRecord object /// +/// \exception PyCPPWrapperException if obj is NULL +/// /// \param obj The object to check the type of /// \return true if the object is of type TSIGRecord, false otherwise bool PyTSIGRecord_Check(PyObject* obj); From 90b740caf4cc5d207dfa2ac98f1c73d9818792e2 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 16 Sep 2011 23:11:34 +0200 Subject: [PATCH 717/974] [1245] namespace fix --- src/lib/dns/python/question_python.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index c075f97ab0..d59577297a 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "question_python.h" @@ -29,6 +30,7 @@ using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; +using namespace isc::util::python; using namespace isc; namespace { From fac67afceead36ba7296e194942811d9ed3b437b Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 16 Sep 2011 22:25:00 +0000 Subject: [PATCH 718/974] [1245] remove const for sunstudio build --- src/lib/dns/python/edns_python.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index 3893c88a00..8b5b584d11 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -177,7 +177,7 @@ EDNS_toText(const s_EDNS* const self) { } PyObject* -EDNS_str(PyObject* const self) { +EDNS_str(PyObject* self) { // Simply call the to_text method we already defined return (PyObject_CallMethod(self, const_cast("to_text"), From aa5fd84d438cf165c9836fa545d15c33781401af Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 16 Sep 2011 22:27:29 +0000 Subject: [PATCH 719/974] [1245] remove #include --- src/lib/dns/python/pydnspp.cc | 2 -- src/lib/dns/python/pydnspp_common.cc | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 71be32c472..9d6bc566bb 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -27,8 +27,6 @@ #include #include -#include - #include "pydnspp_common.h" /* Note that we do forward declarations of the initialization functions here, * and these are not defined in headers (since they are not to be used in any diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc index 4a2ad16487..d47177bcbd 100644 --- a/src/lib/dns/python/pydnspp_common.cc +++ b/src/lib/dns/python/pydnspp_common.cc @@ -15,9 +15,6 @@ #include #include - -#include - #include #include From cbf08d56345922d754182b941b84b18bfddabcda Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Sat, 17 Sep 2011 00:44:36 +0200 Subject: [PATCH 720/974] [1245] remove some unnecessary comments --- src/lib/dns/python/message_python.h | 5 ----- src/lib/dns/python/name_python.h | 5 ----- src/lib/dns/python/pydnspp.cc | 1 - src/lib/dns/python/pydnspp_common.cc | 1 - src/lib/dns/python/question_python.h | 5 ----- src/lib/dns/python/rdata_python.h | 5 ----- src/lib/dns/python/rrset_python.h | 5 ----- src/lib/dns/python/rrttl_python.h | 5 ----- src/lib/dns/python/rrtype_python.h | 5 ----- src/lib/dns/python/tsig_python.cc | 7 ------- src/lib/dns/python/tsig_rdata_python.cc | 10 +--------- src/lib/dns/python/tsigerror_python.cc | 8 -------- src/lib/dns/python/tsigkey_python.cc | 9 +-------- src/lib/dns/python/tsigrecord_python.cc | 6 ------ 14 files changed, 2 insertions(+), 75 deletions(-) diff --git a/src/lib/dns/python/message_python.h b/src/lib/dns/python/message_python.h index be1adb9d1b..8ae3efcc9b 100644 --- a/src/lib/dns/python/message_python.h +++ b/src/lib/dns/python/message_python.h @@ -23,11 +23,6 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// extern PyObject* po_MessageTooShort; extern PyObject* po_InvalidMessageSection; extern PyObject* po_InvalidMessageOperation; diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h index 9160a22b21..fdf6e4abfc 100644 --- a/src/lib/dns/python/name_python.h +++ b/src/lib/dns/python/name_python.h @@ -23,11 +23,6 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// extern PyObject* po_EmptyLabel; extern PyObject* po_TooLongName; extern PyObject* po_TooLongLabel; diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 9d6bc566bb..4699e55384 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -21,7 +21,6 @@ // name initModulePart_, and return true/false instead of // NULL/*mod // -// And of course care has to be taken that all identifiers be unique #define PY_SSIZE_T_CLEAN #include diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc index d47177bcbd..0f0f873867 100644 --- a/src/lib/dns/python/pydnspp_common.cc +++ b/src/lib/dns/python/pydnspp_common.cc @@ -41,7 +41,6 @@ #include "question_python.h" #include "message_python.h" -// order is important here! using namespace isc::dns::python; namespace isc { diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h index 575f0b7489..4e8b2ab142 100644 --- a/src/lib/dns/python/question_python.h +++ b/src/lib/dns/python/question_python.h @@ -23,11 +23,6 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// extern PyObject* po_EmptyQuestion; extern PyTypeObject question_type; diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h index ca54d13788..d3e110d351 100644 --- a/src/lib/dns/python/rdata_python.h +++ b/src/lib/dns/python/rdata_python.h @@ -23,11 +23,6 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// extern PyObject* po_EmptyRdata; extern PyTypeObject rdata_type; diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h index 6e56685a36..4268678d92 100644 --- a/src/lib/dns/python/rrset_python.h +++ b/src/lib/dns/python/rrset_python.h @@ -25,11 +25,6 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// extern PyObject* po_EmptyRRset; extern PyTypeObject rrset_type; diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h index c5b2b77b52..f5bdb49f7a 100644 --- a/src/lib/dns/python/rrttl_python.h +++ b/src/lib/dns/python/rrttl_python.h @@ -23,11 +23,6 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// extern PyObject* po_InvalidRRTTL; extern PyObject* po_IncompleteRRTTL; diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h index 6915427237..a0a1b70731 100644 --- a/src/lib/dns/python/rrtype_python.h +++ b/src/lib/dns/python/rrtype_python.h @@ -23,11 +23,6 @@ namespace isc { namespace dns { namespace python { -// -// Declaration of the custom exceptions -// Initialization and addition of these go in the module init at the -// end -// extern PyObject* po_InvalidRRType; extern PyObject* po_IncompleteRRType; diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc index 11f084c329..e27acde041 100644 --- a/src/lib/dns/python/tsig_python.cc +++ b/src/lib/dns/python/tsig_python.cc @@ -37,17 +37,10 @@ using namespace isc::util::python; using namespace isc::dns; using namespace isc::dns::python; -// -// Definition of the classes -// - // For each class, we need a struct, a helper functions (init, destroy, // and static wrappers around the methods we export), a list of methods, // and a type description -// -// TSIGContext -// namespace { // The s_* Class simply covers one instantiation of the object class s_TSIGContext : public PyObject { diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc index 9b953a731b..858bee1998 100644 --- a/src/lib/dns/python/tsig_rdata_python.cc +++ b/src/lib/dns/python/tsig_rdata_python.cc @@ -33,20 +33,12 @@ using namespace isc::dns; using namespace isc::dns::rdata; using namespace isc::dns::python; -// -// Definition of the classes -// - // For each class, we need a struct, a helper functions (init, destroy, // and static wrappers around the methods we export), a list of methods, // and a type description -// -// TSIG RDATA -// - namespace { - // The s_* Class simply covers one instantiation of the object +// The s_* Class simply covers one instantiation of the object class s_TSIG : public PyObject { public: s_TSIG() : cppobj(NULL) {}; diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc index ba22c059c2..93dfbe8d93 100644 --- a/src/lib/dns/python/tsigerror_python.cc +++ b/src/lib/dns/python/tsigerror_python.cc @@ -30,18 +30,10 @@ using namespace isc::util::python; using namespace isc::dns; using namespace isc::dns::python; -// -// Definition of the classes -// - // For each class, we need a struct, a helper functions (init, destroy, // and static wrappers around the methods we export), a list of methods, // and a type description -// -// TSIGError -// - // Import pydoc text #include "tsigerror_python_inc.cc" diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc index 32d060b7f3..b453ae9698 100644 --- a/src/lib/dns/python/tsigkey_python.cc +++ b/src/lib/dns/python/tsigkey_python.cc @@ -31,10 +31,6 @@ using namespace isc::util::python; using namespace isc::dns; using namespace isc::dns::python; -// -// Definition of the classes -// - // For each class, we need a struct, a helper functions (init, destroy, // and static wrappers around the methods we export), a list of methods, // and a type description @@ -307,11 +303,8 @@ PyTSIGKey_ToTSIGKey(const PyObject* tsigkey_obj) { // TSIGKeyRing // -// The s_* Class simply covers one instantiation of the object - -// The s_* Class simply covers one instantiation of the object - namespace { +// The s_* Class simply covers one instantiation of the object class s_TSIGKeyRing : public PyObject { public: s_TSIGKeyRing() : cppobj(NULL) {}; diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc index 68d57e5504..947d0d9c28 100644 --- a/src/lib/dns/python/tsigrecord_python.cc +++ b/src/lib/dns/python/tsigrecord_python.cc @@ -32,10 +32,6 @@ using namespace isc::util::python; using namespace isc::dns; using namespace isc::dns::python; -// -// Definition of the classes -// - // For each class, we need a struct, a helper functions (init, destroy, // and static wrappers around the methods we export), a list of methods, // and a type description @@ -44,8 +40,6 @@ using namespace isc::dns::python; // TSIGRecord // -// Trivial constructor. - namespace { // The s_* Class simply covers one instantiation of the object class s_TSIGRecord : public PyObject { From 88fe1bafce118f40d256097c2bfbdf9e53553784 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 19 Sep 2011 14:16:03 +0200 Subject: [PATCH 721/974] [1177] Added missing documentation --- src/lib/datasrc/database.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index c9c2bc5a3a..69bfa2fa1b 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -640,6 +640,33 @@ public: FoundRRsets; /// \brief Just shortcut for set of types typedef std::set WantedTypes; + /** + * \brief Searches database for RRsets of one domain. + * + * This method scans RRs of single domain specified by name and + * extracts any RRsets found and requested by parameters. + * + * It is used internally by find(), because it is called multiple + * times (usually with different domains). + * + * \param name Which domain name should be scanned. + * \param types List of types the caller is interested in. + * \param check_ns If this is set to true, it checks nothing lives + * together with NS record (with few little exceptions, like RRSET + * or NSEC). This check is meant for non-apex NS records. + * \param construct_name If this is NULL, the resulting RRsets have + * their name set to name. If it is not NULL, it overrides the name + * and uses this one (this can be used for wildcard synthesized + * records). + * \return A pair, where the first element indicates if the domain + * contains any RRs at all (not only the requested, it may happen + * this is set to true, but the second part is empty). The second + * part is map from RRtypes to RRsets of the corresponding types. + * If the RRset is not present in DB, the RRtype is not there at + * all (so you'll not find NULL pointer in the result). + * \throw DataSourceError If there's a low-level error with the + * database or the database contains bad data. + */ FoundRRsets getRRsets(const dns::Name& name, const WantedTypes& types, bool check_ns, const dns::Name* construct_name = NULL); From 723a6d1f333f1d513d5e4fe26a8ee7611767c9fc Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 19 Sep 2011 14:16:17 +0200 Subject: [PATCH 722/974] [1177] Cleanup: removed exception variable names --- src/lib/datasrc/database.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 8cc70920cc..1204096d1c 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -247,15 +247,15 @@ DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, // FIXME: Is something else allowed in the delegation point? DS? seen_other = true; } - } catch (const InvalidRRType& irt) { + } catch (const InvalidRRType&) { isc_throw(DataSourceError, "Invalid RRType in database for " << name << ": " << columns[DatabaseAccessor:: TYPE_COLUMN]); - } catch (const InvalidRRTTL& irttl) { + } catch (const InvalidRRTTL&) { isc_throw(DataSourceError, "Invalid TTL in database for " << name << ": " << columns[DatabaseAccessor:: TTL_COLUMN]); - } catch (const rdata::InvalidRdataText& ird) { + } catch (const rdata::InvalidRdataText&) { isc_throw(DataSourceError, "Invalid rdata in database for " << name << ": " << columns[DatabaseAccessor:: RDATA_COLUMN]); From e9286ce511be095f2b16b1b7bc503b1e4377593d Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 19 Sep 2011 14:22:57 +0200 Subject: [PATCH 723/974] [1177] Allow NSEC and DS together with NS --- src/lib/datasrc/database.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 1204096d1c..432957f6a8 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -197,6 +197,7 @@ DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, } bool seen_cname(false); + bool seen_ds(false); bool seen_other(false); bool seen_ns(false); @@ -243,8 +244,14 @@ DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, seen_cname = true; } else if (cur_type == RRType::NS()) { seen_ns = true; - } else if (cur_type != RRType::RRSIG()) {//RRSIG can live anywhere - // FIXME: Is something else allowed in the delegation point? DS? + } else if (cur_type == RRType::DS()) { + seen_ds = true; + } else if (cur_type != RRType::RRSIG() && + cur_type != RRType::NSEC3() && + cur_type != RRType::NSEC()) { + // NSEC and RRSIG can coexist with anything, otherwise + // we've seen something that can't live together with potential + // CNAME or NS seen_other = true; } } catch (const InvalidRRType&) { @@ -261,7 +268,7 @@ DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, RDATA_COLUMN]); } } - if (seen_cname && (seen_other || seen_ns)) { + if (seen_cname && (seen_other || seen_ns || seen_ds)) { isc_throw(DataSourceError, "CNAME shares domain " << name << " with something else"); } From 88bee2515653d3b5481608bc92a1956c7ea7cf48 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 19 Sep 2011 14:34:23 +0200 Subject: [PATCH 724/974] [1177] Remove unneeded static variable --- src/lib/datasrc/database.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 432957f6a8..25184f41da 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -302,8 +302,6 @@ DatabaseClient::Finder::hasSubdomains(const std::string& name) { // Some manipulation with RRType sets namespace { -const std::set empty_types; - // To conveniently put the RRTypes into the sets. This is not really clean // design, but it is hidden inside this file and makes the calls much more // convenient. @@ -335,7 +333,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // we can't do it under NS, so we store it here to check isc::dns::RRsetPtr first_ns; // This is used at multiple places - static const WantedTypes nsec_types(empty_types + RRType::NSEC()); + static const WantedTypes nsec_types(WantedTypes() + RRType::NSEC()); // First, do we have any kind of delegation (NS/DNAME) here? const Name origin(getOrigin()); @@ -353,7 +351,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, for (int i(remove_labels); i > 0; --i) { Name superdomain(name.split(i)); // Look if there's NS or DNAME (but ignore the NS in origin) - static const WantedTypes delegation_types(empty_types + + static const WantedTypes delegation_types(WantedTypes() + RRType::DNAME() + RRType::NS()); found = getRRsets(superdomain, delegation_types, i != remove_labels); @@ -401,7 +399,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // It is special if there's a CNAME or NS, DNAME is ignored here // And we don't consider the NS in origin - static const WantedTypes final_types(empty_types + RRType::CNAME() + + static const WantedTypes final_types(WantedTypes() + RRType::CNAME() + RRType::NS() + RRType::NSEC()); found = getRRsets(name, final_types + type, name != origin); records_found = found.first; @@ -455,7 +453,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, const Name superdomain(name.split(i)); const Name wildcard(star.concatenate(superdomain)); // TODO What do we do about DNAME here? - static const WantedTypes wildcard_types(empty_types + + static const WantedTypes wildcard_types(WantedTypes() + RRType::CNAME() + RRType::NS() + RRType::NSEC()); From 38d80ef7186ac2b18ed234a825894f5f78fc90b1 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 19 Sep 2011 15:14:45 +0200 Subject: [PATCH 725/974] [1177] Concatenate on strings --- src/lib/datasrc/database.cc | 41 ++++++++++++++++++++++--------------- src/lib/datasrc/database.h | 6 +++--- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 25184f41da..bb5be93412 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -175,8 +175,8 @@ private: } DatabaseClient::Finder::FoundRRsets -DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, - bool check_ns, const Name* construct_name) +DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types, + bool check_ns, const string* construct_name) { RRsigStore sig_store; bool records_found = false; @@ -184,11 +184,10 @@ DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, // Request the context DatabaseAccessor::IteratorContextPtr - context(accessor_->getRecords(name.toText(), zone_id_)); + context(accessor_->getRecords(name, zone_id_)); // It must not return NULL, that's a bug of the implementation if (!context) { - isc_throw(isc::Unexpected, "Iterator context null at " + - name.toText()); + isc_throw(isc::Unexpected, "Iterator context null at " + name); } std::string columns[DatabaseAccessor::COLUMN_COUNT]; @@ -196,6 +195,8 @@ DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, construct_name = &name; } + const Name construct_name_object(*construct_name); + bool seen_cname(false); bool seen_ds(false); bool seen_other(false); @@ -234,8 +235,8 @@ DatabaseClient::Finder::getRRsets(const Name& name, const WantedTypes& types, // contains an rrtype that is different from the actual value // of the 'type covered' field in the RRSIG Rdata). //cur_sigtype(columns[SIGTYPE_COLUMN]); - addOrCreate(result[cur_type], *construct_name, getClass(), - cur_type, cur_ttl, + addOrCreate(result[cur_type], construct_name_object, + getClass(), cur_type, cur_ttl, columns[DatabaseAccessor::RDATA_COLUMN], *accessor_); } @@ -305,6 +306,14 @@ namespace { // To conveniently put the RRTypes into the sets. This is not really clean // design, but it is hidden inside this file and makes the calls much more // convenient. +// +// While this is not straightforward use of the + operator, some mathematical +// conventions do allow summing sets with elements (usually in communitative +// way, we define only one order of the operator parameters, as the other +// isn't used). +// +// This arguably produces more readable code than having bunch of proxy +// functions and set.insert calls scattered through the code. std::set operator +(std::set set, const RRType& type) { set.insert(type); return (set); @@ -354,7 +363,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, static const WantedTypes delegation_types(WantedTypes() + RRType::DNAME() + RRType::NS()); - found = getRRsets(superdomain, delegation_types, i != remove_labels); + found = getRRsets(superdomain.toText(), delegation_types, + i != remove_labels); if (found.first) { // It contains some RRs, so it exists. last_known = superdomain.getLabelCount(); @@ -401,7 +411,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, static const WantedTypes final_types(WantedTypes() + RRType::CNAME() + RRType::NS() + RRType::NSEC()); - found = getRRsets(name, final_types + type, name != origin); + found = getRRsets(name.toText(), final_types + type, name != origin); records_found = found.first; // NS records, CNAME record and Wanted Type records @@ -444,21 +454,18 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Go up to first non-empty domain. remove_labels = current_label_count - last_known; - const Name star("*"); for (size_t i(1); i <= remove_labels; ++ i) { // Construct the name with * - // TODO: Once the underlying DatabaseAccessor takes - // string, do the concatenation on strings, not - // Names const Name superdomain(name.split(i)); - const Name wildcard(star.concatenate(superdomain)); + const string wildcard("*." + superdomain.toText()); + const string construct_name(name.toText()); // TODO What do we do about DNAME here? static const WantedTypes wildcard_types(WantedTypes() + RRType::CNAME() + RRType::NS() + RRType::NSEC()); found = getRRsets(wildcard, wildcard_types + type, true, - &name); + &construct_name); if (found.first) { if (first_ns) { // In case we are under NS, we don't @@ -526,7 +533,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, arg(name).arg(superdomain); } break; - } else if (hasSubdomains(wildcard.toText())) { + } else if (hasSubdomains(wildcard)) { // Empty non-terminal asterisk records_found = true; get_cover = dnssec_data; @@ -563,7 +570,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Which one should contain the NSEC record? const Name coverName(findPreviousName(name)); // Get the record and copy it out - found = getRRsets(coverName, nsec_types, true); + found = getRRsets(coverName.toText(), nsec_types, true); const FoundIterator nci(found.second.find(RRType::NSEC())); if (nci != found.second.end()) { diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 69bfa2fa1b..7d36508fd2 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -667,9 +667,9 @@ public: * \throw DataSourceError If there's a low-level error with the * database or the database contains bad data. */ - FoundRRsets getRRsets(const dns::Name& name, const WantedTypes& types, - bool check_ns, - const dns::Name* construct_name = NULL); + FoundRRsets getRRsets(const std::string& name, + const WantedTypes& types, bool check_ns, + const std::string* construct_name = NULL); /** * \brief Checks if something lives below this domain. * From 45ef63790b34ebc2d26081609bb168aefee800dc Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 20 Sep 2011 00:23:37 +0200 Subject: [PATCH 726/974] [1245] move initModulePart calls to pydnspp.cc --- src/lib/dns/python/edns_python.cc | 20 - src/lib/dns/python/message_python.cc | 72 +- src/lib/dns/python/messagerenderer_python.cc | 19 - src/lib/dns/python/name_python.cc | 83 --- src/lib/dns/python/opcode_python.cc | 54 -- src/lib/dns/python/pydnspp.cc | 695 ++++++++++++++++++- src/lib/dns/python/question_python.cc | 19 - src/lib/dns/python/rcode_python.cc | 55 -- src/lib/dns/python/rdata_python.cc | 37 +- src/lib/dns/python/rdata_python.h | 4 +- src/lib/dns/python/rrclass_python.cc | 29 +- src/lib/dns/python/rrset_python.cc | 30 - src/lib/dns/python/rrttl_python.cc | 26 +- src/lib/dns/python/rrtype_python.cc | 23 - src/lib/dns/python/tsig_python.cc | 55 -- src/lib/dns/python/tsig_rdata_python.cc | 20 - src/lib/dns/python/tsigerror_python.cc | 71 -- src/lib/dns/python/tsigkey_python.cc | 71 -- src/lib/dns/python/tsigrecord_python.cc | 39 -- 19 files changed, 670 insertions(+), 752 deletions(-) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index df7e8a4950..126422312a 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -363,26 +363,6 @@ PyTypeObject edns_type = { 0 // tp_version_tag }; -namespace internal { -bool -initModulePart_EDNS(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&edns_type) < 0) { - return (false); - } - Py_INCREF(&edns_type); - void* p = &edns_type; - PyModule_AddObject(mod, "EDNS", static_cast(p)); - - addClassVariable(edns_type, "SUPPORTED_VERSION", - Py_BuildValue("B", EDNS::SUPPORTED_VERSION)); - - return (true); -} -} // end namespace internal - PyObject* createEDNSObject(const EDNS& source) { EDNSContainer container(PyObject_New(s_EDNS, &edns_type)); diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 0ac1aabadf..a41523e0a4 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -646,7 +646,7 @@ namespace python { // // Declaration of the custom exceptions // Initialization and addition of these go in the initModulePart -// function at the end of this file +// function in pydnspp.cc // PyObject* po_MessageTooShort; PyObject* po_InvalidMessageSection; @@ -706,76 +706,6 @@ PyTypeObject message_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_Message(PyObject* mod) { - if (PyType_Ready(&message_type) < 0) { - return (false); - } - Py_INCREF(&message_type); - - // Class variables - // These are added to the tp_dict of the type object - // - addClassVariable(message_type, "PARSE", - Py_BuildValue("I", Message::PARSE)); - addClassVariable(message_type, "RENDER", - Py_BuildValue("I", Message::RENDER)); - - addClassVariable(message_type, "HEADERFLAG_QR", - Py_BuildValue("I", Message::HEADERFLAG_QR)); - addClassVariable(message_type, "HEADERFLAG_AA", - Py_BuildValue("I", Message::HEADERFLAG_AA)); - addClassVariable(message_type, "HEADERFLAG_TC", - Py_BuildValue("I", Message::HEADERFLAG_TC)); - addClassVariable(message_type, "HEADERFLAG_RD", - Py_BuildValue("I", Message::HEADERFLAG_RD)); - addClassVariable(message_type, "HEADERFLAG_RA", - Py_BuildValue("I", Message::HEADERFLAG_RA)); - addClassVariable(message_type, "HEADERFLAG_AD", - Py_BuildValue("I", Message::HEADERFLAG_AD)); - addClassVariable(message_type, "HEADERFLAG_CD", - Py_BuildValue("I", Message::HEADERFLAG_CD)); - - addClassVariable(message_type, "SECTION_QUESTION", - Py_BuildValue("I", Message::SECTION_QUESTION)); - addClassVariable(message_type, "SECTION_ANSWER", - Py_BuildValue("I", Message::SECTION_ANSWER)); - addClassVariable(message_type, "SECTION_AUTHORITY", - Py_BuildValue("I", Message::SECTION_AUTHORITY)); - addClassVariable(message_type, "SECTION_ADDITIONAL", - Py_BuildValue("I", Message::SECTION_ADDITIONAL)); - - addClassVariable(message_type, "DEFAULT_MAX_UDPSIZE", - Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE)); - - /* Class-specific exceptions */ - po_MessageTooShort = PyErr_NewException("pydnspp.MessageTooShort", NULL, - NULL); - PyModule_AddObject(mod, "MessageTooShort", po_MessageTooShort); - po_InvalidMessageSection = - PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL); - PyModule_AddObject(mod, "InvalidMessageSection", po_InvalidMessageSection); - po_InvalidMessageOperation = - PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL); - PyModule_AddObject(mod, "InvalidMessageOperation", - po_InvalidMessageOperation); - po_InvalidMessageUDPSize = - PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL); - PyModule_AddObject(mod, "InvalidMessageUDPSize", po_InvalidMessageUDPSize); - po_DNSMessageBADVERS = PyErr_NewException("pydnspp.DNSMessageBADVERS", - NULL, NULL); - PyModule_AddObject(mod, "DNSMessageBADVERS", po_DNSMessageBADVERS); - - PyModule_AddObject(mod, "Message", - reinterpret_cast(&message_type)); - - - return (true); -} -} // end namespace internal - } // end python namespace } // end dns namespace } // end isc namespace diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc index 438dbb420d..d5c2311c95 100644 --- a/src/lib/dns/python/messagerenderer_python.cc +++ b/src/lib/dns/python/messagerenderer_python.cc @@ -239,25 +239,6 @@ PyTypeObject messagerenderer_type = { 0 // tp_version_tag }; -namespace internal { -bool initModulePart_MessageRenderer(PyObject* mod) { - if (PyType_Ready(&messagerenderer_type) < 0) { - return (false); - } - Py_INCREF(&messagerenderer_type); - - addClassVariable(messagerenderer_type, "CASE_INSENSITIVE", - Py_BuildValue("I", MessageRenderer::CASE_INSENSITIVE)); - addClassVariable(messagerenderer_type, "CASE_SENSITIVE", - Py_BuildValue("I", MessageRenderer::CASE_SENSITIVE)); - - PyModule_AddObject(mod, "MessageRenderer", - reinterpret_cast(&messagerenderer_type)); - - return (true); -} -} // end namespace internal - PyObject* createMessageRendererObject(const MessageRenderer& source) { // should we copy? can we? diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index 6e60ba9296..c7dcab313c 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -637,89 +637,6 @@ PyTypeObject name_type = { 0 // tp_version_tag }; -namespace internal { -bool -initModulePart_Name(PyObject* mod) { - // Add the classes to the module - // We initialize the static description object with PyType_Ready(), - // then add it to the module - - // - // NameComparisonResult - // - if (PyType_Ready(&name_comparison_result_type) < 0) { - return (false); - } - Py_INCREF(&name_comparison_result_type); - - // Add the enums to the module - po_NameRelation = Py_BuildValue("{i:s,i:s,i:s,i:s}", - NameComparisonResult::SUPERDOMAIN, "SUPERDOMAIN", - NameComparisonResult::SUBDOMAIN, "SUBDOMAIN", - NameComparisonResult::EQUAL, "EQUAL", - NameComparisonResult::COMMONANCESTOR, "COMMONANCESTOR"); - addClassVariable(name_comparison_result_type, "NameRelation", po_NameRelation); - - PyModule_AddObject(mod, "NameComparisonResult", - reinterpret_cast(&name_comparison_result_type)); - - // - // Name - // - - if (PyType_Ready(&name_type) < 0) { - return (false); - } - Py_INCREF(&name_type); - - // Add the constants to the module - addClassVariable(name_type, "MAX_WIRE", Py_BuildValue("I", Name::MAX_WIRE)); - addClassVariable(name_type, "MAX_LABELS", Py_BuildValue("I", Name::MAX_LABELS)); - addClassVariable(name_type, "MAX_LABELLEN", Py_BuildValue("I", Name::MAX_LABELLEN)); - addClassVariable(name_type, "MAX_COMPRESS_POINTER", Py_BuildValue("I", Name::MAX_COMPRESS_POINTER)); - addClassVariable(name_type, "COMPRESS_POINTER_MARK8", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK8)); - addClassVariable(name_type, "COMPRESS_POINTER_MARK16", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK16)); - - s_Name* root_name = PyObject_New(s_Name, &name_type); - root_name->cppobj = new Name(Name::ROOT_NAME()); - PyObject* po_ROOT_NAME = root_name; - addClassVariable(name_type, "ROOT_NAME", po_ROOT_NAME); - - PyModule_AddObject(mod, "Name", - reinterpret_cast(&name_type)); - - - // Add the exceptions to the module - po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL); - PyModule_AddObject(mod, "EmptyLabel", po_EmptyLabel); - - po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL); - PyModule_AddObject(mod, "TooLongName", po_TooLongName); - - po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL); - PyModule_AddObject(mod, "TooLongLabel", po_TooLongLabel); - - po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL); - PyModule_AddObject(mod, "BadLabelType", po_BadLabelType); - - po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL); - PyModule_AddObject(mod, "BadEscape", po_BadEscape); - - po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL, NULL); - PyModule_AddObject(mod, "IncompleteName", po_IncompleteName); - - po_InvalidBufferPosition = PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL); - PyModule_AddObject(mod, "InvalidBufferPosition", po_InvalidBufferPosition); - - // This one could have gone into the message_python.cc file, but is - // already needed here. - po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR", NULL, NULL); - PyModule_AddObject(mod, "DNSMessageFORMERR", po_DNSMessageFORMERR); - - return (true); -} -} // end namespace internal - PyObject* createNameObject(const Name& source) { NameContainer container(PyObject_New(s_Name, &name_type)); diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index ea76d99e22..4917dc2359 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -341,60 +341,6 @@ PyTypeObject opcode_type = { 0 // tp_version_tag }; -namespace internal { -bool -initModulePart_Opcode(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&opcode_type) < 0) { - return (false); - } - Py_INCREF(&opcode_type); - void* p = &opcode_type; - if (PyModule_AddObject(mod, "Opcode", static_cast(p)) != 0) { - Py_DECREF(&opcode_type); - return (false); - } - - addClassVariable(opcode_type, "QUERY_CODE", - Py_BuildValue("h", Opcode::QUERY_CODE)); - addClassVariable(opcode_type, "IQUERY_CODE", - Py_BuildValue("h", Opcode::IQUERY_CODE)); - addClassVariable(opcode_type, "STATUS_CODE", - Py_BuildValue("h", Opcode::STATUS_CODE)); - addClassVariable(opcode_type, "RESERVED3_CODE", - Py_BuildValue("h", Opcode::RESERVED3_CODE)); - addClassVariable(opcode_type, "NOTIFY_CODE", - Py_BuildValue("h", Opcode::NOTIFY_CODE)); - addClassVariable(opcode_type, "UPDATE_CODE", - Py_BuildValue("h", Opcode::UPDATE_CODE)); - addClassVariable(opcode_type, "RESERVED6_CODE", - Py_BuildValue("h", Opcode::RESERVED6_CODE)); - addClassVariable(opcode_type, "RESERVED7_CODE", - Py_BuildValue("h", Opcode::RESERVED7_CODE)); - addClassVariable(opcode_type, "RESERVED8_CODE", - Py_BuildValue("h", Opcode::RESERVED8_CODE)); - addClassVariable(opcode_type, "RESERVED9_CODE", - Py_BuildValue("h", Opcode::RESERVED9_CODE)); - addClassVariable(opcode_type, "RESERVED10_CODE", - Py_BuildValue("h", Opcode::RESERVED10_CODE)); - addClassVariable(opcode_type, "RESERVED11_CODE", - Py_BuildValue("h", Opcode::RESERVED11_CODE)); - addClassVariable(opcode_type, "RESERVED12_CODE", - Py_BuildValue("h", Opcode::RESERVED12_CODE)); - addClassVariable(opcode_type, "RESERVED13_CODE", - Py_BuildValue("h", Opcode::RESERVED13_CODE)); - addClassVariable(opcode_type, "RESERVED14_CODE", - Py_BuildValue("h", Opcode::RESERVED14_CODE)); - addClassVariable(opcode_type, "RESERVED15_CODE", - Py_BuildValue("h", Opcode::RESERVED15_CODE)); - - return (true); -} -} // end namespace internal - - PyObject* createOpcodeObject(const Opcode& source) { OpcodeContainer container(PyObject_New(s_Opcode, &opcode_type)); diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 4699e55384..8c23725cf7 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -21,52 +21,677 @@ // name initModulePart_, and return true/false instead of // NULL/*mod // +// The big init function is split up into a separate initModulePart function +// for each class we add. #define PY_SSIZE_T_CLEAN #include #include +#include + #include "pydnspp_common.h" -/* Note that we do forward declarations of the initialization functions here, - * and these are not defined in headers (since they are not to be used in any - * other place */ -namespace isc { -namespace dns { -namespace python { -namespace internal { - -bool initModulePart_EDNS(PyObject* mod); -bool initModulePart_Message(PyObject* mod); -bool initModulePart_MessageRenderer(PyObject* mod); -bool initModulePart_Name(PyObject* mod); -bool initModulePart_Opcode(PyObject* mod); -bool initModulePart_Question(PyObject* mod); -bool initModulePart_Rcode(PyObject* mod); -bool initModulePart_Rdata(PyObject* mod); -bool initModulePart_RRClass(PyObject* mod); -bool initModulePart_RRset(PyObject* mod); -bool initModulePart_RRTTL(PyObject* mod); -bool initModulePart_RRType(PyObject* mod); -bool initModulePart_TSIGError(PyObject* mod); -bool initModulePart_TSIGKey(PyObject* mod); -bool initModulePart_TSIGKeyRing(PyObject* mod); -bool initModulePart_TSIGContext(PyObject* mod); -bool initModulePart_TSIG(PyObject* mod); -bool initModulePart_TSIGRecord(PyObject* mod); - -} -} // namespace python -} // namespace dns -} // namespace isc +#include "edns_python.h" +#include "message_python.h" +#include "messagerenderer_python.h" +#include "name_python.h" +#include "opcode_python.h" +#include "pydnspp_common.h" +#include "pydnspp_towire.h" +#include "question_python.h" +#include "rcode_python.h" +#include "rdata_python.h" +#include "rrclass_python.h" +#include "rrset_python.h" +#include "rrttl_python.h" +#include "rrtype_python.h" +#include "tsigerror_python.h" +#include "tsigkey_python.h" +#include "tsig_python.h" +#include "tsig_rdata_python.h" +#include "tsigrecord_python.h" +using namespace isc::dns; using namespace isc::dns::python; -using namespace isc::dns::python::internal; +using namespace isc::util::python; -// -// Definition of the module -// namespace { + +bool +initModulePart_EDNS(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + // + // After the type has been initialized, we initialize any exceptions + // that are defined in the wrapper for this class, and add constants + // to the type, if any + + if (PyType_Ready(&edns_type) < 0) { + return (false); + } + Py_INCREF(&edns_type); + void* p = &edns_type; + PyModule_AddObject(mod, "EDNS", static_cast(p)); + + addClassVariable(edns_type, "SUPPORTED_VERSION", + Py_BuildValue("B", EDNS::SUPPORTED_VERSION)); + + return (true); +} + +bool +initModulePart_Message(PyObject* mod) { + if (PyType_Ready(&message_type) < 0) { + return (false); + } + Py_INCREF(&message_type); + + // Class variables + // These are added to the tp_dict of the type object + // + addClassVariable(message_type, "PARSE", + Py_BuildValue("I", Message::PARSE)); + addClassVariable(message_type, "RENDER", + Py_BuildValue("I", Message::RENDER)); + + addClassVariable(message_type, "HEADERFLAG_QR", + Py_BuildValue("I", Message::HEADERFLAG_QR)); + addClassVariable(message_type, "HEADERFLAG_AA", + Py_BuildValue("I", Message::HEADERFLAG_AA)); + addClassVariable(message_type, "HEADERFLAG_TC", + Py_BuildValue("I", Message::HEADERFLAG_TC)); + addClassVariable(message_type, "HEADERFLAG_RD", + Py_BuildValue("I", Message::HEADERFLAG_RD)); + addClassVariable(message_type, "HEADERFLAG_RA", + Py_BuildValue("I", Message::HEADERFLAG_RA)); + addClassVariable(message_type, "HEADERFLAG_AD", + Py_BuildValue("I", Message::HEADERFLAG_AD)); + addClassVariable(message_type, "HEADERFLAG_CD", + Py_BuildValue("I", Message::HEADERFLAG_CD)); + + addClassVariable(message_type, "SECTION_QUESTION", + Py_BuildValue("I", Message::SECTION_QUESTION)); + addClassVariable(message_type, "SECTION_ANSWER", + Py_BuildValue("I", Message::SECTION_ANSWER)); + addClassVariable(message_type, "SECTION_AUTHORITY", + Py_BuildValue("I", Message::SECTION_AUTHORITY)); + addClassVariable(message_type, "SECTION_ADDITIONAL", + Py_BuildValue("I", Message::SECTION_ADDITIONAL)); + + addClassVariable(message_type, "DEFAULT_MAX_UDPSIZE", + Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE)); + + /* Class-specific exceptions */ + po_MessageTooShort = PyErr_NewException("pydnspp.MessageTooShort", NULL, + NULL); + PyModule_AddObject(mod, "MessageTooShort", po_MessageTooShort); + po_InvalidMessageSection = + PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL); + PyModule_AddObject(mod, "InvalidMessageSection", po_InvalidMessageSection); + po_InvalidMessageOperation = + PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL); + PyModule_AddObject(mod, "InvalidMessageOperation", + po_InvalidMessageOperation); + po_InvalidMessageUDPSize = + PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL); + PyModule_AddObject(mod, "InvalidMessageUDPSize", po_InvalidMessageUDPSize); + po_DNSMessageBADVERS = PyErr_NewException("pydnspp.DNSMessageBADVERS", + NULL, NULL); + PyModule_AddObject(mod, "DNSMessageBADVERS", po_DNSMessageBADVERS); + + PyModule_AddObject(mod, "Message", + reinterpret_cast(&message_type)); + + + return (true); +} + +bool +initModulePart_MessageRenderer(PyObject* mod) { + if (PyType_Ready(&messagerenderer_type) < 0) { + return (false); + } + Py_INCREF(&messagerenderer_type); + + addClassVariable(messagerenderer_type, "CASE_INSENSITIVE", + Py_BuildValue("I", MessageRenderer::CASE_INSENSITIVE)); + addClassVariable(messagerenderer_type, "CASE_SENSITIVE", + Py_BuildValue("I", MessageRenderer::CASE_SENSITIVE)); + + PyModule_AddObject(mod, "MessageRenderer", + reinterpret_cast(&messagerenderer_type)); + + return (true); +} + +bool +initModulePart_Name(PyObject* mod) { + + // + // NameComparisonResult + // + if (PyType_Ready(&name_comparison_result_type) < 0) { + return (false); + } + Py_INCREF(&name_comparison_result_type); + + // Add the enums to the module + po_NameRelation = Py_BuildValue("{i:s,i:s,i:s,i:s}", + NameComparisonResult::SUPERDOMAIN, "SUPERDOMAIN", + NameComparisonResult::SUBDOMAIN, "SUBDOMAIN", + NameComparisonResult::EQUAL, "EQUAL", + NameComparisonResult::COMMONANCESTOR, "COMMONANCESTOR"); + addClassVariable(name_comparison_result_type, "NameRelation", + po_NameRelation); + + PyModule_AddObject(mod, "NameComparisonResult", + reinterpret_cast(&name_comparison_result_type)); + + // + // Name + // + + if (PyType_Ready(&name_type) < 0) { + return (false); + } + Py_INCREF(&name_type); + + // Add the constants to the module + addClassVariable(name_type, "MAX_WIRE", + Py_BuildValue("I", Name::MAX_WIRE)); + addClassVariable(name_type, "MAX_LABELS", + Py_BuildValue("I", Name::MAX_LABELS)); + addClassVariable(name_type, "MAX_LABELLEN", + Py_BuildValue("I", Name::MAX_LABELLEN)); + addClassVariable(name_type, "MAX_COMPRESS_POINTER", + Py_BuildValue("I", Name::MAX_COMPRESS_POINTER)); + addClassVariable(name_type, "COMPRESS_POINTER_MARK8", + Py_BuildValue("I", Name::COMPRESS_POINTER_MARK8)); + addClassVariable(name_type, "COMPRESS_POINTER_MARK16", + Py_BuildValue("I", Name::COMPRESS_POINTER_MARK16)); + + addClassVariable(name_type, "ROOT_NAME", + createNameObject(Name::ROOT_NAME())); + + PyModule_AddObject(mod, "Name", + reinterpret_cast(&name_type)); + + + // Add the exceptions to the module + po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL); + PyModule_AddObject(mod, "EmptyLabel", po_EmptyLabel); + + po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL); + PyModule_AddObject(mod, "TooLongName", po_TooLongName); + + po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL); + PyModule_AddObject(mod, "TooLongLabel", po_TooLongLabel); + + po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL); + PyModule_AddObject(mod, "BadLabelType", po_BadLabelType); + + po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL); + PyModule_AddObject(mod, "BadEscape", po_BadEscape); + + po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL, NULL); + PyModule_AddObject(mod, "IncompleteName", po_IncompleteName); + + po_InvalidBufferPosition = + PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL); + PyModule_AddObject(mod, "InvalidBufferPosition", po_InvalidBufferPosition); + + // This one could have gone into the message_python.cc file, but is + // already needed here. + po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR", + NULL, NULL); + PyModule_AddObject(mod, "DNSMessageFORMERR", po_DNSMessageFORMERR); + + return (true); +} + +bool +initModulePart_Opcode(PyObject* mod) { + if (PyType_Ready(&opcode_type) < 0) { + return (false); + } + Py_INCREF(&opcode_type); + void* p = &opcode_type; + if (PyModule_AddObject(mod, "Opcode", static_cast(p)) != 0) { + Py_DECREF(&opcode_type); + return (false); + } + + addClassVariable(opcode_type, "QUERY_CODE", + Py_BuildValue("h", Opcode::QUERY_CODE)); + addClassVariable(opcode_type, "IQUERY_CODE", + Py_BuildValue("h", Opcode::IQUERY_CODE)); + addClassVariable(opcode_type, "STATUS_CODE", + Py_BuildValue("h", Opcode::STATUS_CODE)); + addClassVariable(opcode_type, "RESERVED3_CODE", + Py_BuildValue("h", Opcode::RESERVED3_CODE)); + addClassVariable(opcode_type, "NOTIFY_CODE", + Py_BuildValue("h", Opcode::NOTIFY_CODE)); + addClassVariable(opcode_type, "UPDATE_CODE", + Py_BuildValue("h", Opcode::UPDATE_CODE)); + addClassVariable(opcode_type, "RESERVED6_CODE", + Py_BuildValue("h", Opcode::RESERVED6_CODE)); + addClassVariable(opcode_type, "RESERVED7_CODE", + Py_BuildValue("h", Opcode::RESERVED7_CODE)); + addClassVariable(opcode_type, "RESERVED8_CODE", + Py_BuildValue("h", Opcode::RESERVED8_CODE)); + addClassVariable(opcode_type, "RESERVED9_CODE", + Py_BuildValue("h", Opcode::RESERVED9_CODE)); + addClassVariable(opcode_type, "RESERVED10_CODE", + Py_BuildValue("h", Opcode::RESERVED10_CODE)); + addClassVariable(opcode_type, "RESERVED11_CODE", + Py_BuildValue("h", Opcode::RESERVED11_CODE)); + addClassVariable(opcode_type, "RESERVED12_CODE", + Py_BuildValue("h", Opcode::RESERVED12_CODE)); + addClassVariable(opcode_type, "RESERVED13_CODE", + Py_BuildValue("h", Opcode::RESERVED13_CODE)); + addClassVariable(opcode_type, "RESERVED14_CODE", + Py_BuildValue("h", Opcode::RESERVED14_CODE)); + addClassVariable(opcode_type, "RESERVED15_CODE", + Py_BuildValue("h", Opcode::RESERVED15_CODE)); + + return (true); +} + +bool +initModulePart_Question(PyObject* mod) { + if (PyType_Ready(&question_type) < 0) { + return (false); + } + Py_INCREF(&question_type); + PyModule_AddObject(mod, "Question", + reinterpret_cast(&question_type)); + + return (true); +} + +bool +initModulePart_Rcode(PyObject* mod) { + if (PyType_Ready(&rcode_type) < 0) { + return (false); + } + Py_INCREF(&rcode_type); + void* p = &rcode_type; + if (PyModule_AddObject(mod, "Rcode", static_cast(p)) != 0) { + Py_DECREF(&rcode_type); + return (false); + } + + addClassVariable(rcode_type, "NOERROR_CODE", + Py_BuildValue("h", Rcode::NOERROR_CODE)); + addClassVariable(rcode_type, "FORMERR_CODE", + Py_BuildValue("h", Rcode::FORMERR_CODE)); + addClassVariable(rcode_type, "SERVFAIL_CODE", + Py_BuildValue("h", Rcode::SERVFAIL_CODE)); + addClassVariable(rcode_type, "NXDOMAIN_CODE", + Py_BuildValue("h", Rcode::NXDOMAIN_CODE)); + addClassVariable(rcode_type, "NOTIMP_CODE", + Py_BuildValue("h", Rcode::NOTIMP_CODE)); + addClassVariable(rcode_type, "REFUSED_CODE", + Py_BuildValue("h", Rcode::REFUSED_CODE)); + addClassVariable(rcode_type, "YXDOMAIN_CODE", + Py_BuildValue("h", Rcode::YXDOMAIN_CODE)); + addClassVariable(rcode_type, "YXRRSET_CODE", + Py_BuildValue("h", Rcode::YXRRSET_CODE)); + addClassVariable(rcode_type, "NXRRSET_CODE", + Py_BuildValue("h", Rcode::NXRRSET_CODE)); + addClassVariable(rcode_type, "NOTAUTH_CODE", + Py_BuildValue("h", Rcode::NOTAUTH_CODE)); + addClassVariable(rcode_type, "NOTZONE_CODE", + Py_BuildValue("h", Rcode::NOTZONE_CODE)); + addClassVariable(rcode_type, "RESERVED11_CODE", + Py_BuildValue("h", Rcode::RESERVED11_CODE)); + addClassVariable(rcode_type, "RESERVED12_CODE", + Py_BuildValue("h", Rcode::RESERVED12_CODE)); + addClassVariable(rcode_type, "RESERVED13_CODE", + Py_BuildValue("h", Rcode::RESERVED13_CODE)); + addClassVariable(rcode_type, "RESERVED14_CODE", + Py_BuildValue("h", Rcode::RESERVED14_CODE)); + addClassVariable(rcode_type, "RESERVED15_CODE", + Py_BuildValue("h", Rcode::RESERVED15_CODE)); + addClassVariable(rcode_type, "BADVERS_CODE", + Py_BuildValue("h", Rcode::BADVERS_CODE)); + + return (true); +} + +bool +initModulePart_Rdata(PyObject* mod) { + if (PyType_Ready(&rdata_type) < 0) { + return (false); + } + Py_INCREF(&rdata_type); + PyModule_AddObject(mod, "Rdata", + reinterpret_cast(&rdata_type)); + + // Add the exceptions to the class + po_InvalidRdataLength = PyErr_NewException("pydnspp.InvalidRdataLength", + NULL, NULL); + PyModule_AddObject(mod, "InvalidRdataLength", po_InvalidRdataLength); + + po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText", + NULL, NULL); + PyModule_AddObject(mod, "InvalidRdataText", po_InvalidRdataText); + + po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong", + NULL, NULL); + PyModule_AddObject(mod, "CharStringTooLong", po_CharStringTooLong); + + + return (true); +} + +bool +initModulePart_RRClass(PyObject* mod) { + po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass", + NULL, NULL); + Py_INCREF(po_InvalidRRClass); + PyModule_AddObject(mod, "InvalidRRClass", po_InvalidRRClass); + po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass", + NULL, NULL); + Py_INCREF(po_IncompleteRRClass); + PyModule_AddObject(mod, "IncompleteRRClass", po_IncompleteRRClass); + + if (PyType_Ready(&rrclass_type) < 0) { + return (false); + } + Py_INCREF(&rrclass_type); + PyModule_AddObject(mod, "RRClass", + reinterpret_cast(&rrclass_type)); + + return (true); +} + +bool +initModulePart_RRset(PyObject* mod) { + po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL); + PyModule_AddObject(mod, "EmptyRRset", po_EmptyRRset); + + // NameComparisonResult + if (PyType_Ready(&rrset_type) < 0) { + return (false); + } + Py_INCREF(&rrset_type); + PyModule_AddObject(mod, "RRset", + reinterpret_cast(&rrset_type)); + + return (true); +} + +bool +initModulePart_RRTTL(PyObject* mod) { + po_InvalidRRTTL = PyErr_NewException("pydnspp.InvalidRRTTL", NULL, NULL); + PyModule_AddObject(mod, "InvalidRRTTL", po_InvalidRRTTL); + po_IncompleteRRTTL = PyErr_NewException("pydnspp.IncompleteRRTTL", + NULL, NULL); + PyModule_AddObject(mod, "IncompleteRRTTL", po_IncompleteRRTTL); + + if (PyType_Ready(&rrttl_type) < 0) { + return (false); + } + Py_INCREF(&rrttl_type); + PyModule_AddObject(mod, "RRTTL", + reinterpret_cast(&rrttl_type)); + + return (true); +} + +bool +initModulePart_RRType(PyObject* mod) { + // Add the exceptions to the module + po_InvalidRRType = PyErr_NewException("pydnspp.InvalidRRType", NULL, NULL); + PyModule_AddObject(mod, "InvalidRRType", po_InvalidRRType); + po_IncompleteRRType = PyErr_NewException("pydnspp.IncompleteRRType", + NULL, NULL); + PyModule_AddObject(mod, "IncompleteRRType", po_IncompleteRRType); + + if (PyType_Ready(&rrtype_type) < 0) { + return (false); + } + Py_INCREF(&rrtype_type); + PyModule_AddObject(mod, "RRType", + reinterpret_cast(&rrtype_type)); + + return (true); +} + +bool +initModulePart_TSIGError(PyObject* mod) { + if (PyType_Ready(&tsigerror_type) < 0) { + return (false); + } + void* p = &tsigerror_type; + if (PyModule_AddObject(mod, "TSIGError", static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&tsigerror_type); + + try { + // Constant class variables + // Error codes (bare values) + installClassVariable(tsigerror_type, "BAD_SIG_CODE", + Py_BuildValue("H", TSIGError::BAD_SIG_CODE)); + installClassVariable(tsigerror_type, "BAD_KEY_CODE", + Py_BuildValue("H", TSIGError::BAD_KEY_CODE)); + installClassVariable(tsigerror_type, "BAD_TIME_CODE", + Py_BuildValue("H", TSIGError::BAD_TIME_CODE)); + + // Error codes (constant objects) + installClassVariable(tsigerror_type, "NOERROR", + createTSIGErrorObject(TSIGError::NOERROR())); + installClassVariable(tsigerror_type, "FORMERR", + createTSIGErrorObject(TSIGError::FORMERR())); + installClassVariable(tsigerror_type, "SERVFAIL", + createTSIGErrorObject(TSIGError::SERVFAIL())); + installClassVariable(tsigerror_type, "NXDOMAIN", + createTSIGErrorObject(TSIGError::NXDOMAIN())); + installClassVariable(tsigerror_type, "NOTIMP", + createTSIGErrorObject(TSIGError::NOTIMP())); + installClassVariable(tsigerror_type, "REFUSED", + createTSIGErrorObject(TSIGError::REFUSED())); + installClassVariable(tsigerror_type, "YXDOMAIN", + createTSIGErrorObject(TSIGError::YXDOMAIN())); + installClassVariable(tsigerror_type, "YXRRSET", + createTSIGErrorObject(TSIGError::YXRRSET())); + installClassVariable(tsigerror_type, "NXRRSET", + createTSIGErrorObject(TSIGError::NXRRSET())); + installClassVariable(tsigerror_type, "NOTAUTH", + createTSIGErrorObject(TSIGError::NOTAUTH())); + installClassVariable(tsigerror_type, "NOTZONE", + createTSIGErrorObject(TSIGError::NOTZONE())); + installClassVariable(tsigerror_type, "RESERVED11", + createTSIGErrorObject(TSIGError::RESERVED11())); + installClassVariable(tsigerror_type, "RESERVED12", + createTSIGErrorObject(TSIGError::RESERVED12())); + installClassVariable(tsigerror_type, "RESERVED13", + createTSIGErrorObject(TSIGError::RESERVED13())); + installClassVariable(tsigerror_type, "RESERVED14", + createTSIGErrorObject(TSIGError::RESERVED14())); + installClassVariable(tsigerror_type, "RESERVED15", + createTSIGErrorObject(TSIGError::RESERVED15())); + installClassVariable(tsigerror_type, "BAD_SIG", + createTSIGErrorObject(TSIGError::BAD_SIG())); + installClassVariable(tsigerror_type, "BAD_KEY", + createTSIGErrorObject(TSIGError::BAD_KEY())); + installClassVariable(tsigerror_type, "BAD_TIME", + createTSIGErrorObject(TSIGError::BAD_TIME())); + } catch (const std::exception& ex) { + const std::string ex_what = + "Unexpected failure in TSIGError initialization: " + + std::string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (false); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure in TSIGError initialization"); + return (false); + } + + return (true); +} + +bool +initModulePart_TSIGKey(PyObject* mod) { + if (PyType_Ready(&tsigkey_type) < 0) { + return (false); + } + void* p = &tsigkey_type; + if (PyModule_AddObject(mod, "TSIGKey", static_cast(p)) != 0) { + return (false); + } + Py_INCREF(&tsigkey_type); + + try { + // Constant class variables + installClassVariable(tsigkey_type, "HMACMD5_NAME", + createNameObject(TSIGKey::HMACMD5_NAME())); + installClassVariable(tsigkey_type, "HMACSHA1_NAME", + createNameObject(TSIGKey::HMACSHA1_NAME())); + installClassVariable(tsigkey_type, "HMACSHA256_NAME", + createNameObject(TSIGKey::HMACSHA256_NAME())); + installClassVariable(tsigkey_type, "HMACSHA224_NAME", + createNameObject(TSIGKey::HMACSHA224_NAME())); + installClassVariable(tsigkey_type, "HMACSHA384_NAME", + createNameObject(TSIGKey::HMACSHA384_NAME())); + installClassVariable(tsigkey_type, "HMACSHA512_NAME", + createNameObject(TSIGKey::HMACSHA512_NAME())); + } catch (const std::exception& ex) { + const std::string ex_what = + "Unexpected failure in TSIGKey initialization: " + + std::string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (false); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure in TSIGKey initialization"); + return (false); + } + + return (true); +} + +bool +initModulePart_TSIGKeyRing(PyObject* mod) { + if (PyType_Ready(&tsigkeyring_type) < 0) { + return (false); + } + Py_INCREF(&tsigkeyring_type); + void* p = &tsigkeyring_type; + if (PyModule_AddObject(mod, "TSIGKeyRing", + static_cast(p)) != 0) { + Py_DECREF(&tsigkeyring_type); + return (false); + } + + addClassVariable(tsigkeyring_type, "SUCCESS", + Py_BuildValue("I", TSIGKeyRing::SUCCESS)); + addClassVariable(tsigkeyring_type, "EXIST", + Py_BuildValue("I", TSIGKeyRing::EXIST)); + addClassVariable(tsigkeyring_type, "NOTFOUND", + Py_BuildValue("I", TSIGKeyRing::NOTFOUND)); + + return (true); +} + +bool +initModulePart_TSIGContext(PyObject* mod) { + if (PyType_Ready(&tsigcontext_type) < 0) { + return (false); + } + void* p = &tsigcontext_type; + if (PyModule_AddObject(mod, "TSIGContext", + static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&tsigcontext_type); + + try { + // Class specific exceptions + po_TSIGContextError = PyErr_NewException("pydnspp.TSIGContextError", + po_IscException, NULL); + PyObjectContainer(po_TSIGContextError).installToModule( + mod, "TSIGContextError"); + + // Constant class variables + installClassVariable(tsigcontext_type, "STATE_INIT", + Py_BuildValue("I", TSIGContext::INIT)); + installClassVariable(tsigcontext_type, "STATE_SENT_REQUEST", + Py_BuildValue("I", TSIGContext::SENT_REQUEST)); + installClassVariable(tsigcontext_type, "STATE_RECEIVED_REQUEST", + Py_BuildValue("I", TSIGContext::RECEIVED_REQUEST)); + installClassVariable(tsigcontext_type, "STATE_SENT_RESPONSE", + Py_BuildValue("I", TSIGContext::SENT_RESPONSE)); + installClassVariable(tsigcontext_type, "STATE_VERIFIED_RESPONSE", + Py_BuildValue("I", + TSIGContext::VERIFIED_RESPONSE)); + + installClassVariable(tsigcontext_type, "DEFAULT_FUDGE", + Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE)); + } catch (const std::exception& ex) { + const std::string ex_what = + "Unexpected failure in TSIGContext initialization: " + + std::string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (false); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure in TSIGContext initialization"); + return (false); + } + + return (true); +} + +bool +initModulePart_TSIG(PyObject* mod) { + if (PyType_Ready(&tsig_type) < 0) { + return (false); + } + void* p = &tsig_type; + if (PyModule_AddObject(mod, "TSIG", static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&tsig_type); + + return (true); +} + +bool +initModulePart_TSIGRecord(PyObject* mod) { + if (PyType_Ready(&tsigrecord_type) < 0) { + return (false); + } + void* p = &tsigrecord_type; + if (PyModule_AddObject(mod, "TSIGRecord", static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&tsigrecord_type); + + try { + // Constant class variables + installClassVariable(tsigrecord_type, "TSIG_TTL", + Py_BuildValue("I", 0)); + } catch (const std::exception& ex) { + const std::string ex_what = + "Unexpected failure in TSIGRecord initialization: " + + std::string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (false); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure in TSIGRecord initialization"); + return (false); + } + + return (true); +} + PyModuleDef pydnspp = { { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, "pydnspp", diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index d59577297a..f9d3103194 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -256,25 +256,6 @@ PyTypeObject question_type = { 0 // tp_version_tag }; -namespace internal { -bool -initModulePart_Question(PyObject* mod) { - // Add the exceptions to the module - - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&question_type) < 0) { - return (false); - } - Py_INCREF(&question_type); - PyModule_AddObject(mod, "Question", - reinterpret_cast(&question_type)); - - return (true); -} -} // end namespace internal - PyObject* createQuestionObject(const Question& source) { s_Question* question = diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc index cc7948351c..eb5bbeddcf 100644 --- a/src/lib/dns/python/rcode_python.cc +++ b/src/lib/dns/python/rcode_python.cc @@ -381,61 +381,6 @@ PyTypeObject rcode_type = { 0 // tp_version_tag }; -namespace internal { -bool -initModulePart_Rcode(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&rcode_type) < 0) { - return (false); - } - Py_INCREF(&rcode_type); - void* p = &rcode_type; - if (PyModule_AddObject(mod, "Rcode", static_cast(p)) != 0) { - Py_DECREF(&rcode_type); - return (false); - } - - addClassVariable(rcode_type, "NOERROR_CODE", - Py_BuildValue("h", Rcode::NOERROR_CODE)); - addClassVariable(rcode_type, "FORMERR_CODE", - Py_BuildValue("h", Rcode::FORMERR_CODE)); - addClassVariable(rcode_type, "SERVFAIL_CODE", - Py_BuildValue("h", Rcode::SERVFAIL_CODE)); - addClassVariable(rcode_type, "NXDOMAIN_CODE", - Py_BuildValue("h", Rcode::NXDOMAIN_CODE)); - addClassVariable(rcode_type, "NOTIMP_CODE", - Py_BuildValue("h", Rcode::NOTIMP_CODE)); - addClassVariable(rcode_type, "REFUSED_CODE", - Py_BuildValue("h", Rcode::REFUSED_CODE)); - addClassVariable(rcode_type, "YXDOMAIN_CODE", - Py_BuildValue("h", Rcode::YXDOMAIN_CODE)); - addClassVariable(rcode_type, "YXRRSET_CODE", - Py_BuildValue("h", Rcode::YXRRSET_CODE)); - addClassVariable(rcode_type, "NXRRSET_CODE", - Py_BuildValue("h", Rcode::NXRRSET_CODE)); - addClassVariable(rcode_type, "NOTAUTH_CODE", - Py_BuildValue("h", Rcode::NOTAUTH_CODE)); - addClassVariable(rcode_type, "NOTZONE_CODE", - Py_BuildValue("h", Rcode::NOTZONE_CODE)); - addClassVariable(rcode_type, "RESERVED11_CODE", - Py_BuildValue("h", Rcode::RESERVED11_CODE)); - addClassVariable(rcode_type, "RESERVED12_CODE", - Py_BuildValue("h", Rcode::RESERVED12_CODE)); - addClassVariable(rcode_type, "RESERVED13_CODE", - Py_BuildValue("h", Rcode::RESERVED13_CODE)); - addClassVariable(rcode_type, "RESERVED14_CODE", - Py_BuildValue("h", Rcode::RESERVED14_CODE)); - addClassVariable(rcode_type, "RESERVED15_CODE", - Py_BuildValue("h", Rcode::RESERVED15_CODE)); - addClassVariable(rcode_type, "BADVERS_CODE", - Py_BuildValue("h", Rcode::BADVERS_CODE)); - - return (true); -} -} // end namespace internal - PyObject* createRcodeObject(const Rcode& source) { RcodeContainer container(PyObject_New(s_Rcode, &rcode_type)); diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index a82fb97698..9a8caf52cc 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -203,11 +203,11 @@ namespace python { // // Declaration of the custom exceptions // Initialization and addition of these go in the initModulePart -// function at the end of this file +// function in pydnspp // -static PyObject* po_InvalidRdataLength; -static PyObject* po_InvalidRdataText; -static PyObject* po_CharStringTooLong; +PyObject* po_InvalidRdataLength; +PyObject* po_InvalidRdataText; +PyObject* po_CharStringTooLong; // This defines the complete type for reflection in python and // parsing of PyObject* to s_Rdata @@ -263,35 +263,6 @@ PyTypeObject rdata_type = { 0 // tp_version_tag }; -namespace internal { -// Module Initialization, all statics are initialized here -bool -initModulePart_Rdata(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&rdata_type) < 0) { - return (false); - } - Py_INCREF(&rdata_type); - PyModule_AddObject(mod, "Rdata", - reinterpret_cast(&rdata_type)); - - // Add the exceptions to the class - po_InvalidRdataLength = PyErr_NewException("pydnspp.InvalidRdataLength", NULL, NULL); - PyModule_AddObject(mod, "InvalidRdataLength", po_InvalidRdataLength); - - po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText", NULL, NULL); - PyModule_AddObject(mod, "InvalidRdataText", po_InvalidRdataText); - - po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong", NULL, NULL); - PyModule_AddObject(mod, "CharStringTooLong", po_CharStringTooLong); - - - return (true); -} -} // end namespace internal - PyObject* createRdataObject(ConstRdataPtr source) { s_Rdata* py_rdata = diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h index d3e110d351..c7ddd57a6d 100644 --- a/src/lib/dns/python/rdata_python.h +++ b/src/lib/dns/python/rdata_python.h @@ -23,7 +23,9 @@ namespace isc { namespace dns { namespace python { -extern PyObject* po_EmptyRdata; +extern PyObject* po_InvalidRdataLength; +extern PyObject* po_InvalidRdataText; +extern PyObject* po_CharStringTooLong; extern PyTypeObject rdata_type; diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index 423ab4207f..abdcc84de5 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -273,7 +273,7 @@ namespace python { // // Declaration of the custom exceptions // Initialization and addition of these go in the initModulePart -// function at the end of this file +// function in pydnspp.cc // PyObject* po_InvalidRRClass; PyObject* po_IncompleteRRClass; @@ -335,33 +335,6 @@ PyTypeObject rrclass_type = { 0 // tp_version_tag }; - -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_RRClass(PyObject* mod) { - // Add the exceptions to the module - po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass", NULL, NULL); - Py_INCREF(po_InvalidRRClass); - PyModule_AddObject(mod, "InvalidRRClass", po_InvalidRRClass); - po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass", NULL, NULL); - Py_INCREF(po_IncompleteRRClass); - PyModule_AddObject(mod, "IncompleteRRClass", po_IncompleteRRClass); - - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&rrclass_type) < 0) { - return (false); - } - Py_INCREF(&rrclass_type); - PyModule_AddObject(mod, "RRClass", - reinterpret_cast(&rrclass_type)); - - return (true); -} -} // end namespace internal - PyObject* createRRClassObject(const RRClass& source) { RRClassContainer container(PyObject_New(s_RRClass, &rrclass_type)); diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index 4ae1baee50..cd1edf8fda 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -345,36 +345,6 @@ PyTypeObject rrset_type = { 0 // tp_version_tag }; - - -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_RRset(PyObject* mod) { - // Add the exceptions to the module - po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL); - PyModule_AddObject(mod, "EmptyRRset", po_EmptyRRset); - - // Add the enums to the module - - // Add the constants to the module - - // Add the classes to the module - // We initialize the static description object with PyType_Ready(), - // then add it to the module - - // NameComparisonResult - if (PyType_Ready(&rrset_type) < 0) { - return (false); - } - Py_INCREF(&rrset_type); - PyModule_AddObject(mod, "RRset", - reinterpret_cast(&rrset_type)); - - return (true); -} -} // end namespace internal - PyObject* createRRsetObject(const RRset& source) { diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index 84610f83e7..99a6f856ef 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -228,7 +228,7 @@ namespace python { // // Declaration of the custom exceptions // Initialization and addition of these go in the initModulePart -// function at the end of this file +// function in pydnspp.cc // PyObject* po_InvalidRRTTL; PyObject* po_IncompleteRRTTL; @@ -291,30 +291,6 @@ PyTypeObject rrttl_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_RRTTL(PyObject* mod) { - // Add the exceptions to the module - po_InvalidRRTTL = PyErr_NewException("pydnspp.InvalidRRTTL", NULL, NULL); - PyModule_AddObject(mod, "InvalidRRTTL", po_InvalidRRTTL); - po_IncompleteRRTTL = PyErr_NewException("pydnspp.IncompleteRRTTL", NULL, NULL); - PyModule_AddObject(mod, "IncompleteRRTTL", po_IncompleteRRTTL); - - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&rrttl_type) < 0) { - return (false); - } - Py_INCREF(&rrttl_type); - PyModule_AddObject(mod, "RRTTL", - reinterpret_cast(&rrttl_type)); - - return (true); -} -} // end namespace internal - PyObject* createRRTTLObject(const RRTTL& source) { RRTTLContainer container(PyObject_New(s_RRTTL, &rrttl_type)); diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index c85bf154ad..0cd88fb455 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -433,29 +433,6 @@ PyTypeObject rrtype_type = { 0 // tp_version_tag }; -namespace internal { -bool -initModulePart_RRType(PyObject* mod) { - // Add the exceptions to the module - po_InvalidRRType = PyErr_NewException("pydnspp.InvalidRRType", NULL, NULL); - PyModule_AddObject(mod, "InvalidRRType", po_InvalidRRType); - po_IncompleteRRType = PyErr_NewException("pydnspp.IncompleteRRType", NULL, NULL); - PyModule_AddObject(mod, "IncompleteRRType", po_IncompleteRRType); - - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&rrtype_type) < 0) { - return (false); - } - Py_INCREF(&rrtype_type); - PyModule_AddObject(mod, "RRType", - reinterpret_cast(&rrtype_type)); - - return (true); -} -} // end namespace internal - PyObject* createRRTypeObject(const RRType& source) { RRTypeContainer container(PyObject_New(s_RRType, &rrtype_type)); diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc index e27acde041..7e1fa965d7 100644 --- a/src/lib/dns/python/tsig_python.cc +++ b/src/lib/dns/python/tsig_python.cc @@ -302,61 +302,6 @@ PyTypeObject tsigcontext_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_TSIGContext(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&tsigcontext_type) < 0) { - return (false); - } - void* p = &tsigcontext_type; - if (PyModule_AddObject(mod, "TSIGContext", - static_cast(p)) < 0) { - return (false); - } - Py_INCREF(&tsigcontext_type); - - try { - // Class specific exceptions - po_TSIGContextError = PyErr_NewException("pydnspp.TSIGContextError", - po_IscException, NULL); - PyObjectContainer(po_TSIGContextError).installToModule( - mod, "TSIGContextError"); - - // Constant class variables - installClassVariable(tsigcontext_type, "STATE_INIT", - Py_BuildValue("I", TSIGContext::INIT)); - installClassVariable(tsigcontext_type, "STATE_SENT_REQUEST", - Py_BuildValue("I", TSIGContext::SENT_REQUEST)); - installClassVariable(tsigcontext_type, "STATE_RECEIVED_REQUEST", - Py_BuildValue("I", TSIGContext::RECEIVED_REQUEST)); - installClassVariable(tsigcontext_type, "STATE_SENT_RESPONSE", - Py_BuildValue("I", TSIGContext::SENT_RESPONSE)); - installClassVariable(tsigcontext_type, "STATE_VERIFIED_RESPONSE", - Py_BuildValue("I", - TSIGContext::VERIFIED_RESPONSE)); - - installClassVariable(tsigcontext_type, "DEFAULT_FUDGE", - Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE)); - } catch (const exception& ex) { - const string ex_what = - "Unexpected failure in TSIGContext initialization: " + - string(ex.what()); - PyErr_SetString(po_IscException, ex_what.c_str()); - return (false); - } catch (...) { - PyErr_SetString(PyExc_SystemError, - "Unexpected failure in TSIGContext initialization"); - return (false); - } - - return (true); -} -} // end namespace internal - bool PyTSIGContext_Check(PyObject* obj) { if (obj == NULL) { diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc index 858bee1998..c22bf0d126 100644 --- a/src/lib/dns/python/tsig_rdata_python.cc +++ b/src/lib/dns/python/tsig_rdata_python.cc @@ -337,26 +337,6 @@ PyTypeObject tsig_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_TSIG(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&tsig_type) < 0) { - return (false); - } - void* p = &tsig_type; - if (PyModule_AddObject(mod, "TSIG", static_cast(p)) < 0) { - return (false); - } - Py_INCREF(&tsig_type); - - return (true); -} -} // end namespace internal - PyObject* createTSIGObject(const any::TSIG& source) { TSIGContainer container(PyObject_New(s_TSIG, &tsig_type)); diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc index 93dfbe8d93..7a0217e3a1 100644 --- a/src/lib/dns/python/tsigerror_python.cc +++ b/src/lib/dns/python/tsigerror_python.cc @@ -280,77 +280,6 @@ PyTypeObject tsigerror_type = { 0 // tp_version_tag }; -namespace { -// Trivial shortcut to create and install TSIGError constants. -inline void -installTSIGErrorConstant(const char* name, const TSIGError& val) { - TSIGErrorContainer container(PyObject_New(s_TSIGError, &tsigerror_type)); - container.installAsClassVariable(tsigerror_type, name, new TSIGError(val)); -} -} - -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_TSIGError(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&tsigerror_type) < 0) { - return (false); - } - void* p = &tsigerror_type; - if (PyModule_AddObject(mod, "TSIGError", static_cast(p)) < 0) { - return (false); - } - Py_INCREF(&tsigerror_type); - - try { - // Constant class variables - // Error codes (bare values) - installClassVariable(tsigerror_type, "BAD_SIG_CODE", - Py_BuildValue("H", TSIGError::BAD_SIG_CODE)); - installClassVariable(tsigerror_type, "BAD_KEY_CODE", - Py_BuildValue("H", TSIGError::BAD_KEY_CODE)); - installClassVariable(tsigerror_type, "BAD_TIME_CODE", - Py_BuildValue("H", TSIGError::BAD_TIME_CODE)); - - // Error codes (constant objects) - installTSIGErrorConstant("NOERROR", TSIGError::NOERROR()); - installTSIGErrorConstant("FORMERR", TSIGError::FORMERR()); - installTSIGErrorConstant("SERVFAIL", TSIGError::SERVFAIL()); - installTSIGErrorConstant("NXDOMAIN", TSIGError::NXDOMAIN()); - installTSIGErrorConstant("NOTIMP", TSIGError::NOTIMP()); - installTSIGErrorConstant("REFUSED", TSIGError::REFUSED()); - installTSIGErrorConstant("YXDOMAIN", TSIGError::YXDOMAIN()); - installTSIGErrorConstant("YXRRSET", TSIGError::YXRRSET()); - installTSIGErrorConstant("NXRRSET", TSIGError::NXRRSET()); - installTSIGErrorConstant("NOTAUTH", TSIGError::NOTAUTH()); - installTSIGErrorConstant("NOTZONE", TSIGError::NOTZONE()); - installTSIGErrorConstant("RESERVED11", TSIGError::RESERVED11()); - installTSIGErrorConstant("RESERVED12", TSIGError::RESERVED12()); - installTSIGErrorConstant("RESERVED13", TSIGError::RESERVED13()); - installTSIGErrorConstant("RESERVED14", TSIGError::RESERVED14()); - installTSIGErrorConstant("RESERVED15", TSIGError::RESERVED15()); - installTSIGErrorConstant("BAD_SIG", TSIGError::BAD_SIG()); - installTSIGErrorConstant("BAD_KEY", TSIGError::BAD_KEY()); - installTSIGErrorConstant("BAD_TIME", TSIGError::BAD_TIME()); - } catch (const exception& ex) { - const string ex_what = - "Unexpected failure in TSIGError initialization: " + - string(ex.what()); - PyErr_SetString(po_IscException, ex_what.c_str()); - return (false); - } catch (...) { - PyErr_SetString(PyExc_SystemError, - "Unexpected failure in TSIGError initialization"); - return (false); - } - - return (true); -} -} // end namespace internal - PyObject* createTSIGErrorObject(const TSIGError& source) { TSIGErrorContainer container(PyObject_New(s_TSIGError, &tsigerror_type)); diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc index b453ae9698..ff13a12104 100644 --- a/src/lib/dns/python/tsigkey_python.cc +++ b/src/lib/dns/python/tsigkey_python.cc @@ -232,52 +232,6 @@ PyTypeObject tsigkey_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_TSIGKey(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&tsigkey_type) < 0) { - return (false); - } - void* p = &tsigkey_type; - if (PyModule_AddObject(mod, "TSIGKey", static_cast(p)) != 0) { - return (false); - } - Py_INCREF(&tsigkey_type); - - try { - // Constant class variables - installClassVariable(tsigkey_type, "HMACMD5_NAME", - createNameObject(TSIGKey::HMACMD5_NAME())); - installClassVariable(tsigkey_type, "HMACSHA1_NAME", - createNameObject(TSIGKey::HMACSHA1_NAME())); - installClassVariable(tsigkey_type, "HMACSHA256_NAME", - createNameObject(TSIGKey::HMACSHA256_NAME())); - installClassVariable(tsigkey_type, "HMACSHA224_NAME", - createNameObject(TSIGKey::HMACSHA224_NAME())); - installClassVariable(tsigkey_type, "HMACSHA384_NAME", - createNameObject(TSIGKey::HMACSHA384_NAME())); - installClassVariable(tsigkey_type, "HMACSHA512_NAME", - createNameObject(TSIGKey::HMACSHA512_NAME())); - } catch (const exception& ex) { - const string ex_what = - "Unexpected failure in TSIGKey initialization: " + - string(ex.what()); - PyErr_SetString(po_IscException, ex_what.c_str()); - return (false); - } catch (...) { - PyErr_SetString(PyExc_SystemError, - "Unexpected failure in TSIGKey initialization"); - return (false); - } - - return (true); -} -} // end namespace internal - bool PyTSIGKey_Check(PyObject* obj) { if (obj == NULL) { @@ -490,31 +444,6 @@ PyTypeObject tsigkeyring_type = { 0 // tp_version_tag }; -namespace internal { -bool -initModulePart_TSIGKeyRing(PyObject* mod) { - if (PyType_Ready(&tsigkeyring_type) < 0) { - return (false); - } - Py_INCREF(&tsigkeyring_type); - void* p = &tsigkeyring_type; - if (PyModule_AddObject(mod, "TSIGKeyRing", - static_cast(p)) != 0) { - Py_DECREF(&tsigkeyring_type); - return (false); - } - - addClassVariable(tsigkeyring_type, "SUCCESS", - Py_BuildValue("I", TSIGKeyRing::SUCCESS)); - addClassVariable(tsigkeyring_type, "EXIST", - Py_BuildValue("I", TSIGKeyRing::EXIST)); - addClassVariable(tsigkeyring_type, "NOTFOUND", - Py_BuildValue("I", TSIGKeyRing::NOTFOUND)); - - return (true); -} -} // end namespace internal - bool PyTSIGKeyRing_Check(PyObject* obj) { if (obj == NULL) { diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc index 947d0d9c28..d75ced3304 100644 --- a/src/lib/dns/python/tsigrecord_python.cc +++ b/src/lib/dns/python/tsigrecord_python.cc @@ -262,45 +262,6 @@ PyTypeObject tsigrecord_type = { 0 // tp_version_tag }; -// Module Initialization, all statics are initialized here -namespace internal { -bool -initModulePart_TSIGRecord(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&tsigrecord_type) < 0) { - return (false); - } - void* p = &tsigrecord_type; - if (PyModule_AddObject(mod, "TSIGRecord", static_cast(p)) < 0) { - return (false); - } - Py_INCREF(&tsigrecord_type); - - // The following template is the typical procedure for installing class - // variables. If the class doesn't have a class variable, remove the - // entire try-catch clauses. - try { - // Constant class variables - installClassVariable(tsigrecord_type, "TSIG_TTL", - Py_BuildValue("I", 0)); - } catch (const exception& ex) { - const string ex_what = - "Unexpected failure in TSIGRecord initialization: " + - string(ex.what()); - PyErr_SetString(po_IscException, ex_what.c_str()); - return (false); - } catch (...) { - PyErr_SetString(PyExc_SystemError, - "Unexpected failure in TSIGRecord initialization"); - return (false); - } - - return (true); -} -} // end namespace internal - PyObject* createTSIGRecordObject(const TSIGRecord& source) { TSIGRecordContainer container(PyObject_New(s_TSIGRecord, &tsigrecord_type)); From f0274b7451761b2dc48c0be148ecd8563c9800da Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Tue, 20 Sep 2011 10:36:09 +0800 Subject: [PATCH 727/974] [trac1112] Replace IS_DIGIT macro with function in anonymouse namespace. Replace TEST_F with TEST in unit tests. Update the description of InvalidRdataTest exception. --- src/lib/dns/character_string.cc | 15 +++++++++------ src/lib/dns/tests/character_string_unittest.cc | 8 +++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/lib/dns/character_string.cc b/src/lib/dns/character_string.cc index ae2ad7a15a..db83eb6b50 100644 --- a/src/lib/dns/character_string.cc +++ b/src/lib/dns/character_string.cc @@ -21,7 +21,11 @@ using namespace isc::dns::rdata; namespace isc { namespace dns { -#define IS_DIGIT(c) (('0' <= (c)) && ((c) <= '9')) +namespace { +bool isDigit(char c) { + return ('0' <= c) && (c <= '9'); +} +} std::string characterstr::getNextCharacterString(const std::string& input_str, @@ -50,20 +54,19 @@ characterstr::getNextCharacterString(const std::string& input_str, if (*input_iterator == '\\') { if (input_iterator + 1 == input_str.end()) { isc_throw(InvalidRdataText, " ended \ - exceptionally."); + prematurely."); } else { - if (IS_DIGIT(*(input_iterator + 1))) { + if (isDigit(*(input_iterator + 1))) { // \DDD where each D is a digit. It its the octet // corresponding to the decimal number described by DDD if (input_iterator + 3 >= input_str.end()) { isc_throw(InvalidRdataText, " ended \ - exceptionally."); + prematurely."); } else { int n = 0; ++input_iterator; for (int i = 0; i < 3; ++i) { - if (('0' <= *input_iterator) && - (*input_iterator <= '9')) { + if (isDigit(*input_iterator)) { n = n*10 + (*input_iterator - '0'); ++input_iterator; } else { diff --git a/src/lib/dns/tests/character_string_unittest.cc b/src/lib/dns/tests/character_string_unittest.cc index 83dbf923a3..5fed9eb0a3 100644 --- a/src/lib/dns/tests/character_string_unittest.cc +++ b/src/lib/dns/tests/character_string_unittest.cc @@ -28,8 +28,6 @@ using namespace isc::dns::characterstr; using namespace isc::dns::rdata; namespace { -class CharacterStringTest : public ::testing::Test { -}; class CharacterString { public: @@ -42,7 +40,7 @@ private: string characterStr_; }; -TEST_F(CharacterStringTest, testNormalCase) { +TEST(CharacterStringTest, testNormalCase) { CharacterString cstr1("foo"); EXPECT_EQ(string("foo"), cstr1.str()); @@ -59,7 +57,7 @@ TEST_F(CharacterStringTest, testNormalCase) { EXPECT_EQ(string("foo\""), cstr4.str()); } -TEST_F(CharacterStringTest, testBadCase) { +TEST(CharacterStringTest, testBadCase) { // The that started with quotes should also be ended // with quotes EXPECT_THROW(CharacterString cstr("\"foo"), InvalidRdataText); @@ -72,7 +70,7 @@ TEST_F(CharacterStringTest, testBadCase) { EXPECT_THROW(CharacterString cstr(str), CharStringTooLong); } -TEST_F(CharacterStringTest, testEscapeCharacter) { +TEST(CharacterStringTest, testEscapeCharacter) { CharacterString cstr1("foo\\bar"); EXPECT_EQ(string("foobar"), cstr1.str()); From 433f29fd44d8dd6c940e49ee2657b769d70781fe Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 20 Sep 2011 00:46:33 -0700 Subject: [PATCH 728/974] [1245] adjusted the python wrapper template so it works with the change of the assumption on @CPPCLASS@Container constructor (it's now explicit) --- src/lib/util/python/wrapper_template.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc index a703731261..426ced557f 100644 --- a/src/lib/util/python/wrapper_template.cc +++ b/src/lib/util/python/wrapper_template.cc @@ -299,8 +299,8 @@ initModulePart_@CPPCLASS@(PyObject* mod) { PyObject* create@CPPCLASS@Object(const @CPPCLASS@& source) { - @CPPCLASS@Container container = - PyObject_New(s_@CPPCLASS@, &@cppclass@_type); + @CPPCLASS@Container container(PyObject_New(s_@CPPCLASS@, + &@cppclass@_type)); container.set(new @CPPCLASS@(source)); return (container.release()); } From ee468e8f02f1cd1bcf09da75170ed62dc230b70e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 20 Sep 2011 14:54:53 +0200 Subject: [PATCH 729/974] [1112] Editorial cleanup: added parentheses --- src/lib/dns/character_string.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/character_string.cc b/src/lib/dns/character_string.cc index db83eb6b50..3a289acd49 100644 --- a/src/lib/dns/character_string.cc +++ b/src/lib/dns/character_string.cc @@ -23,7 +23,7 @@ namespace dns { namespace { bool isDigit(char c) { - return ('0' <= c) && (c <= '9'); + return (('0' <= c) && (c <= '9')); } } From 4e93ba217318854742144bf0b8e30f4c3614db92 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 20 Sep 2011 15:14:03 +0200 Subject: [PATCH 730/974] [1177] Comment update DNSSEC/NSEC order looks awkward. --- src/lib/datasrc/database.h | 2 +- src/lib/datasrc/zone.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 7d36508fd2..184a2efa1d 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -476,7 +476,7 @@ public: virtual const std::string& getDBName() const = 0; /** - * \brief It returns the previous name in DNSSEC/NSEC order. + * \brief It returns the previous name in DNSSEC order. * * This is used in DatabaseClient::findPreviousName and does more * or less the real work, except for working on strings. diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 14bf295808..794e46dc7c 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -211,7 +211,7 @@ public: /// \brief Get previous name in the zone /// - /// Gets the previous name in the DNSSEC/NSEC order. This can be used + /// Gets the previous name in the DNSSEC order. This can be used /// to find the correct NSEC records for proving nonexistence /// of domains. /// From 2f8c4b3da6060a9b57e944726dd61cb1b2a19906 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 20 Sep 2011 16:14:58 +0200 Subject: [PATCH 731/974] [1245] catch exceptions where createXXXObject() is called and removed unused createMessageRendererObject function --- src/lib/dns/python/message_python.cc | 69 +++++++++++++---- src/lib/dns/python/messagerenderer_python.cc | 25 +----- src/lib/dns/python/messagerenderer_python.h | 9 --- src/lib/dns/python/question_python.cc | 42 +++++++++- src/lib/dns/python/rrset_python.cc | 81 +++++++++++++++++--- src/lib/dns/python/tsig_python.cc | 2 +- 6 files changed, 164 insertions(+), 64 deletions(-) diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index a41523e0a4..b40ab45e6f 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -311,8 +311,14 @@ Message_getOpcode(s_Message* self) { } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); return (NULL); + } catch (const exception& ex) { + const string ex_what = + "Failed to get message opcode: " + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (NULL); } catch (...) { - PyErr_SetString(po_IscException, "Unexpected exception"); + PyErr_SetString(po_IscException, + "Unexpected exception getting opcode from message"); return (NULL); } } @@ -338,7 +344,17 @@ Message_getEDNS(s_Message* self) { if (!src) { Py_RETURN_NONE; } - return (createEDNSObject(*src)); + try { + return (createEDNSObject(*src)); + } catch (const exception& ex) { + const string ex_what = + "Failed to get EDNS from message: " + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting EDNS from message"); + } + return (NULL); } PyObject* @@ -418,13 +434,25 @@ Message_getQuestion(s_Message* self) { return (NULL); } - for (; qi != qi_end; ++qi) { - if (PyList_Append(list, createQuestionObject(**qi)) == -1) { - Py_DECREF(list); - return (NULL); + try { + for (; qi != qi_end; ++qi) { + if (PyList_Append(list, createQuestionObject(**qi)) == -1) { + Py_DECREF(list); + return (NULL); + } } + return (list); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting Question section: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting Question section"); } - return (list); + Py_DECREF(list); + return (NULL); } PyObject* @@ -458,18 +486,25 @@ Message_getSection(s_Message* self, PyObject* args) { if (list == NULL) { return (NULL); } - for (; rrsi != rrsi_end; ++rrsi) { - PyObject* rrset = createRRsetObject(**rrsi); - if (PyList_Append(list, rrset) == -1) { - Py_DECREF(rrset); - Py_DECREF(list); - return (NULL); + try { + for (; rrsi != rrsi_end; ++rrsi) { + if (PyList_Append(list, createRRsetObject(**rrsi)) == -1) { + Py_DECREF(list); + return (NULL); + } } - // PyList_Append increases refcount, so we remove ours since - // we don't need it anymore - Py_DECREF(rrset); + return (list); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure creating Question object: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure creating Question object"); } - return (list); + Py_DECREF(list); + return (NULL); } //static PyObject* Message_beginQuestion(s_Message* self, PyObject* args); diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc index d5c2311c95..84993e4175 100644 --- a/src/lib/dns/python/messagerenderer_python.cc +++ b/src/lib/dns/python/messagerenderer_python.cc @@ -239,28 +239,9 @@ PyTypeObject messagerenderer_type = { 0 // tp_version_tag }; -PyObject* -createMessageRendererObject(const MessageRenderer& source) { - // should we copy? can we? - // copy the existing buffer into a new one, then create a new renderer with - // that buffer - s_MessageRenderer* mr = static_cast( - messagerenderer_type.tp_alloc(&messagerenderer_type, 0)); - if (mr == NULL) { - isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " - "probably due to short memory"); - } - try { - mr->outputbuffer = new OutputBuffer(4096); - mr->outputbuffer->writeData(source.getData(), source.getLength()); - mr->cppobj = new MessageRenderer(*mr->outputbuffer); - - return (mr); - } catch (const std::bad_alloc&) { - isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " - "probably due to short memory"); - } -} +// If we need a createMessageRendererObject(), should we copy? can we? +// copy the existing buffer into a new one, then create a new renderer with +// that buffer? bool PyMessageRenderer_Check(PyObject* obj) { diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h index bfd895ae62..f68d57d0fb 100644 --- a/src/lib/dns/python/messagerenderer_python.h +++ b/src/lib/dns/python/messagerenderer_python.h @@ -26,15 +26,6 @@ namespace python { extern PyTypeObject messagerenderer_type; -/// This is a simple shortcut to create a python MessageRenderer object (in the -/// form of a pointer to PyObject) with minimal exception safety. -/// On success, it returns a valid pointer to PyObject with a reference -/// counter of 1; if something goes wrong it throws an exception (it never -/// returns a NULL pointer). -/// This function is expected to be called within a try block -/// followed by necessary setup for python exception. -PyObject* createMessageRendererObject(const MessageRenderer& source); - /// \brief Checks if the given python object is a MessageRenderer object /// /// \exception PyCPPWrapperException if obj is NULL diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index f9d3103194..76c95f014a 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -20,13 +20,14 @@ #include #include +#include "pydnspp_common.h" #include "question_python.h" - #include "name_python.h" #include "rrclass_python.h" #include "rrtype_python.h" #include "messagerenderer_python.h" +using namespace std; using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; @@ -139,17 +140,50 @@ Question_destroy(s_Question* self) { static PyObject* Question_getName(s_Question* self) { - return (createNameObject(self->cppobj->getName())); + try { + return (createNameObject(self->cppobj->getName())); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting question Name: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting question Name"); + } + return (NULL); } static PyObject* Question_getType(s_Question* self) { - return (createRRTypeObject(self->cppobj->getType())); + try { + return (createRRTypeObject(self->cppobj->getType())); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting question RRType: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting question RRType"); + } + return (NULL); } static PyObject* Question_getClass(s_Question* self) { - return (createRRClassObject(self->cppobj->getClass())); + try { + return (createRRClassObject(self->cppobj->getClass())); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting question RRClass: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting question RRClass"); + } + return (NULL); } static PyObject* diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index cd1edf8fda..27ded07d05 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -29,6 +29,7 @@ #include "rdata_python.h" #include "messagerenderer_python.h" +using namespace std; using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; @@ -138,22 +139,66 @@ RRset_getRdataCount(s_RRset* self) { PyObject* RRset_getName(s_RRset* self) { - return (createNameObject(self->cppobj->getName())); + try { + return (createNameObject(self->cppobj->getName())); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting rrset Name: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting rrset Name"); + } + return (NULL); } PyObject* RRset_getClass(s_RRset* self) { - return (createRRClassObject(self->cppobj->getClass())); + try { + return (createRRClassObject(self->cppobj->getClass())); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting question RRClass: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting question RRClass"); + } + return (NULL); } PyObject* RRset_getType(s_RRset* self) { - return (createRRTypeObject(self->cppobj->getType())); + try { + return (createRRTypeObject(self->cppobj->getType())); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting question RRType: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting question RRType"); + } + return (NULL); } PyObject* RRset_getTTL(s_RRset* self) { - return (createRRTTLObject(self->cppobj->getTTL())); + try { + return (createRRTTLObject(self->cppobj->getTTL())); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting question TTL: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting question TTL"); + } + return (NULL); } PyObject* @@ -251,14 +296,28 @@ RRset_getRdata(s_RRset* self) { RdataIteratorPtr it = self->cppobj->getRdataIterator(); - for (; !it->isLast(); it->next()) { - const rdata::Rdata *rd = &it->getCurrent(); - PyList_Append(list, - createRdataObject(createRdata(self->cppobj->getType(), - self->cppobj->getClass(), *rd))); + try { + for (; !it->isLast(); it->next()) { + const rdata::Rdata *rd = &it->getCurrent(); + if (PyList_Append(list, + createRdataObject(createRdata(self->cppobj->getType(), + self->cppobj->getClass(), *rd))) == -1) { + Py_DECREF(list); + return (NULL); + } + } + return (list); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure getting rrset Rdata: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure getting rrset Rdata"); } - - return (list); + Py_DECREF(list); + return (NULL); } PyObject* diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc index 7e1fa965d7..b836c230b1 100644 --- a/src/lib/dns/python/tsig_python.cc +++ b/src/lib/dns/python/tsig_python.cc @@ -148,7 +148,7 @@ PyObject* TSIGContext_getError(s_TSIGContext* self) { try { PyObjectContainer container(createTSIGErrorObject( - self->cppobj->getError())); + self->cppobj->getError())); return (Py_BuildValue("O", container.get())); } catch (const exception& ex) { const string ex_what = From 3eb0dedb8a5d9835b394484c6112a4b2fcbe9d51 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 20 Sep 2011 16:34:44 +0200 Subject: [PATCH 732/974] [1245] add NULL checks in PyXXX_ToXXX() functions --- src/lib/dns/python/edns_python.cc | 4 ++++ src/lib/dns/python/messagerenderer_python.cc | 4 ++++ src/lib/dns/python/name_python.cc | 4 ++++ src/lib/dns/python/opcode_python.cc | 4 ++++ src/lib/dns/python/question_python.cc | 4 ++++ src/lib/dns/python/rcode_python.cc | 4 ++++ src/lib/dns/python/rdata_python.cc | 4 ++++ src/lib/dns/python/rrclass_python.cc | 4 ++++ src/lib/dns/python/rrset_python.cc | 4 ++++ src/lib/dns/python/rrttl_python.cc | 4 ++++ src/lib/dns/python/rrtype_python.cc | 4 ++++ src/lib/dns/python/tsig_python.cc | 4 ++++ src/lib/dns/python/tsig_rdata_python.cc | 4 ++++ src/lib/dns/python/tsigkey_python.cc | 4 ++++ src/lib/dns/python/tsigrecord_python.cc | 4 ++++ 15 files changed, 60 insertions(+) diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index 126422312a..8f0f1a4213 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -380,6 +380,10 @@ PyEDNS_Check(PyObject* obj) { const EDNS& PyEDNS_ToEDNS(const PyObject* edns_obj) { + if (edns_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in EDNS PyObject conversion"); + } const s_EDNS* edns = static_cast(edns_obj); return (*edns->cppobj); } diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc index 84993e4175..bb896228b2 100644 --- a/src/lib/dns/python/messagerenderer_python.cc +++ b/src/lib/dns/python/messagerenderer_python.cc @@ -253,6 +253,10 @@ PyMessageRenderer_Check(PyObject* obj) { MessageRenderer& PyMessageRenderer_ToMessageRenderer(PyObject* messagerenderer_obj) { + if (messagerenderer_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in MessageRenderer PyObject conversion"); + } s_MessageRenderer* messagerenderer = static_cast(messagerenderer_obj); return (*messagerenderer->cppobj); } diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc index c7dcab313c..404344549b 100644 --- a/src/lib/dns/python/name_python.cc +++ b/src/lib/dns/python/name_python.cc @@ -654,6 +654,10 @@ PyName_Check(PyObject* obj) { const Name& PyName_ToName(const PyObject* name_obj) { + if (name_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in Name PyObject conversion"); + } const s_Name* name = static_cast(name_obj); return (*name->cppobj); } diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc index 4917dc2359..50436a9f70 100644 --- a/src/lib/dns/python/opcode_python.cc +++ b/src/lib/dns/python/opcode_python.cc @@ -358,6 +358,10 @@ PyOpcode_Check(PyObject* obj) { const Opcode& PyOpcode_ToOpcode(const PyObject* opcode_obj) { + if (opcode_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in Opcode PyObject conversion"); + } const s_Opcode* opcode = static_cast(opcode_obj); return (*opcode->cppobj); } diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc index 76c95f014a..44d68a2047 100644 --- a/src/lib/dns/python/question_python.cc +++ b/src/lib/dns/python/question_python.cc @@ -308,6 +308,10 @@ PyQuestion_Check(PyObject* obj) { const Question& PyQuestion_ToQuestion(const PyObject* question_obj) { + if (question_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in Question PyObject conversion"); + } const s_Question* question = static_cast(question_obj); return (*question->cppobj); } diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc index eb5bbeddcf..42b48e7b62 100644 --- a/src/lib/dns/python/rcode_python.cc +++ b/src/lib/dns/python/rcode_python.cc @@ -398,6 +398,10 @@ PyRcode_Check(PyObject* obj) { const Rcode& PyRcode_ToRcode(const PyObject* rcode_obj) { + if (rcode_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in Rcode PyObject conversion"); + } const s_Rcode* rcode = static_cast(rcode_obj); return (*rcode->cppobj); } diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc index 9a8caf52cc..06c0263fa6 100644 --- a/src/lib/dns/python/rdata_python.cc +++ b/src/lib/dns/python/rdata_python.cc @@ -285,6 +285,10 @@ PyRdata_Check(PyObject* obj) { const Rdata& PyRdata_ToRdata(const PyObject* rdata_obj) { + if (rdata_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in Rdata PyObject conversion"); + } const s_Rdata* rdata = static_cast(rdata_obj); return (*rdata->cppobj); } diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc index abdcc84de5..00141872e9 100644 --- a/src/lib/dns/python/rrclass_python.cc +++ b/src/lib/dns/python/rrclass_python.cc @@ -353,6 +353,10 @@ PyRRClass_Check(PyObject* obj) { const RRClass& PyRRClass_ToRRClass(const PyObject* rrclass_obj) { + if (rrclass_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in RRClass PyObject conversion"); + } const s_RRClass* rrclass = static_cast(rrclass_obj); return (*rrclass->cppobj); } diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc index 27ded07d05..9fc3d79166 100644 --- a/src/lib/dns/python/rrset_python.cc +++ b/src/lib/dns/python/rrset_python.cc @@ -448,6 +448,10 @@ PyRRset_ToRRset(PyObject* rrset_obj) { RRsetPtr PyRRset_ToRRsetPtr(PyObject* rrset_obj) { + if (rrset_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in RRset PyObject conversion"); + } s_RRset* rrset = static_cast(rrset_obj); return (rrset->cppobj); } diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc index 99a6f856ef..3a3f067755 100644 --- a/src/lib/dns/python/rrttl_python.cc +++ b/src/lib/dns/python/rrttl_python.cc @@ -308,6 +308,10 @@ PyRRTTL_Check(PyObject* obj) { const RRTTL& PyRRTTL_ToRRTTL(const PyObject* rrttl_obj) { + if (rrttl_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in RRTTL PyObject conversion"); + } const s_RRTTL* rrttl = static_cast(rrttl_obj); return (*rrttl->cppobj); } diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc index 0cd88fb455..bf20b7cd9e 100644 --- a/src/lib/dns/python/rrtype_python.cc +++ b/src/lib/dns/python/rrtype_python.cc @@ -450,6 +450,10 @@ PyRRType_Check(PyObject* obj) { const RRType& PyRRType_ToRRType(const PyObject* rrtype_obj) { + if (rrtype_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in RRType PyObject conversion"); + } const s_RRType* rrtype = static_cast(rrtype_obj); return (*rrtype->cppobj); } diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc index b836c230b1..0764e3375e 100644 --- a/src/lib/dns/python/tsig_python.cc +++ b/src/lib/dns/python/tsig_python.cc @@ -312,6 +312,10 @@ PyTSIGContext_Check(PyObject* obj) { TSIGContext& PyTSIGContext_ToTSIGContext(PyObject* tsigcontext_obj) { + if (tsigcontext_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in TSIGContext PyObject conversion"); + } s_TSIGContext* tsigcontext = static_cast(tsigcontext_obj); return (*tsigcontext->cppobj); } diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc index c22bf0d126..6ec0f0999f 100644 --- a/src/lib/dns/python/tsig_rdata_python.cc +++ b/src/lib/dns/python/tsig_rdata_python.cc @@ -354,6 +354,10 @@ PyTSIG_Check(PyObject* obj) { const any::TSIG& PyTSIG_ToTSIG(const PyObject* tsig_obj) { + if (tsig_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in TSIG PyObject conversion"); + } const s_TSIG* tsig = static_cast(tsig_obj); return (*tsig->cppobj); } diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc index ff13a12104..cf79c1aa82 100644 --- a/src/lib/dns/python/tsigkey_python.cc +++ b/src/lib/dns/python/tsigkey_python.cc @@ -454,6 +454,10 @@ PyTSIGKeyRing_Check(PyObject* obj) { const TSIGKeyRing& PyTSIGKeyRing_ToTSIGKeyRing(const PyObject* tsigkeyring_obj) { + if (tsigkeyring_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in TSIGKeyRing PyObject conversion"); + } const s_TSIGKeyRing* tsigkeyring = static_cast(tsigkeyring_obj); return (*tsigkeyring->cppobj); diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc index d75ced3304..c754dd2c82 100644 --- a/src/lib/dns/python/tsigrecord_python.cc +++ b/src/lib/dns/python/tsigrecord_python.cc @@ -279,6 +279,10 @@ PyTSIGRecord_Check(PyObject* obj) { const TSIGRecord& PyTSIGRecord_ToTSIGRecord(PyObject* tsigrecord_obj) { + if (tsigrecord_obj == NULL) { + isc_throw(PyCPPWrapperException, + "obj argument NULL in TSIGRecord PyObject conversion"); + } s_TSIGRecord* tsigrecord = static_cast(tsigrecord_obj); return (*tsigrecord->cppobj); } From f16de89251e4607eb413df666a64022c50478a4c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 20 Sep 2011 16:46:37 +0200 Subject: [PATCH 733/974] [1177] Little bit more tests --- src/lib/datasrc/tests/database_unittest.cc | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 4a8309c3cc..36099ec96a 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -127,6 +127,7 @@ const char* const TEST_RECORDS[][5] = { {"delegation.example.org.", "NS", "3600", "", "ns.example.com."}, {"delegation.example.org.", "NS", "3600", "", "ns.delegation.example.org."}, + {"delegation.example.org.", "DS", "3600", "", "1 RSAMD5 2 abcd"}, {"delegation.example.org.", "RRSIG", "3600", "", "NS 5 3 3600 " "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"}, {"ns.delegation.example.org.", "A", "3600", "", "192.0.2.1"}, @@ -171,11 +172,17 @@ const char* const TEST_RECORDS[][5] = { {"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"}, {"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"}, {"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"}, + {"*.cnamewild.example.org.", "CNAME", "3600", "", "www.example.org."}, + {"*.nswild.example.org.", "NS", "3600", "", "ns.example.com."}, // For finding previous, this one is the last one in the zone {"zzz.example.org.", "NSEC", "3600", "", "example.org NSEC"}, // For NSEC empty non-terminal {"l.example.org.", "NSEC", "3600", "", "empty.nonterminal.example.org. NSEC"}, {"empty.nonterminal.example.org.", "A", "3600", "", "192.0.2.1"}, + // Invalid rdata + {"invalidrdata.example.org.", "A", "3600", "", "Bunch of nonsense"}, + {"invalidrdata2.example.org.", "A", "3600", "", "192.0.2.1"}, + {"invalidrdata2.example.org.", "RRSIG", "3600", "", "Nonsense"}, {NULL, NULL, NULL, NULL, NULL}, }; @@ -557,6 +564,8 @@ public: } else if (rname == "org.example.www2." || rname == "org.example.www1.") { return ("www.example.org."); + } else if (rname == "org.example.badnsec2.") { + return ("badnsec1.example.org."); } else if (rname == "org.example.notimplnsec." || rname == "org.example.wild.here.") { isc_throw(isc::NotImplemented, "Not implemented in this test"); @@ -1578,6 +1587,22 @@ TYPED_TEST(DatabaseClientTest, wildcard) { // FIXME: What should be returned in this case? How does the // DNSSEC logic handle it? } + + // Some strange things in the wild node + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("www.example.org."); + this->expected_sig_rdatas_.clear(); + doFindTest(*finder, isc::dns::Name("a.cnamewild.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::CNAME(), + this->rrttl_, ZoneFinder::CNAME, + this->expected_rdatas_, this->expected_sig_rdatas_); + + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("ns.example.com."); + doFindTest(*finder, isc::dns::Name("a.nswild.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::NS(), + this->rrttl_, ZoneFinder::DELEGATION, + this->expected_rdatas_, this->expected_sig_rdatas_); } TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) { @@ -2305,4 +2330,21 @@ TYPED_TEST(DatabaseClientTest, previous) { } } +TYPED_TEST(DatabaseClientTest, invalidRdata) { + shared_ptr finder(this->getFinder()); + + EXPECT_THROW(finder->find(Name("invalidrdata.example.org."), RRType::A()), + DataSourceError); + EXPECT_THROW(finder->find(Name("invalidrdata2.example.org."), RRType::A()), + DataSourceError); +} + +TEST_F(MockDatabaseClientTest, missingNSEC) { + shared_ptr finder(this->getFinder()); + + EXPECT_THROW(finder->find(Name("badnsec2.example.org."), RRType::A(), NULL, + ZoneFinder::FIND_DNSSEC), + DataSourceError); +} + } From af10f1ef696ee94f817bc389e0e8b6cd08234333 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 20 Sep 2011 16:50:31 +0200 Subject: [PATCH 734/974] [1245] use forward decl in xxx_python.h where practical --- src/lib/dns/python/edns_python.h | 4 ++-- src/lib/dns/python/message_python.h | 4 ++-- src/lib/dns/python/messagerenderer_python.h | 3 ++- src/lib/dns/python/name_python.h | 4 ++-- src/lib/dns/python/opcode_python.h | 4 ++-- src/lib/dns/python/pydnspp.cc | 3 +++ src/lib/dns/python/question_python.h | 4 ++-- src/lib/dns/python/rcode_python.h | 4 ++-- src/lib/dns/python/rrclass_python.h | 4 ++-- src/lib/dns/python/rrttl_python.h | 4 ++-- src/lib/dns/python/rrtype_python.h | 4 ++-- src/lib/dns/python/tsig_python.h | 4 ++-- src/lib/dns/python/tsigerror_python.h | 4 ++-- src/lib/dns/python/tsigkey_python.h | 5 +++-- src/lib/dns/python/tsigrecord_python.h | 5 +++-- 15 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/lib/dns/python/edns_python.h b/src/lib/dns/python/edns_python.h index 81869df24c..30d92abe22 100644 --- a/src/lib/dns/python/edns_python.h +++ b/src/lib/dns/python/edns_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class EDNS; + namespace python { extern PyTypeObject edns_type; diff --git a/src/lib/dns/python/message_python.h b/src/lib/dns/python/message_python.h index 8ae3efcc9b..be238907db 100644 --- a/src/lib/dns/python/message_python.h +++ b/src/lib/dns/python/message_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class Message; + namespace python { extern PyObject* po_MessageTooShort; diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h index f68d57d0fb..ea9a9402d9 100644 --- a/src/lib/dns/python/messagerenderer_python.h +++ b/src/lib/dns/python/messagerenderer_python.h @@ -17,11 +17,12 @@ #include -#include #include namespace isc { namespace dns { +class MessageRenderer; + namespace python { extern PyTypeObject messagerenderer_type; diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h index fdf6e4abfc..86d7fd08a0 100644 --- a/src/lib/dns/python/name_python.h +++ b/src/lib/dns/python/name_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class Name; + namespace python { extern PyObject* po_EmptyLabel; diff --git a/src/lib/dns/python/opcode_python.h b/src/lib/dns/python/opcode_python.h index 72865883a9..d0aec15e8b 100644 --- a/src/lib/dns/python/opcode_python.h +++ b/src/lib/dns/python/opcode_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class Opcode; + namespace python { extern PyTypeObject opcode_type; diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 8c23725cf7..830876c7a1 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -28,6 +28,9 @@ #include #include +#include +#include +#include #include #include "pydnspp_common.h" diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h index 4e8b2ab142..f5d78b1372 100644 --- a/src/lib/dns/python/question_python.h +++ b/src/lib/dns/python/question_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class Question; + namespace python { extern PyObject* po_EmptyQuestion; diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h index a2de2798aa..a149406c0e 100644 --- a/src/lib/dns/python/rcode_python.h +++ b/src/lib/dns/python/rcode_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class Rcode; + namespace python { extern PyTypeObject rcode_type; diff --git a/src/lib/dns/python/rrclass_python.h b/src/lib/dns/python/rrclass_python.h index dc84cdb68f..f58bba604c 100644 --- a/src/lib/dns/python/rrclass_python.h +++ b/src/lib/dns/python/rrclass_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class RRClass; + namespace python { extern PyObject* po_InvalidRRClass; diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h index f5bdb49f7a..9dbc9824bd 100644 --- a/src/lib/dns/python/rrttl_python.h +++ b/src/lib/dns/python/rrttl_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class RRTTL; + namespace python { extern PyObject* po_InvalidRRTTL; diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h index a0a1b70731..596598e002 100644 --- a/src/lib/dns/python/rrtype_python.h +++ b/src/lib/dns/python/rrtype_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class RRType; + namespace python { extern PyObject* po_InvalidRRType; diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h index b76002dd8d..e4e9ffffcd 100644 --- a/src/lib/dns/python/tsig_python.h +++ b/src/lib/dns/python/tsig_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class TSIGContext; + namespace python { extern PyTypeObject tsigcontext_type; diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h index 14391fffe3..0b5b630ace 100644 --- a/src/lib/dns/python/tsigerror_python.h +++ b/src/lib/dns/python/tsigerror_python.h @@ -17,10 +17,10 @@ #include -#include - namespace isc { namespace dns { +class TSIGError; + namespace python { extern PyTypeObject tsigerror_type; diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h index 1807f5415c..6c3d2e3e92 100644 --- a/src/lib/dns/python/tsigkey_python.h +++ b/src/lib/dns/python/tsigkey_python.h @@ -17,10 +17,11 @@ #include -#include - namespace isc { namespace dns { +class TSIGKey; +class TSIGKeyRing; + namespace python { extern PyTypeObject tsigkey_type; diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h index 9b392c3f4b..d6252e13fb 100644 --- a/src/lib/dns/python/tsigrecord_python.h +++ b/src/lib/dns/python/tsigrecord_python.h @@ -17,12 +17,13 @@ #include -#include - namespace isc { namespace dns { +class TSIGRecord; + namespace python { + extern PyTypeObject tsigrecord_type; /// This is A simple shortcut to create a python TSIGRecord object (in the From 2a5c5383e3df0e625367bf85b740f62bf777b211 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 20 Sep 2011 16:57:46 +0200 Subject: [PATCH 735/974] [1177] Document why we need reversed name --- src/lib/datasrc/database.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 184a2efa1d..c269e87190 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -482,6 +482,10 @@ public: * or less the real work, except for working on strings. * * \param rname The name to ask for previous of, in reversed form. + * We use the reversed form (see isc::dns::Name::reverse), + * because then the case insensitive order of string representation + * and the DNSSEC order correspond (eg. org.example.a is followed + * by org.example.a.b which is followed by org.example.b, etc). * \param zone_id The zone to look through. * \return The previous name. * From d1a1871cc6c93ababba62f42bcab5205320b8867 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 13 Sep 2011 16:06:26 +0200 Subject: [PATCH 736/974] [1179] python wrapper for new datasrc classes --- configure.ac | 1 + src/lib/Makefile.am | 6 +- src/lib/python/isc/Makefile.am | 2 +- src/lib/python/isc/datasrc/Makefile.am | 26 +- src/lib/python/isc/datasrc/__init__.py | 7 + src/lib/python/isc/datasrc/client_python.cc | 268 ++++++++++++++++++ src/lib/python/isc/datasrc/client_python.h | 44 +++ src/lib/python/isc/datasrc/datasrc.cc | 117 ++++++++ src/lib/python/isc/datasrc/datasrc.h | 50 ++++ src/lib/python/isc/datasrc/finder_python.cc | 263 +++++++++++++++++ src/lib/python/isc/datasrc/finder_python.h | 46 +++ src/lib/python/isc/datasrc/iterator_python.cc | 214 ++++++++++++++ src/lib/python/isc/datasrc/iterator_python.h | 47 +++ src/lib/python/isc/datasrc/tests/Makefile.am | 10 +- .../python/isc/datasrc/tests/datasrc_test.py | 209 ++++++++++++++ src/lib/python/isc/datasrc/updater_python.cc | 230 +++++++++++++++ src/lib/python/isc/datasrc/updater_python.h | 47 +++ 17 files changed, 1578 insertions(+), 9 deletions(-) create mode 100644 src/lib/python/isc/datasrc/client_python.cc create mode 100644 src/lib/python/isc/datasrc/client_python.h create mode 100644 src/lib/python/isc/datasrc/datasrc.cc create mode 100644 src/lib/python/isc/datasrc/datasrc.h create mode 100644 src/lib/python/isc/datasrc/finder_python.cc create mode 100644 src/lib/python/isc/datasrc/finder_python.h create mode 100644 src/lib/python/isc/datasrc/iterator_python.cc create mode 100644 src/lib/python/isc/datasrc/iterator_python.h create mode 100644 src/lib/python/isc/datasrc/tests/datasrc_test.py create mode 100644 src/lib/python/isc/datasrc/updater_python.cc create mode 100644 src/lib/python/isc/datasrc/updater_python.h diff --git a/configure.ac b/configure.ac index 6ae71bf1fa..d991eda3f1 100644 --- a/configure.ac +++ b/configure.ac @@ -834,6 +834,7 @@ AC_CONFIG_FILES([Makefile src/lib/python/isc/util/tests/Makefile src/lib/python/isc/datasrc/Makefile src/lib/python/isc/datasrc/tests/Makefile + src/lib/python/isc/dns/Makefile src/lib/python/isc/cc/Makefile src/lib/python/isc/cc/tests/Makefile src/lib/python/isc/config/Makefile diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 5adf150027..04eee45f8d 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = exceptions util log cryptolink dns cc config acl python xfr \ - bench asiolink asiodns nsas cache resolve testutils datasrc \ - server_common +SUBDIRS = exceptions util log cryptolink dns cc config acl xfr bench \ + asiolink asiodns nsas cache resolve testutils datasrc \ + server_common python diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am index d94100bc94..7b832c8be1 100644 --- a/src/lib/python/isc/Makefile.am +++ b/src/lib/python/isc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = datasrc cc config log net notify util testutils acl bind10 +SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10 python_PYTHON = __init__.py diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am index 46fb661ccb..9a0aaa8f84 100644 --- a/src/lib/python/isc/datasrc/Makefile.am +++ b/src/lib/python/isc/datasrc/Makefile.am @@ -1,10 +1,34 @@ SUBDIRS = . tests +# old data, should be removed in the near future once conversion is done python_PYTHON = __init__.py master.py sqlite3_ds.py -pythondir = $(pyexecdir)/isc/datasrc +#pythondir = $(pyexecdir)/isc/pydatasrc + +# new data + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(SQLITE_CFLAGS) + +pythondir = $(pyexecdir)/isc +pyexec_LTLIBRARIES = datasrc.la +datasrc_la_SOURCES = datasrc.cc datasrc.h +datasrc_la_SOURCES += client_python.cc client_python.h +datasrc_la_SOURCES += iterator_python.cc iterator_python.h +datasrc_la_SOURCES += finder_python.cc finder_python.h +datasrc_la_SOURCES += updater_python.cc updater_python.h + +datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) +datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) +datasrc_la_LDFLAGS = $(PYTHON_LDFLAGS) +datasrc_la_LDFLAGS += -module +datasrc_la_LIBADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la +datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/python/libpydnspp.la +datasrc_la_LIBADD += $(PYTHON_LIB) +datasrc_la_LIBADD += $(SQLITE_LIBS) CLEANDIRS = __pycache__ clean-local: rm -rf $(CLEANDIRS) + diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py index 0e1e481080..e78103a1bf 100644 --- a/src/lib/python/isc/datasrc/__init__.py +++ b/src/lib/python/isc/datasrc/__init__.py @@ -1,2 +1,9 @@ from isc.datasrc.master import * from isc.datasrc.sqlite3_ds import * + +for base in sys.path[:]: + loglibdir = os.path.join(base, 'isc/datasrc/.libs') + if os.path.exists(loglibdir): + sys.path.insert(0, loglibdir) + +from datasrc import * diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc new file mode 100644 index 0000000000..049e8a472e --- /dev/null +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -0,0 +1,268 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// Enable this if you use s# variants with PyArg_ParseTuple(), see +// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers +//#define PY_SSIZE_T_CLEAN + +// Python.h needs to be placed at the head of the program file, see: +// http://docs.python.org/py3k/extending/extending.html#a-simple-example +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "datasrc.h" +#include "client_python.h" +#include "finder_python.h" +#include "iterator_python.h" +#include "updater_python.h" + +using namespace std; +using namespace isc::util::python; +using namespace isc::datasrc; +using namespace isc::datasrc::python; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// DataSourceClient +// + +// Trivial constructor. +s_DataSourceClient::s_DataSourceClient() : cppobj(NULL) { +} + +namespace { +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer + DataSourceClientContainer; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +// General creation and destruction +int DataSourceClient_init(s_DataSourceClient* self, PyObject* args); +void DataSourceClient_destroy(s_DataSourceClient* self); + +// These are the functions we export +// +PyObject* +DataSourceClient_FindZone(PyObject* po_self, PyObject* args) { + s_DataSourceClient* const self = static_cast(po_self); + PyObject *name; + if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name)) { + DataSourceClient::FindResult find_result( + self->cppobj->findZone(isc::dns::python::PyName_ToName(name))); + + result::Result r = find_result.code; + ZoneFinderPtr zfp = find_result.zone_finder; + return Py_BuildValue("IO", r, createZoneFinderObject(zfp)); + } else { + return (NULL); + } +} + +PyObject* +DataSourceClient_GetIterator(PyObject* po_self, PyObject* args) { + s_DataSourceClient* const self = static_cast(po_self); + PyObject *name_obj; + if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name_obj)) { + return (createZoneIteratorObject(self->cppobj->getIterator(isc::dns::python::PyName_ToName(name_obj)))); + } else { + return (NULL); + } +} + +PyObject* +DataSourceClient_GetUpdater(PyObject* po_self, PyObject* args) { + s_DataSourceClient* const self = static_cast(po_self); + PyObject *name_obj; + PyObject *replace_obj; + if (PyArg_ParseTuple(args, "O!O", &isc::dns::python::name_type, &name_obj, &replace_obj) && PyBool_Check(replace_obj)) { + bool replace = (replace_obj != Py_False); + return (createZoneUpdaterObject(self->cppobj->getUpdater(isc::dns::python::PyName_ToName(name_obj), replace))); + } else { + return (NULL); + } +} + +// These are the functions we export + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef DataSourceClient_methods[] = { + { "find_zone", DataSourceClient_FindZone, METH_VARARGS, "TODO" }, + { "get_iterator", DataSourceClient_GetIterator, METH_VARARGS, "TODO" }, + { "get_updater", DataSourceClient_GetUpdater, METH_VARARGS, "TODO" }, + { NULL, NULL, 0, NULL } +}; + +// This is a template of typical code logic of python class initialization +// with C++ backend. You'll need to adjust it according to details of the +// actual C++ class. +int +DataSourceClient_init(s_DataSourceClient* self, PyObject* args) { + // TODO: we should use the factory function which hasn't been written + // yet. For now we hardcode the sqlite3 initialization, and pass it one + // string for the database file. (similar to how the 'old direct' + // sqlite3_ds code works) + try { + char* db_file_name; + if (PyArg_ParseTuple(args, "s", &db_file_name)) { + boost::shared_ptr sqlite3_accessor( + new SQLite3Accessor(db_file_name, isc::dns::RRClass::IN())); + self->cppobj = new DatabaseClient(isc::dns::RRClass::IN(), + sqlite3_accessor); + return (0); + } else { + return (-1); + } + + } catch (const exception& ex) { + const string ex_what = "Failed to construct DataSourceClient object: " + + string(ex.what()); + PyErr_SetString(getDataSourceException("Error"), ex_what.c_str()); + return (-1); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, + "Unexpected exception in constructing DataSourceClient"); + return (-1); + } + PyErr_SetString(PyExc_TypeError, + "Invalid arguments to DataSourceClient constructor"); + + return (-1); +} + +// This is a template of typical code logic of python object destructor. +// In many cases you can use it without modification, but check that carefully. +void +DataSourceClient_destroy(s_DataSourceClient* const self) { + delete self->cppobj; + self->cppobj = NULL; + Py_TYPE(self)->tp_free(self); +} + +} // end anonymous namespace + +namespace isc { +namespace datasrc { +namespace python { +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_DataSourceClient +// Most of the functions are not actually implemented and NULL here. +PyTypeObject datasourceclient_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "datasrc.DataSourceClient", + sizeof(s_DataSourceClient), // tp_basicsize + 0, // tp_itemsize + reinterpret_cast(DataSourceClient_destroy), // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + NULL, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The DataSourceClient class objects is...(COMPLETE THIS)", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + DataSourceClient_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + reinterpret_cast(DataSourceClient_init), // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +// Module Initialization, all statics are initialized here +bool +initModulePart_DataSourceClient(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&datasourceclient_type) < 0) { + return (false); + } + void* dscp = &datasourceclient_type; + if (PyModule_AddObject(mod, "DataSourceClient", static_cast(dscp)) < 0) { + return (false); + } + Py_INCREF(&datasourceclient_type); + + isc::dns::python::addClassVariable(datasourceclient_type, "SUCCESS", + Py_BuildValue("I", result::SUCCESS)); + isc::dns::python::addClassVariable(datasourceclient_type, "EXIST", + Py_BuildValue("I", result::EXIST)); + isc::dns::python::addClassVariable(datasourceclient_type, "NOTFOUND", + Py_BuildValue("I", result::NOTFOUND)); + isc::dns::python::addClassVariable(datasourceclient_type, "PARTIALMATCH", + Py_BuildValue("I", result::PARTIALMATCH)); + + return (true); +} + +} // namespace python +} // namespace datasrc +} // namespace isc diff --git a/src/lib/python/isc/datasrc/client_python.h b/src/lib/python/isc/datasrc/client_python.h new file mode 100644 index 0000000000..ea2c014370 --- /dev/null +++ b/src/lib/python/isc/datasrc/client_python.h @@ -0,0 +1,44 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_DATASRC_CLIENT_H +#define __PYTHON_DATASRC_CLIENT_H 1 + +#include + +namespace isc { +namespace datasrc { +class DataSourceClient; + +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_DataSourceClient : public PyObject { +public: + s_DataSourceClient(); + DataSourceClient* cppobj; +}; + +extern PyTypeObject datasourceclient_type; + +bool initModulePart_DataSourceClient(PyObject* mod); + +} // namespace python +} // namespace datasrc +} // namespace isc +#endif // __PYTHON_DATASRC_CLIENT_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc new file mode 100644 index 0000000000..225137cd84 --- /dev/null +++ b/src/lib/python/isc/datasrc/datasrc.cc @@ -0,0 +1,117 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#define PY_SSIZE_T_CLEAN +#include +#include + +#include + +#include +#include +#include + +#include "datasrc.h" +#include "client_python.h" +#include "finder_python.h" +#include "iterator_python.h" +#include "updater_python.h" + +#include + +using namespace isc::datasrc::python; +using namespace isc::util::python; + +namespace isc { +namespace datasrc { +namespace python { +PyObject* +getDataSourceException(const char* ex_name) { + PyObject* ex_obj = NULL; + + PyObject* acl_module = PyImport_AddModule("isc.datasrc"); + if (acl_module != NULL) { + PyObject* acl_dict = PyModule_GetDict(acl_module); + if (acl_dict != NULL) { + ex_obj = PyDict_GetItemString(acl_dict, ex_name); + } + } + + if (ex_obj == NULL) { + ex_obj = PyExc_RuntimeError; + } + return (ex_obj); +} +} // end namespace python +} // end namespace datasrc +} // end namespace isc + +namespace { + +PyObject* po_DataSourceError; + +PyModuleDef iscDataSrc = { + { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, + "datasrc", + "Python bindings for the classes in the isc::datasrc namespace.\n\n" + "These bindings are close match to the C++ API, but they are not complete " + "(some parts are not needed) and some are done in more python-like ways.", + -1, + NULL,// TODO do we need module-level functions? + NULL, + NULL, + NULL, + NULL +}; + +} // end anonymous namespace + +PyMODINIT_FUNC +PyInit_datasrc(void) { + PyObject* mod = PyModule_Create(&iscDataSrc); + if (mod == NULL) { + return (NULL); + } + + if (!initModulePart_DataSourceClient(mod)) { + Py_DECREF(mod); + return (NULL); + } + + if (!initModulePart_ZoneFinder(mod)) { + Py_DECREF(mod); + return (NULL); + } + + if (!initModulePart_ZoneIterator(mod)) { + Py_DECREF(mod); + return (NULL); + } + + if (!initModulePart_ZoneUpdater(mod)) { + Py_DECREF(mod); + return (NULL); + } + + try { + po_DataSourceError = PyErr_NewException("isc.datasrc.Error", NULL, + NULL); + PyObjectContainer(po_DataSourceError).installToModule(mod, "Error"); + } catch (...) { + Py_DECREF(mod); + return (NULL); + } + + return (mod); +} diff --git a/src/lib/python/isc/datasrc/datasrc.h b/src/lib/python/isc/datasrc/datasrc.h new file mode 100644 index 0000000000..9ad1212ae0 --- /dev/null +++ b/src/lib/python/isc/datasrc/datasrc.h @@ -0,0 +1,50 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_DATASRC_H +#define __PYTHON_DATASRC_H 1 + +#include + +namespace isc { +namespace datasrc { +namespace python { + +// Return a Python exception object of the given name (ex_name) defined in +// the isc.datasrc.datasrc loadable module. +// +// Since the datasrc module is a different binary image and is loaded separately +// from the dns module, it would be very tricky to directly access to +// C/C++ symbols defined in that module. So we get access to these object +// using the Python interpretor through this wrapper function. +// +// The __init__.py file should ensure isc.acl.acl has been loaded by the time +// whenever this function is called, and there shouldn't be any operation +// within this function that can fail (such as dynamic memory allocation), +// so this function should always succeed. Yet there may be an overlooked +// failure mode, perhaps due to a bug in the binding implementation, or +// due to invalid usage. As a last resort for such cases, this function +// returns PyExc_RuntimeError (a C binding of Python's RuntimeError) should +// it encounters an unexpected failure. +extern PyObject* getDataSourceException(const char* ex_name); + +} // namespace python +} // namespace datasrc +} // namespace isc + +#endif // __PYTHON_ACL_DNS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc new file mode 100644 index 0000000000..eedd270eb2 --- /dev/null +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -0,0 +1,263 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// Enable this if you use s# variants with PyArg_ParseTuple(), see +// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers +//#define PY_SSIZE_T_CLEAN + +// Python.h needs to be placed at the head of the program file, see: +// http://docs.python.org/py3k/extending/extending.html#a-simple-example +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "datasrc.h" +#include "finder_python.h" + +using namespace std; +using namespace isc::util::python; +using namespace isc::datasrc; +using namespace isc::datasrc::python; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// Zone Finder +// + +// Trivial constructor. +s_ZoneFinder::s_ZoneFinder() : cppobj(ZoneFinderPtr()) { +} + +namespace { +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer ZoneFinderContainer; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +// General creation and destruction +int ZoneFinder_init(s_ZoneFinder* self, PyObject* args); +void ZoneFinder_destroy(s_ZoneFinder* self); + +// These are the functions we export +// +PyObject* ZoneFinder_GetClass(PyObject* po_self, PyObject*) { + s_ZoneFinder* self = static_cast(po_self); + return (isc::dns::python::createRRClassObject(self->cppobj->getClass())); +} + +PyObject* ZoneFinder_GetOrigin(PyObject* po_self, PyObject*) { + s_ZoneFinder* self = static_cast(po_self); + return (isc::dns::python::createNameObject(self->cppobj->getOrigin())); +} + +PyObject* ZoneFinder_Find(PyObject* po_self, PyObject* args) { + s_ZoneFinder* const self = static_cast(po_self); + PyObject *name; + PyObject *rrtype; + PyObject *target; + int options_int; + if (PyArg_ParseTuple(args, "O!O!OI", &isc::dns::python::name_type, &name, + &isc::dns::python::rrtype_type, &rrtype, + &target, &options_int)) { + ZoneFinder::FindOptions options = static_cast(options_int); + ZoneFinder::FindResult find_result( + self->cppobj->find(isc::dns::python::PyName_ToName(name), + isc::dns::python::PyRRType_ToRRType(rrtype), + NULL, + options + )); + ZoneFinder::Result r = find_result.code; + isc::dns::ConstRRsetPtr rrsp = find_result.rrset; + if (rrsp) { + return Py_BuildValue("IO", r, isc::dns::python::createRRsetObject(*rrsp)); + } else { + Py_INCREF(Py_None); + return Py_BuildValue("IO", r, Py_None); + } + } else { + return (NULL); + } + return Py_BuildValue("I", 1); +} + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef ZoneFinder_methods[] = { + { "get_class", ZoneFinder_GetClass, METH_NOARGS, "TODO" }, + { "get_origin", ZoneFinder_GetOrigin, METH_NOARGS, "TODO" }, + { "find", ZoneFinder_Find, METH_VARARGS, "TODO" }, + { NULL, NULL, 0, NULL } +}; + +// This is a template of typical code logic of python class initialization +// with C++ backend. You'll need to adjust it according to details of the +// actual C++ class. +int +ZoneFinder_init(s_ZoneFinder* self, PyObject* args) { + // can't be called directly + PyErr_SetString(PyExc_TypeError, + "ZoneFinder cannot be constructed directly"); + + return (-1); +} + +// This is a template of typical code logic of python object destructor. +// In many cases you can use it without modification, but check that carefully. +void +ZoneFinder_destroy(s_ZoneFinder* const self) { + //delete self->cppobj; + //self->cppobj = NULL; + Py_TYPE(self)->tp_free(self); +} + +} // end of unnamed namespace + +namespace isc { +namespace datasrc { +namespace python { +PyTypeObject zonefinder_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "datasrc.ZoneFinder", + sizeof(s_ZoneFinder), // tp_basicsize + 0, // tp_itemsize + reinterpret_cast(ZoneFinder_destroy), // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + NULL, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The ZoneFinder class objects is...(TODO COMPLETE THIS)", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + ZoneFinder_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + reinterpret_cast(ZoneFinder_init),// tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +// Module Initialization, all statics are initialized here +bool +initModulePart_ZoneFinder(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&zonefinder_type) < 0) { + return (false); + } + void* zip = &zonefinder_type; + if (PyModule_AddObject(mod, "ZoneFinder", static_cast(zip)) < 0) { + return (false); + } + Py_INCREF(&zonefinder_type); + + isc::dns::python::addClassVariable(zonefinder_type, "SUCCESS", + Py_BuildValue("I", ZoneFinder::SUCCESS)); + isc::dns::python::addClassVariable(zonefinder_type, "DELEGATION", + Py_BuildValue("I", ZoneFinder::DELEGATION)); + isc::dns::python::addClassVariable(zonefinder_type, "NXDOMAIN", + Py_BuildValue("I", ZoneFinder::NXDOMAIN)); + isc::dns::python::addClassVariable(zonefinder_type, "NXRRSET", + Py_BuildValue("I", ZoneFinder::NXRRSET)); + isc::dns::python::addClassVariable(zonefinder_type, "CNAME", + Py_BuildValue("I", ZoneFinder::CNAME)); + isc::dns::python::addClassVariable(zonefinder_type, "DNAME", + Py_BuildValue("I", ZoneFinder::DNAME)); + + isc::dns::python::addClassVariable(zonefinder_type, "FIND_DEFAULT", + Py_BuildValue("I", ZoneFinder::FIND_DEFAULT)); + isc::dns::python::addClassVariable(zonefinder_type, "FIND_GLUE_OK", + Py_BuildValue("I", ZoneFinder::FIND_GLUE_OK)); + isc::dns::python::addClassVariable(zonefinder_type, "FIND_DNSSEC", + Py_BuildValue("I", ZoneFinder::FIND_DNSSEC)); + + + return (true); +} + +PyObject* +createZoneFinderObject(isc::datasrc::ZoneFinderPtr source) { + s_ZoneFinder* py_zi = static_cast( + zonefinder_type.tp_alloc(&zonefinder_type, 0)); + if (py_zi != NULL) { + py_zi->cppobj = source; + } + return (py_zi); +} + +} // namespace python +} // namespace datasrc +} // namespace isc + diff --git a/src/lib/python/isc/datasrc/finder_python.h b/src/lib/python/isc/datasrc/finder_python.h new file mode 100644 index 0000000000..7dfc34e06d --- /dev/null +++ b/src/lib/python/isc/datasrc/finder_python.h @@ -0,0 +1,46 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_DATASRC_FINDER_H +#define __PYTHON_DATASRC_FINDER_H 1 + +#include + +namespace isc { +namespace datasrc { + +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_ZoneFinder : public PyObject { +public: + s_ZoneFinder(); + ZoneFinderPtr cppobj; +}; + +extern PyTypeObject zonefinder_type; + +bool initModulePart_ZoneFinder(PyObject* mod); + +PyObject* createZoneFinderObject(isc::datasrc::ZoneFinderPtr source); + + +} // namespace python +} // namespace datasrc +} // namespace isc +#endif // __PYTHON_DATASRC_FINDER_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc new file mode 100644 index 0000000000..4d9c5c7de2 --- /dev/null +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -0,0 +1,214 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// Enable this if you use s# variants with PyArg_ParseTuple(), see +// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers +//#define PY_SSIZE_T_CLEAN + +// Python.h needs to be placed at the head of the program file, see: +// http://docs.python.org/py3k/extending/extending.html#a-simple-example +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include "datasrc.h" +#include "iterator_python.h" + +using namespace std; +using namespace isc::util::python; +using namespace isc::datasrc; +using namespace isc::datasrc::python; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// Zone Iterator +// + +// Trivial constructor. +s_ZoneIterator::s_ZoneIterator() : cppobj(ZoneIteratorPtr()) { +} + +namespace { +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer ZoneIteratorContainer; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// +PyObject* ZoneIterator_GetNextRRset(PyObject* po_self, PyObject*) { + s_ZoneIterator* self = static_cast(po_self); + try { + isc::dns::ConstRRsetPtr rrset = self->cppobj->getNextRRset(); + if (!rrset) { + Py_RETURN_NONE; + } + return (isc::dns::python::createRRsetObject(*rrset)); + } catch (const isc::Exception& isce) { + // isc::Unexpected is thrown when we call getNextRRset() when we are + // already done iterating ('iterating past end') + // We could also simply return None again + PyErr_SetString(getDataSourceException("Error"), isce.what()); + return (NULL); + } + +} + +// General creation and destruction +int ZoneIterator_init(s_ZoneIterator* self, PyObject* args); +void ZoneIterator_destroy(s_ZoneIterator* self); + +// These are the functions we export +// + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef ZoneIterator_methods[] = { + { "get_next_rrset", ZoneIterator_GetNextRRset, METH_NOARGS, "TODO" }, + { NULL, NULL, 0, NULL } +}; + +// This is a template of typical code logic of python class initialization +// with C++ backend. You'll need to adjust it according to details of the +// actual C++ class. +int +ZoneIterator_init(s_ZoneIterator* self, PyObject* args) { + // can't be called directly + PyErr_SetString(PyExc_TypeError, + "ZoneIterator cannot be constructed directly"); + + return (-1); +} + +// This is a template of typical code logic of python object destructor. +// In many cases you can use it without modification, but check that carefully. +void +ZoneIterator_destroy(s_ZoneIterator* const self) { + //delete self->cppobj; + //self->cppobj = NULL; + Py_TYPE(self)->tp_free(self); +} + +} // end of unnamed namespace + +namespace isc { +namespace datasrc { +namespace python { +PyTypeObject zoneiterator_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "datasrc.ZoneIterator", + sizeof(s_ZoneIterator), // tp_basicsize + 0, // tp_itemsize + reinterpret_cast(ZoneIterator_destroy), // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + NULL, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The ZoneIterator class objects is...(TODO COMPLETE THIS)", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + ZoneIterator_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + reinterpret_cast(ZoneIterator_init),// tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +// Module Initialization, all statics are initialized here +bool +initModulePart_ZoneIterator(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&zoneiterator_type) < 0) { + return (false); + } + void* zip = &zoneiterator_type; + if (PyModule_AddObject(mod, "ZoneIterator", static_cast(zip)) < 0) { + return (false); + } + Py_INCREF(&zoneiterator_type); + + return (true); +} + +PyObject* +createZoneIteratorObject(isc::datasrc::ZoneIteratorPtr source) { + s_ZoneIterator* py_zi = static_cast( + zoneiterator_type.tp_alloc(&zoneiterator_type, 0)); + if (py_zi != NULL) { + py_zi->cppobj = source; + } + return (py_zi); +} + +} // namespace python +} // namespace datasrc +} // namespace isc + diff --git a/src/lib/python/isc/datasrc/iterator_python.h b/src/lib/python/isc/datasrc/iterator_python.h new file mode 100644 index 0000000000..7ad4d36531 --- /dev/null +++ b/src/lib/python/isc/datasrc/iterator_python.h @@ -0,0 +1,47 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_DATASRC_ITERATOR_H +#define __PYTHON_DATASRC_ITERATOR_H 1 + +#include + +namespace isc { +namespace datasrc { +class DataSourceClient; + +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_ZoneIterator : public PyObject { +public: + s_ZoneIterator(); + ZoneIteratorPtr cppobj; +}; + +extern PyTypeObject zoneiterator_type; + +bool initModulePart_ZoneIterator(PyObject* mod); + +PyObject* createZoneIteratorObject(isc::datasrc::ZoneIteratorPtr source); + + +} // namespace python +} // namespace datasrc +} // namespace isc +#endif // __PYTHON_DATASRC_ITERATOR_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am index 6f6d15731d..4bb36f4b8e 100644 --- a/src/lib/python/isc/datasrc/tests/Makefile.am +++ b/src/lib/python/isc/datasrc/tests/Makefile.am @@ -1,16 +1,18 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ -PYTESTS = master_test.py sqlite3_ds_test.py +# old tests, TODO remove or change to use new API? +#PYTESTS = master_test.py sqlite3_ds_test.py +PYTESTS = datasrc_test.py EXTRA_DIST = $(PYTESTS) EXTRA_DIST += testdata/brokendb.sqlite3 EXTRA_DIST += testdata/example.com.sqlite3 -CLEANFILES = $(abs_builddir)/example.com.out.sqlite3 +CLEANFILES = $(abs_builddir)/rwtest.sqlite3.copied # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS @@ -23,7 +25,7 @@ endif for pytest in $(PYTESTS) ; do \ 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 \ + 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/python/isc/datasrc/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs \ TESTDATA_PATH=$(abs_srcdir)/testdata \ TESTDATA_WRITE_PATH=$(abs_builddir) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py new file mode 100644 index 0000000000..f9f875f4f0 --- /dev/null +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -0,0 +1,209 @@ +# 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. + +import isc.log +import isc.datasrc +import isc.dns +import unittest +import os +import shutil + +TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep +TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep + +READ_ZONE_DB_FILE = TESTDATA_PATH + "example.com.sqlite3" +BROKEN_DB_FILE = TESTDATA_PATH + "brokendb.sqlite3" +WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "rwtest.sqlite3.copied" +NEW_DB_FILE = TESTDATA_WRITE_PATH + "new_db.sqlite3" + +class DataSrcClient(unittest.TestCase): + + def atest_iterate(self): + dsc = isc.datasrc.DataSourceClient(READ_ZONE_DB_FILE) + + # for RRSIGS, the TTL's are currently modified. This test should + # start failing when we fix that. + rrs = dsc.get_iterator(isc.dns.Name("sql1.example.com.")) + self.assertEqual("sql1.example.com. 3600 IN DNSKEY 256 3 5 AwEAAdYdRhBAEY67R/8G1N5AjGF6asIiNh/pNGeQ8xDQP13JN2lo+sNqWcmpYNhuVqRbLB+mamsU1XcCICSBvAlSmfz/ZUdafX23knArTlALxMmspcfdpqun3Yr3YYnztuj06rV7RqmveYckWvAUXVYMSMQZfJ305fs0dE/xLztL/CzZ\nsql1.example.com. 3600 IN DNSKEY 257 3 5 AwEAAbaKDSa9XEFTsjSYpUTHRotTS9Tz3krfDucugW5UokGQKC26QlyHXlPTZkC+aRFUs/dicJX2kopndLcnlNAPWiKnKtrsFSCnIJDBZIyvcKq+9RXmV3HK3bUdHnQZ88IZWBRmWKfZ6wnzHo53kdYKAemTErkztaX3lRRPLYWpxRcDPEjysXT3Lh0vfL5D+CIO1yKw/q7C+v6+/kYAxc2lfbNE3HpklSuF+dyX4nXxWgzbcFuLz5Bwfq6ZJ9RYe/kNkA0uMWNa1KkGeRh8gg22kgD/KT5hPTnpezUWLvoY5Qc7IB3T0y4n2JIwiF2ZrZYVrWgDjRWAzGsxJiJyjd6w2k0=\n", rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\nsql1.example.com. 3600 IN NS dns02.example.com.\nsql1.example.com. 3600 IN NS dns03.example.com.\n", rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 7200 IN NSEC www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY\n", rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 3600 IN RRSIG SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG DNSKEY 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG DNSKEY 5 3 3600 20100322084536 20100220084536 33313 sql1.example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 3600 IN SOA master.example.com. admin.example.com. 678 3600 1800 2419200 7200\n", rrs.get_next_rrset().to_text()) + self.assertEqual("www.sql1.example.com. 3600 IN A 192.0.2.100\n", rrs.get_next_rrset().to_text()) + self.assertEqual("www.sql1.example.com. 7200 IN NSEC sql1.example.com. A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("www.sql1.example.com. 3600 IN RRSIG A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nwww.sql1.example.com. 3600 IN RRSIG NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + self.assertEqual(None, rrs.get_next_rrset()) + # TODO should we catch this (iterating past end) and just return None + # instead of failing? + self.assertRaises(isc.datasrc.Error, rrs.get_next_rrset) + + rrs = dsc.get_iterator(isc.dns.Name("example.com")) + self.assertEqual("*.wild.example.com. 3600 IN A 192.0.2.255\n", rrs.get_next_rrset().to_text()) + self.assertEqual("*.wild.example.com. 7200 IN NSEC www.example.com. A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("*.wild.example.com. 3600 IN RRSIG A 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n*.wild.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + self.assertEqual("cname-ext.example.com. 3600 IN CNAME www.sql1.example.com.\n", rrs.get_next_rrset().to_text()) + self.assertEqual("cname-ext.example.com. 7200 IN NSEC cname-int.example.com. CNAME RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("cname-ext.example.com. 3600 IN RRSIG CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\ncname-ext.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + self.assertEqual("cname-int.example.com. 3600 IN CNAME www.example.com.\n", rrs.get_next_rrset().to_text()) + self.assertEqual("cname-int.example.com. 7200 IN NSEC dname.example.com. CNAME RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("cname-int.example.com. 3600 IN RRSIG CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\ncname-int.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + self.assertEqual("dname.example.com. 3600 IN DNAME sql1.example.com.\n", rrs.get_next_rrset().to_text()) + self.assertEqual("dname.example.com. 7200 IN NSEC dns01.example.com. DNAME RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("dname.example.com. 3600 IN RRSIG DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\ndname.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + self.assertEqual("dns01.example.com. 3600 IN A 192.0.2.1\n", rrs.get_next_rrset().to_text()) + self.assertEqual("dns01.example.com. 7200 IN NSEC dns02.example.com. A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("dns01.example.com. 3600 IN RRSIG A 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\ndns01.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + # there are more than 80 RRs in this zone... let's just count the rest + # hmm. this makes me think we might want a real iterator returned from get_iterator() + rrset = rrs.get_next_rrset() + count = 0 + while rrset is not None: + count = count + 1 + rrset = rrs.get_next_rrset() + self.assertEqual(40, count) + # TODO should we catch this (iterating past end) and just return None + # instead of failing? + self.assertRaises(isc.datasrc.Error, rrs.get_next_rrset) + + self.assertRaises(TypeError, dsc.get_iterator, "asdf") + + def test_find(self): + dsc = isc.datasrc.DataSourceClient(READ_ZONE_DB_FILE) + + result, finder = dsc.find_zone(isc.dns.Name("example.com")) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual(isc.dns.RRClass.IN(), finder.get_class()) + self.assertEqual("example.com.", finder.get_origin().to_text()) + + result, rrset = finder.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + + result, rrset = finder.find(isc.dns.Name("www.sql1.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.DELEGATION, result) + self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\nsql1.example.com. 3600 IN NS dns02.example.com.\nsql1.example.com. 3600 IN NS dns03.example.com.\n", rrset.to_text()) + + result, rrset = finder.find(isc.dns.Name("doesnotexist.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.NXDOMAIN, result) + self.assertEqual(None, rrset) + + result, rrset = finder.find(isc.dns.Name("www.some.other.domain"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.NXDOMAIN, result) + self.assertEqual(None, rrset) + + result, rrset = finder.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.TXT(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.NXRRSET, result) + self.assertEqual(None, rrset) + + result, rrset = finder.find(isc.dns.Name("cname-ext.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.CNAME, result) + self.assertEqual("cname-ext.example.com. 3600 IN CNAME www.sql1.example.com.\n", rrset.to_text()) + + self.assertRaises(TypeError, finder.find, + "foo", + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertRaises(TypeError, finder.find, + isc.dns.Name("cname-ext.example.com"), + "foo", + None, + finder.FIND_DEFAULT) + self.assertRaises(TypeError, finder.find, + isc.dns.Name("cname-ext.example.com"), + isc.dns.RRType.A(), + None, + "foo") + +class DataSrcUpdater(unittest.TestCase): + + def setUp(self): + # Make a fresh copy of the writable database with all original content + shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE) + + + def test_update(self): + dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE) + + # first make sure, through a separate finder, that some record exists + result, finder = dsc.find_zone(isc.dns.Name("example.com")) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual(isc.dns.RRClass.IN(), finder.get_class()) + self.assertEqual("example.com.", finder.get_origin().to_text()) + + result, rrset = finder.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + + rrset_to_delete = rrset; + # can't delete rrset with associated sig + rrset_to_delete.remove_rrsig() + + result, rrset = finder.find(isc.dns.Name("doesnotexist.example.com"), + isc.dns.RRType.TXT(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.NXDOMAIN, result) + self.assertEqual(None, rrset) + + + updater = dsc.get_updater(isc.dns.Name("example.com"), True) + updater.delete_rrset(rrset_to_delete) + updater.commit() + + result, rrset = finder.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.NXDOMAIN, result) + self.assertEqual(None, rrset) + + # now add it again + updater = dsc.get_updater(isc.dns.Name("example.com"), True) + updater.add_rrset(rrset_to_delete) + updater.commit() + + result, rrset = finder.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + + +if __name__ == "__main__": + isc.log.init("bind10") + unittest.main() diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc new file mode 100644 index 0000000000..2108b73844 --- /dev/null +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -0,0 +1,230 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +// Enable this if you use s# variants with PyArg_ParseTuple(), see +// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers +//#define PY_SSIZE_T_CLEAN + +// Python.h needs to be placed at the head of the program file, see: +// http://docs.python.org/py3k/extending/extending.html#a-simple-example +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include "datasrc.h" +#include "updater_python.h" + +using namespace std; +using namespace isc::util::python; +using namespace isc::datasrc; +using namespace isc::datasrc::python; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// Zone Updater +// + +// Trivial constructor. +s_ZoneUpdater::s_ZoneUpdater() : cppobj(ZoneUpdaterPtr()) { +} + +namespace { +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer ZoneUpdaterContainer; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +// General creation and destruction +int ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args); +void ZoneUpdater_destroy(s_ZoneUpdater* self); + +// These are the functions we export +// +PyObject* ZoneUpdater_AddRRset(PyObject* po_self, PyObject* args) { + // TODO err handling + s_ZoneUpdater* const self = static_cast(po_self); + PyObject* rrset_obj; + if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { + self->cppobj->addRRset(isc::dns::python::PyRRset_ToRRset(rrset_obj)); + Py_RETURN_NONE; + } else { + return (NULL); + } +} + +PyObject* ZoneUpdater_DeleteRRset(PyObject* po_self, PyObject* args) { + // TODO err handling + s_ZoneUpdater* const self = static_cast(po_self); + PyObject* rrset_obj; + if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { + self->cppobj->deleteRRset(isc::dns::python::PyRRset_ToRRset(rrset_obj)); + Py_RETURN_NONE; + } else { + return (NULL); + } +} + +PyObject* ZoneUpdater_Commit(PyObject* po_self, PyObject*) { + s_ZoneUpdater* const self = static_cast(po_self); + self->cppobj->commit(); + Py_RETURN_NONE; +} + + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef ZoneUpdater_methods[] = { +/* { "get_finder", ZoneUpdater_GetFinder, METH_NOARGS, "TODO" },*/ + { "add_rrset", ZoneUpdater_AddRRset, METH_VARARGS, "TODO" }, + { "delete_rrset", ZoneUpdater_DeleteRRset, METH_VARARGS, "TODO" }, + { "commit", ZoneUpdater_Commit, METH_NOARGS, "TODO" }, + { NULL, NULL, 0, NULL } +}; + +// This is a template of typical code logic of python class initialization +// with C++ backend. You'll need to adjust it according to details of the +// actual C++ class. +int +ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args) { + // can't be called directly + PyErr_SetString(PyExc_TypeError, + "ZoneUpdater cannot be constructed directly"); + + return (-1); +} + +// This is a template of typical code logic of python object destructor. +// In many cases you can use it without modification, but check that carefully. +void +ZoneUpdater_destroy(s_ZoneUpdater* const self) { + //delete self->cppobj; + //self->cppobj = NULL; + Py_TYPE(self)->tp_free(self); +} + +} // end of unnamed namespace + +namespace isc { +namespace datasrc { +namespace python { +PyTypeObject zoneupdater_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "datasrc.ZoneUpdater", + sizeof(s_ZoneUpdater), // tp_basicsize + 0, // tp_itemsize + reinterpret_cast(ZoneUpdater_destroy), // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + NULL, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The ZoneUpdater class objects is...(TODO COMPLETE THIS)", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + ZoneUpdater_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + reinterpret_cast(ZoneUpdater_init),// tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +// Module Initialization, all statics are initialized here +bool +initModulePart_ZoneUpdater(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&zoneupdater_type) < 0) { + return (false); + } + void* zip = &zoneupdater_type; + if (PyModule_AddObject(mod, "ZoneUpdater", static_cast(zip)) < 0) { + return (false); + } + Py_INCREF(&zoneupdater_type); + + return (true); +} + +PyObject* +createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source) { + s_ZoneUpdater* py_zi = static_cast( + zoneupdater_type.tp_alloc(&zoneupdater_type, 0)); + if (py_zi != NULL) { + py_zi->cppobj = source; + } + return (py_zi); +} + +} // namespace python +} // namespace datasrc +} // namespace isc + diff --git a/src/lib/python/isc/datasrc/updater_python.h b/src/lib/python/isc/datasrc/updater_python.h new file mode 100644 index 0000000000..e3aec21b48 --- /dev/null +++ b/src/lib/python/isc/datasrc/updater_python.h @@ -0,0 +1,47 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __PYTHON_DATASRC_UPDATER_H +#define __PYTHON_DATASRC_UPDATER_H 1 + +#include + +namespace isc { +namespace datasrc { +class DataSourceClient; + +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_ZoneUpdater : public PyObject { +public: + s_ZoneUpdater(); + ZoneUpdaterPtr cppobj; +}; + +extern PyTypeObject zoneupdater_type; + +bool initModulePart_ZoneUpdater(PyObject* mod); + +PyObject* createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source); + + +} // namespace python +} // namespace datasrc +} // namespace isc +#endif // __PYTHON_DATASRC_UPDATER_H + +// Local Variables: +// mode: c++ +// End: From 8634aa9cab1c2205629540b4d99b88847148bd80 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 13 Sep 2011 16:46:17 +0200 Subject: [PATCH 737/974] [1179] Makefile fix --- src/lib/python/isc/dns/Makefile.am | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/lib/python/isc/dns/Makefile.am diff --git a/src/lib/python/isc/dns/Makefile.am b/src/lib/python/isc/dns/Makefile.am new file mode 100644 index 0000000000..161c2a5d09 --- /dev/null +++ b/src/lib/python/isc/dns/Makefile.am @@ -0,0 +1,7 @@ +python_PYTHON = __init__.py + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) + From b6261f09b53af42a26d88fd50d74ab1e84524cce Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 14 Sep 2011 11:20:44 +0200 Subject: [PATCH 738/974] [1179] exceptions --- src/lib/python/isc/datasrc/client_python.cc | 38 ++++- src/lib/python/isc/datasrc/datasrc.cc | 5 + src/lib/python/isc/datasrc/finder_python.cc | 88 +++++++----- src/lib/python/isc/datasrc/iterator_python.cc | 47 +++--- .../python/isc/datasrc/tests/datasrc_test.py | 14 +- src/lib/python/isc/datasrc/updater_python.cc | 134 ++++++++++-------- 6 files changed, 193 insertions(+), 133 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index 049e8a472e..746da4187e 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -82,12 +83,17 @@ DataSourceClient_FindZone(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); PyObject *name; if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name)) { - DataSourceClient::FindResult find_result( - self->cppobj->findZone(isc::dns::python::PyName_ToName(name))); + try { + DataSourceClient::FindResult find_result( + self->cppobj->findZone(isc::dns::python::PyName_ToName(name))); - result::Result r = find_result.code; - ZoneFinderPtr zfp = find_result.zone_finder; - return Py_BuildValue("IO", r, createZoneFinderObject(zfp)); + result::Result r = find_result.code; + ZoneFinderPtr zfp = find_result.zone_finder; + return Py_BuildValue("IO", r, createZoneFinderObject(zfp)); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } } else { return (NULL); } @@ -98,7 +104,16 @@ DataSourceClient_GetIterator(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); PyObject *name_obj; if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name_obj)) { - return (createZoneIteratorObject(self->cppobj->getIterator(isc::dns::python::PyName_ToName(name_obj)))); + try { + return (createZoneIteratorObject(self->cppobj->getIterator(isc::dns::python::PyName_ToName(name_obj)))); + } catch (const isc::NotImplemented& ne) { + PyErr_SetString(getDataSourceException("NotImplemented"), ne.what()); + } catch (const DataSourceError& dse) { + PyErr_SetString(getDataSourceException("Error"), dse.what()); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } } else { return (NULL); } @@ -111,7 +126,16 @@ DataSourceClient_GetUpdater(PyObject* po_self, PyObject* args) { PyObject *replace_obj; if (PyArg_ParseTuple(args, "O!O", &isc::dns::python::name_type, &name_obj, &replace_obj) && PyBool_Check(replace_obj)) { bool replace = (replace_obj != Py_False); - return (createZoneUpdaterObject(self->cppobj->getUpdater(isc::dns::python::PyName_ToName(name_obj), replace))); + try { + return (createZoneUpdaterObject(self->cppobj->getUpdater(isc::dns::python::PyName_ToName(name_obj), replace))); + } catch (const isc::NotImplemented& ne) { + PyErr_SetString(getDataSourceException("NotImplemented"), ne.what()); + } catch (const DataSourceError& dse) { + PyErr_SetString(getDataSourceException("Error"), dse.what()); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } } else { return (NULL); } diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc index 225137cd84..88cad283da 100644 --- a/src/lib/python/isc/datasrc/datasrc.cc +++ b/src/lib/python/isc/datasrc/datasrc.cc @@ -60,6 +60,7 @@ getDataSourceException(const char* ex_name) { namespace { PyObject* po_DataSourceError; +PyObject* po_NotImplemented; PyModuleDef iscDataSrc = { { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, @@ -108,6 +109,10 @@ PyInit_datasrc(void) { po_DataSourceError = PyErr_NewException("isc.datasrc.Error", NULL, NULL); PyObjectContainer(po_DataSourceError).installToModule(mod, "Error"); + po_NotImplemented = PyErr_NewException("isc.datasrc.NotImplemented", + NULL, NULL); + PyObjectContainer(po_NotImplemented).installToModule(mod, + "NotImplemented"); } catch (...) { Py_DECREF(mod); return (NULL); diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index eedd270eb2..337d4a6ef9 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -71,19 +72,41 @@ typedef CPPPyObjectContainer ZoneFinderContainer; // // General creation and destruction -int ZoneFinder_init(s_ZoneFinder* self, PyObject* args); -void ZoneFinder_destroy(s_ZoneFinder* self); +int +ZoneFinder_init(s_ZoneFinder* self, PyObject* args) { + // can't be called directly + PyErr_SetString(PyExc_TypeError, + "ZoneFinder cannot be constructed directly"); + + return (-1); +} + +void +ZoneFinder_destroy(s_ZoneFinder* const self) { + // cppobj is shared ptr, so no need for explicit delete + Py_TYPE(self)->tp_free(self); +} // These are the functions we export // PyObject* ZoneFinder_GetClass(PyObject* po_self, PyObject*) { s_ZoneFinder* self = static_cast(po_self); - return (isc::dns::python::createRRClassObject(self->cppobj->getClass())); + try { + return (isc::dns::python::createRRClassObject(self->cppobj->getClass())); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } } PyObject* ZoneFinder_GetOrigin(PyObject* po_self, PyObject*) { s_ZoneFinder* self = static_cast(po_self); - return (isc::dns::python::createNameObject(self->cppobj->getOrigin())); + try { + return (isc::dns::python::createNameObject(self->cppobj->getOrigin())); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } } PyObject* ZoneFinder_Find(PyObject* po_self, PyObject* args) { @@ -95,20 +118,28 @@ PyObject* ZoneFinder_Find(PyObject* po_self, PyObject* args) { if (PyArg_ParseTuple(args, "O!O!OI", &isc::dns::python::name_type, &name, &isc::dns::python::rrtype_type, &rrtype, &target, &options_int)) { - ZoneFinder::FindOptions options = static_cast(options_int); - ZoneFinder::FindResult find_result( - self->cppobj->find(isc::dns::python::PyName_ToName(name), - isc::dns::python::PyRRType_ToRRType(rrtype), - NULL, - options - )); - ZoneFinder::Result r = find_result.code; - isc::dns::ConstRRsetPtr rrsp = find_result.rrset; - if (rrsp) { - return Py_BuildValue("IO", r, isc::dns::python::createRRsetObject(*rrsp)); - } else { - Py_INCREF(Py_None); - return Py_BuildValue("IO", r, Py_None); + try { + ZoneFinder::FindOptions options = static_cast(options_int); + ZoneFinder::FindResult find_result( + self->cppobj->find(isc::dns::python::PyName_ToName(name), + isc::dns::python::PyRRType_ToRRType(rrtype), + NULL, + options + )); + ZoneFinder::Result r = find_result.code; + isc::dns::ConstRRsetPtr rrsp = find_result.rrset; + if (rrsp) { + return Py_BuildValue("IO", r, isc::dns::python::createRRsetObject(*rrsp)); + } else { + Py_INCREF(Py_None); + return Py_BuildValue("IO", r, Py_None); + } + } catch (const DataSourceError& dse) { + PyErr_SetString(getDataSourceException("Error"), dse.what()); + return (NULL); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); } } else { return (NULL); @@ -132,27 +163,6 @@ PyMethodDef ZoneFinder_methods[] = { { NULL, NULL, 0, NULL } }; -// This is a template of typical code logic of python class initialization -// with C++ backend. You'll need to adjust it according to details of the -// actual C++ class. -int -ZoneFinder_init(s_ZoneFinder* self, PyObject* args) { - // can't be called directly - PyErr_SetString(PyExc_TypeError, - "ZoneFinder cannot be constructed directly"); - - return (-1); -} - -// This is a template of typical code logic of python object destructor. -// In many cases you can use it without modification, but check that carefully. -void -ZoneFinder_destroy(s_ZoneFinder* const self) { - //delete self->cppobj; - //self->cppobj = NULL; - Py_TYPE(self)->tp_free(self); -} - } // end of unnamed namespace namespace isc { diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index 4d9c5c7de2..bb4fb137de 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -61,6 +61,25 @@ namespace { // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer ZoneIteratorContainer; + +// General creation and destruction +int +ZoneIterator_init(s_ZoneIterator* self, PyObject* args) { + // can't be called directly + PyErr_SetString(PyExc_TypeError, + "ZoneIterator cannot be constructed directly"); + + return (-1); +} + +// This is a template of typical code logic of python object destructor. +// In many cases you can use it without modification, but check that carefully. +void +ZoneIterator_destroy(s_ZoneIterator* const self) { + // cppobj is a shared ptr so no need to delete that + Py_TYPE(self)->tp_free(self); +} + // // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other @@ -79,14 +98,12 @@ PyObject* ZoneIterator_GetNextRRset(PyObject* po_self, PyObject*) { // We could also simply return None again PyErr_SetString(getDataSourceException("Error"), isce.what()); return (NULL); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); } - } -// General creation and destruction -int ZoneIterator_init(s_ZoneIterator* self, PyObject* args); -void ZoneIterator_destroy(s_ZoneIterator* self); - // These are the functions we export // @@ -104,26 +121,6 @@ PyMethodDef ZoneIterator_methods[] = { { NULL, NULL, 0, NULL } }; -// This is a template of typical code logic of python class initialization -// with C++ backend. You'll need to adjust it according to details of the -// actual C++ class. -int -ZoneIterator_init(s_ZoneIterator* self, PyObject* args) { - // can't be called directly - PyErr_SetString(PyExc_TypeError, - "ZoneIterator cannot be constructed directly"); - - return (-1); -} - -// This is a template of typical code logic of python object destructor. -// In many cases you can use it without modification, but check that carefully. -void -ZoneIterator_destroy(s_ZoneIterator* const self) { - //delete self->cppobj; - //self->cppobj = NULL; - Py_TYPE(self)->tp_free(self); -} } // end of unnamed namespace diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py index f9f875f4f0..e0492b781b 100644 --- a/src/lib/python/isc/datasrc/tests/datasrc_test.py +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -30,7 +30,7 @@ NEW_DB_FILE = TESTDATA_WRITE_PATH + "new_db.sqlite3" class DataSrcClient(unittest.TestCase): - def atest_iterate(self): + def test_iterate(self): dsc = isc.datasrc.DataSourceClient(READ_ZONE_DB_FILE) # for RRSIGS, the TTL's are currently modified. This test should @@ -169,8 +169,6 @@ class DataSrcUpdater(unittest.TestCase): self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) rrset_to_delete = rrset; - # can't delete rrset with associated sig - rrset_to_delete.remove_rrsig() result, rrset = finder.find(isc.dns.Name("doesnotexist.example.com"), isc.dns.RRType.TXT(), @@ -179,8 +177,13 @@ class DataSrcUpdater(unittest.TestCase): self.assertEqual(finder.NXDOMAIN, result) self.assertEqual(None, rrset) - + # can't delete rrset with associated sig. Abuse that to force an + # exception first, then remove the sig, then delete the record updater = dsc.get_updater(isc.dns.Name("example.com"), True) + self.assertRaises(isc.datasrc.Error, updater.delete_rrset, rrset_to_delete) + + rrset_to_delete.remove_rrsig() + updater.delete_rrset(rrset_to_delete) updater.commit() @@ -196,6 +199,9 @@ class DataSrcUpdater(unittest.TestCase): updater.add_rrset(rrset_to_delete) updater.commit() + # second commit should throw + self.assertRaises(isc.datasrc.Error, updater.commit) + result, rrset = finder.find(isc.dns.Name("www.example.com"), isc.dns.RRType.A(), None, diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index 2108b73844..8bac405f3f 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -67,62 +68,6 @@ typedef CPPPyObjectContainer ZoneUpdaterContainer; // // General creation and destruction -int ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args); -void ZoneUpdater_destroy(s_ZoneUpdater* self); - -// These are the functions we export -// -PyObject* ZoneUpdater_AddRRset(PyObject* po_self, PyObject* args) { - // TODO err handling - s_ZoneUpdater* const self = static_cast(po_self); - PyObject* rrset_obj; - if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { - self->cppobj->addRRset(isc::dns::python::PyRRset_ToRRset(rrset_obj)); - Py_RETURN_NONE; - } else { - return (NULL); - } -} - -PyObject* ZoneUpdater_DeleteRRset(PyObject* po_self, PyObject* args) { - // TODO err handling - s_ZoneUpdater* const self = static_cast(po_self); - PyObject* rrset_obj; - if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { - self->cppobj->deleteRRset(isc::dns::python::PyRRset_ToRRset(rrset_obj)); - Py_RETURN_NONE; - } else { - return (NULL); - } -} - -PyObject* ZoneUpdater_Commit(PyObject* po_self, PyObject*) { - s_ZoneUpdater* const self = static_cast(po_self); - self->cppobj->commit(); - Py_RETURN_NONE; -} - - -// These are the functions we export -// For a minimal support, we don't need them. - -// This list contains the actual set of functions we have in -// python. Each entry has -// 1. Python method name -// 2. Our static function here -// 3. Argument type -// 4. Documentation -PyMethodDef ZoneUpdater_methods[] = { -/* { "get_finder", ZoneUpdater_GetFinder, METH_NOARGS, "TODO" },*/ - { "add_rrset", ZoneUpdater_AddRRset, METH_VARARGS, "TODO" }, - { "delete_rrset", ZoneUpdater_DeleteRRset, METH_VARARGS, "TODO" }, - { "commit", ZoneUpdater_Commit, METH_NOARGS, "TODO" }, - { NULL, NULL, 0, NULL } -}; - -// This is a template of typical code logic of python class initialization -// with C++ backend. You'll need to adjust it according to details of the -// actual C++ class. int ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args) { // can't be called directly @@ -136,11 +81,84 @@ ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args) { // In many cases you can use it without modification, but check that carefully. void ZoneUpdater_destroy(s_ZoneUpdater* const self) { - //delete self->cppobj; - //self->cppobj = NULL; + // cppobj is a shared ptr so should take care of itself Py_TYPE(self)->tp_free(self); } +// These are the functions we export +// +PyObject* ZoneUpdater_AddRRset(PyObject* po_self, PyObject* args) { + // TODO err handling + s_ZoneUpdater* const self = static_cast(po_self); + PyObject* rrset_obj; + if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { + try { + self->cppobj->addRRset(isc::dns::python::PyRRset_ToRRset(rrset_obj)); + Py_RETURN_NONE; + } catch (const DataSourceError& dse) { + PyErr_SetString(getDataSourceException("Error"), dse.what()); + return (NULL); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } + } else { + return (NULL); + } +} + +PyObject* ZoneUpdater_DeleteRRset(PyObject* po_self, PyObject* args) { + // TODO err handling + s_ZoneUpdater* const self = static_cast(po_self); + PyObject* rrset_obj; + if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { + try { + self->cppobj->deleteRRset(isc::dns::python::PyRRset_ToRRset(rrset_obj)); + Py_RETURN_NONE; + } catch (const DataSourceError& dse) { + PyErr_SetString(getDataSourceException("Error"), dse.what()); + return (NULL); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } + } else { + return (NULL); + } +} + +PyObject* ZoneUpdater_Commit(PyObject* po_self, PyObject*) { + s_ZoneUpdater* const self = static_cast(po_self); + try { + self->cppobj->commit(); + Py_RETURN_NONE; + } catch (const DataSourceError& dse) { + PyErr_SetString(getDataSourceException("Error"), dse.what()); + return (NULL); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } +} + + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef ZoneUpdater_methods[] = { +/*TODO { "get_finder", ZoneUpdater_GetFinder, METH_NOARGS, "TODO" },*/ + { "add_rrset", ZoneUpdater_AddRRset, METH_VARARGS, "TODO" }, + { "delete_rrset", ZoneUpdater_DeleteRRset, METH_VARARGS, "TODO" }, + { "commit", ZoneUpdater_Commit, METH_NOARGS, "TODO" }, + { NULL, NULL, 0, NULL } +}; + } // end of unnamed namespace namespace isc { From 54c6127e005c8e3dd82cd97d49aca23f5a5d8029 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 14 Sep 2011 16:56:28 +0200 Subject: [PATCH 739/974] [1179] pydoc with script from #933 --- src/lib/python/isc/datasrc/client_inc.cc | 176 +++++++++++++++ src/lib/python/isc/datasrc/client_python.cc | 18 +- src/lib/python/isc/datasrc/finder_inc.cc | 105 +++++++++ src/lib/python/isc/datasrc/finder_python.cc | 18 +- src/lib/python/isc/datasrc/iterator_inc.cc | 33 +++ src/lib/python/isc/datasrc/iterator_python.cc | 9 +- src/lib/python/isc/datasrc/updater_inc.cc | 203 ++++++++++++++++++ src/lib/python/isc/datasrc/updater_python.cc | 20 +- 8 files changed, 557 insertions(+), 25 deletions(-) create mode 100644 src/lib/python/isc/datasrc/client_inc.cc create mode 100644 src/lib/python/isc/datasrc/finder_inc.cc create mode 100644 src/lib/python/isc/datasrc/iterator_inc.cc create mode 100644 src/lib/python/isc/datasrc/updater_inc.cc diff --git a/src/lib/python/isc/datasrc/client_inc.cc b/src/lib/python/isc/datasrc/client_inc.cc new file mode 100644 index 0000000000..a126cf3757 --- /dev/null +++ b/src/lib/python/isc/datasrc/client_inc.cc @@ -0,0 +1,176 @@ +namespace { + +const char* const DataSourceClient_doc = "\ +The base class of data source clients.\n\ +\n\ +This is an abstract base class that defines the common interface for\n\ +various types of data source clients. A data source client is a top\n\ +level access point to a data source, allowing various operations on\n\ +the data source such as lookups, traversing or updates. The client\n\ +class itself has limited focus and delegates the responsibility for\n\ +these specific operations to other classes; in general methods of this\n\ +class act as factories of these other classes.\n\ +\n\ +- InMemoryClient: A client of a conceptual data source that stores all\n\ + necessary data in memory for faster lookups\n\ +- DatabaseClient: A client that uses a real database backend (such as\n\ + an SQL database). It would internally hold a connection to the\n\ + underlying database system.\n\ +\n\ +It is intentional that while the term these derived classes don't\n\ +contain \"DataSource\" unlike their base class. It's also noteworthy\n\ +that the naming of the base class is somewhat redundant because the\n\ +namespace datasrc would indicate that it's related to a data source.\n\ +The redundant naming comes from the observation that namespaces are\n\ +often omitted with using directives, in which case \"Client\" would be\n\ +too generic. On the other hand, concrete derived classes are generally\n\ +not expected to be referenced directly from other modules and\n\ +applications, so we'll give them more concise names such as\n\ +InMemoryClient. A single DataSourceClient object is expected to handle\n\ +only a single RR class even if the underlying data source contains\n\ +records for multiple RR classes. Likewise, (when we support views) a\n\ +DataSourceClient object is expected to handle only a single view.\n\ +\n\ +If the application uses multiple threads, each thread will need to\n\ +create and use a separate DataSourceClient. This is because some\n\ +database backend doesn't allow multiple threads to share the same\n\ +connection to the database.\n\ +\n\ +For a client using an in memory backend, this may result in having a\n\ +multiple copies of the same data in memory, increasing the memory\n\ +footprint substantially. Depending on how to support multiple CPU\n\ +cores for concurrent lookups on the same single data source (which is\n\ +not fully fixed yet, and for which multiple threads may be used), this\n\ +design may have to be revisited. This class (and therefore its derived\n\ +classes) are not copyable. This is because the derived classes would\n\ +generally contain attributes that are not easy to copy (such as a\n\ +large size of in memory data or a network connection to a database\n\ +server). In order to avoid a surprising disruption with a naive copy\n\ +it's prohibited explicitly. For the expected usage of the client\n\ +classes the restriction should be acceptable.\n\ +\n\ +TodoThis class is still not complete. It will need more factory\n\ +methods, e.g. for (re)loading a zone.\n\ +\n\ +DataSourceClient()\n\ +\n\ + Default constructor.\n\ +\n\ + This is intentionally defined as protected as this base class\n\ + should never be instantiated directly.\n\ +\n\ + The constructor of a concrete derived class may throw an\n\ + exception. This interface does not specify which exceptions can\n\ + happen (at least at this moment), and the caller should expect any\n\ + type of exception and react accordingly.\n\ +\n\ +"; + +const char* const DataSourceClient_findZone_doc = "\ +find_zone(name) -> FindResult\n\ +\n\ +Returns a ZoneFinder for a zone that best matches the given name.\n\ +\n\ +- code: The result code of the operation.result.SUCCESS: A zone that\n\ + gives an exact match is foundresult.PARTIALMATCH: A zone whose\n\ + origin is a super domain of name is found (but there is no exact\n\ + match)result.NOTFOUND: For all other cases.\n\ +- result.SUCCESS: A zone that gives an exact match is found\n\ +- result.PARTIALMATCH: A zone whose origin is a super domain of name\n\ + is found (but there is no exact match)\n\ +- result.NOTFOUND: For all other cases.\n\ +- zone_finder: Pointer to a ZoneFinder object for the found zone if\n\ + one is found; otherwise NULL.\n\ +\n\ +A specific derived version of this method may throw an exception. This\n\ +interface does not specify which exceptions can happen (at least at\n\ +this moment), and the caller should expect any type of exception and\n\ +react accordingly.\n\ +\n\ +Parameters:\n\ + name A domain name for which the search is performed.\n\ +\n\ +Return Value(s): A FindResult object enclosing the search result (see\n\ +above).\n\ +"; + +const char* const DataSourceClient_getIterator_doc = "\ +get_iterator(name) -> ZoneIterator\n\ +\n\ +Returns an iterator to the given zone.\n\ +\n\ +This allows for traversing the whole zone. The returned object can\n\ +provide the RRsets one by one.\n\ +\n\ +This throws DataSourceError when the zone does not exist in the\n\ +datasource.\n\ +\n\ +The default implementation throws isc.NotImplemented. This allows for\n\ +easy and fast deployment of minimal custom data sources, where the\n\ +user/implementator doesn't have to care about anything else but the\n\ +actual queries. Also, in some cases, it isn't possible to traverse the\n\ +zone from logic point of view (eg. dynamically generated zone data).\n\ +\n\ +It is not fixed if a concrete implementation of this method can throw\n\ +anything else.\n\ +\n\ +Parameters:\n\ + name The name of zone apex to be traversed. It doesn't do\n\ + nearest match as find_zone.\n\ +\n\ +Return Value(s): Pointer to the iterator.\n\ +"; + +const char* const DataSourceClient_getUpdater_doc = "\ +get_updater(name, replace) -> ZoneUpdater\n\ +\n\ +Return an updater to make updates to a specific zone.\n\ +\n\ +The RR class of the zone is the one that the client is expected to\n\ +handle (see the detailed description of this class).\n\ +\n\ +If the specified zone is not found via the client, a NULL pointer will\n\ +be returned; in other words a completely new zone cannot be created\n\ +using an updater. It must be created beforehand (even if it's an empty\n\ +placeholder) in a way specific to the underlying data source.\n\ +\n\ +Conceptually, the updater will trigger a separate transaction for\n\ +subsequent updates to the zone within the context of the updater (the\n\ +actual implementation of the \"transaction\" may vary for the specific\n\ +underlying data source). Until commit() is performed on the updater,\n\ +the intermediate updates won't affect the results of other methods\n\ +(and the result of the object's methods created by other factory\n\ +methods). Likewise, if the updater is destructed without performing\n\ +commit(), the intermediate updates will be effectively canceled and\n\ +will never affect other methods.\n\ +\n\ +If the underlying data source allows concurrent updates, this method\n\ +can be called multiple times while the previously returned updater(s)\n\ +are still active. In this case each updater triggers a different\n\ +\"transaction\". Normally it would be for different zones for such a\n\ +case as handling multiple incoming AXFR streams concurrently, but this\n\ +interface does not even prohibit an attempt of getting more than one\n\ +updater for the same zone, as long as the underlying data source\n\ +allows such an operation (and any conflict resolution is left to the\n\ +specific derived class implementation).\n\ +\n\ +If replace is true, any existing RRs of the zone will be deleted on\n\ +successful completion of updates (after commit() on the updater); if\n\ +it's false, the existing RRs will be intact unless explicitly deleted\n\ +by delete_r_rset() on the updater.\n\ +\n\ +A data source can be \"read only\" or can prohibit partial updates. In\n\ +such cases this method will result in an isc.NotImplemented exception\n\ +unconditionally or when replace is false).\n\ +\n\ +Exceptions:\n\ + NotImplemented The underlying data source does not support updates.\n\ + DataSourceError Internal error in the underlying data source.\n\ + std.bad_alloc Resource allocation failure.\n\ +\n\ +Parameters:\n\ + name The zone name to be updated\n\ + replace Whether to delete existing RRs before making updates\n\ +\n\ +"; +} // unnamed namespace diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index 746da4187e..1abe82f698 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -40,6 +40,7 @@ #include "finder_python.h" #include "iterator_python.h" #include "updater_python.h" +#include "client_inc.cc" using namespace std; using namespace isc::util::python; @@ -79,7 +80,7 @@ void DataSourceClient_destroy(s_DataSourceClient* self); // These are the functions we export // PyObject* -DataSourceClient_FindZone(PyObject* po_self, PyObject* args) { +DataSourceClient_findZone(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); PyObject *name; if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name)) { @@ -100,7 +101,7 @@ DataSourceClient_FindZone(PyObject* po_self, PyObject* args) { } PyObject* -DataSourceClient_GetIterator(PyObject* po_self, PyObject* args) { +DataSourceClient_getIterator(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); PyObject *name_obj; if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name_obj)) { @@ -120,7 +121,7 @@ DataSourceClient_GetIterator(PyObject* po_self, PyObject* args) { } PyObject* -DataSourceClient_GetUpdater(PyObject* po_self, PyObject* args) { +DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); PyObject *name_obj; PyObject *replace_obj; @@ -150,9 +151,12 @@ DataSourceClient_GetUpdater(PyObject* po_self, PyObject* args) { // 3. Argument type // 4. Documentation PyMethodDef DataSourceClient_methods[] = { - { "find_zone", DataSourceClient_FindZone, METH_VARARGS, "TODO" }, - { "get_iterator", DataSourceClient_GetIterator, METH_VARARGS, "TODO" }, - { "get_updater", DataSourceClient_GetUpdater, METH_VARARGS, "TODO" }, + { "find_zone", reinterpret_cast(DataSourceClient_findZone), METH_VARARGS, + DataSourceClient_findZone_doc }, + { "get_iterator", reinterpret_cast(DataSourceClient_getIterator), METH_VARARGS, + DataSourceClient_getIterator_doc }, + { "get_updater", reinterpret_cast(DataSourceClient_getUpdater), METH_VARARGS, + DataSourceClient_getUpdater_doc }, { NULL, NULL, 0, NULL } }; @@ -231,7 +235,7 @@ PyTypeObject datasourceclient_type = { NULL, // tp_setattro NULL, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - "The DataSourceClient class objects is...(COMPLETE THIS)", + DataSourceClient_doc, NULL, // tp_traverse NULL, // tp_clear NULL, // tp_richcompare diff --git a/src/lib/python/isc/datasrc/finder_inc.cc b/src/lib/python/isc/datasrc/finder_inc.cc new file mode 100644 index 0000000000..d6ffde92ef --- /dev/null +++ b/src/lib/python/isc/datasrc/finder_inc.cc @@ -0,0 +1,105 @@ +namespace { +const char* const ZoneFinder_doc = "\ +The base class to search a zone for RRsets.\n\ +\n\ +The ZoneFinder class is an abstract base class for representing an\n\ +object that performs DNS lookups in a specific zone accessible via a\n\ +data source. In general, different types of data sources (in-memory,\n\ +database-based, etc) define their own derived classes of ZoneFinder,\n\ +implementing ways to retrieve the required data through the common\n\ +interfaces declared in the base class. Each concrete ZoneFinder object\n\ +is therefore (conceptually) associated with a specific zone of one\n\ +specific data source instance.\n\ +\n\ +The origin name and the RR class of the associated zone are available\n\ +via the get_origin() and get_class() methods, respectively.\n\ +\n\ +The most important method of this class is find(), which performs the\n\ +lookup for a given domain and type. See the description of the method\n\ +for details.\n\ +\n\ +It's not clear whether we should request that a zone finder form a\n\ +\"transaction\", that is, whether to ensure the finder is not\n\ +susceptible to changes made by someone else than the creator of the\n\ +finder. If we don't request that, for example, two different lookup\n\ +results for the same name and type can be different if other threads\n\ +or programs make updates to the zone between the lookups. We should\n\ +revisit this point as we gain more experiences.\n\ +\n\ +ZoneFinder()\n\ +\n\ + The default constructor.\n\ +\n\ + This is intentionally defined as protected as this base class\n\ + should never be instantiated (except as part of a derived class).\n\ +\n\ +"; + +const char* const ZoneFinder_getOrigin_doc = "\ +get_origin() -> isc.dns.Name\n\ +\n\ +Return the origin name of the zone.\n\ +\n\ +"; + +const char* const ZoneFinder_getClass_doc = "\ +get_class() -> isc.dns.RRClass\n\ +\n\ +Return the RR class of the zone.\n\ +\n\ +"; + +const char* const ZoneFinder_find_doc = "\ +find(name, type, target=NULL, options=FIND_DEFAULT) -> FindResult\n\ +\n\ +Search the zone for a given pair of domain name and RR type.\n\ +\n\ +- If the search name belongs under a zone cut, it returns the code of\n\ + DELEGATION and the NS RRset at the zone cut.\n\ +- If there is no matching name, it returns the code of NXDOMAIN, and,\n\ + if DNSSEC is requested, the NSEC RRset that proves the non-\n\ + existence.\n\ +- If there is a matching name but no RRset of the search type, it\n\ + returns the code of NXRRSET, and, if DNSSEC is required, the NSEC\n\ + RRset for that name.\n\ +- If there is a CNAME RR of the searched name but there is no RR of\n\ + the searched type of the name (so this type is different from\n\ + CNAME), it returns the code of CNAME and that CNAME RR. Note that if\n\ + the searched RR type is CNAME, it is considered a successful match,\n\ + and the code of SUCCESS will be returned.\n\ +- If the search name matches a delegation point of DNAME, it returns\n\ + the code of DNAME and that DNAME RR.\n\ +- If the target isn't NULL, all RRsets under the domain are inserted\n\ + there and SUCCESS (or NXDOMAIN, in case of empty domain) is returned\n\ + instead of normall processing. This is intended to handle ANY query.\n\ + : this behavior is controversial as we discussed in\n\ + https://lists.isc.org/pipermail/bind10-dev/2011-January/001918.html\n\ + We should revisit the interface before we heavily rely on it. The\n\ + options parameter specifies customized behavior of the search. Their\n\ + semantics is as follows:\n\ +- GLUE_OK Allow search under a zone cut. By default the search will\n\ + stop once it encounters a zone cut. If this option is specified it\n\ + remembers information about the highest zone cut and continues the\n\ + search until it finds an exact match for the given name or it\n\ + detects there is no exact match. If an exact match is found, RRsets\n\ + for that name are searched just like the normal case; otherwise, if\n\ + the search has encountered a zone cut, DELEGATION with the\n\ + information of the highest zone cut will be returned.\n\ +\n\ +A derived version of this method may involve internal resource\n\ +allocation, especially for constructing the resulting RRset, and may\n\ +throw an exception if it fails. It throws DuplicateRRset exception if\n\ +there are duplicate rrsets under the same domain. It should not throw\n\ +other types of exceptions.\n\ +\n\ +Parameters:\n\ + name The domain name to be searched for.\n\ + type The RR type to be searched for.\n\ + target If target is not NULL, insert all RRs under the domain\n\ + into it.\n\ + options The search options.\n\ +\n\ +Return Value(s): A FindResult object enclosing the search result (see\n\ +above).\n\ +"; +} // unnamed namespace diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index 337d4a6ef9..44a4ab5ff2 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -40,6 +40,7 @@ #include "datasrc.h" #include "finder_python.h" +#include "finder_inc.cc" using namespace std; using namespace isc::util::python; @@ -89,7 +90,7 @@ ZoneFinder_destroy(s_ZoneFinder* const self) { // These are the functions we export // -PyObject* ZoneFinder_GetClass(PyObject* po_self, PyObject*) { +PyObject* ZoneFinder_getClass(PyObject* po_self, PyObject*) { s_ZoneFinder* self = static_cast(po_self); try { return (isc::dns::python::createRRClassObject(self->cppobj->getClass())); @@ -99,7 +100,7 @@ PyObject* ZoneFinder_GetClass(PyObject* po_self, PyObject*) { } } -PyObject* ZoneFinder_GetOrigin(PyObject* po_self, PyObject*) { +PyObject* ZoneFinder_getOrigin(PyObject* po_self, PyObject*) { s_ZoneFinder* self = static_cast(po_self); try { return (isc::dns::python::createNameObject(self->cppobj->getOrigin())); @@ -109,7 +110,7 @@ PyObject* ZoneFinder_GetOrigin(PyObject* po_self, PyObject*) { } } -PyObject* ZoneFinder_Find(PyObject* po_self, PyObject* args) { +PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) { s_ZoneFinder* const self = static_cast(po_self); PyObject *name; PyObject *rrtype; @@ -157,9 +158,12 @@ PyObject* ZoneFinder_Find(PyObject* po_self, PyObject* args) { // 3. Argument type // 4. Documentation PyMethodDef ZoneFinder_methods[] = { - { "get_class", ZoneFinder_GetClass, METH_NOARGS, "TODO" }, - { "get_origin", ZoneFinder_GetOrigin, METH_NOARGS, "TODO" }, - { "find", ZoneFinder_Find, METH_VARARGS, "TODO" }, + { "get_origin", reinterpret_cast(ZoneFinder_getOrigin), METH_NOARGS, + ZoneFinder_getOrigin_doc }, + { "get_class", reinterpret_cast(ZoneFinder_getClass), METH_NOARGS, + ZoneFinder_getClass_doc }, + { "find", reinterpret_cast(ZoneFinder_find), METH_VARARGS, + ZoneFinder_find_doc }, { NULL, NULL, 0, NULL } }; @@ -189,7 +193,7 @@ PyTypeObject zonefinder_type = { NULL, // tp_setattro NULL, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - "The ZoneFinder class objects is...(TODO COMPLETE THIS)", + ZoneFinder_doc, NULL, // tp_traverse NULL, // tp_clear NULL, // tp_richcompare diff --git a/src/lib/python/isc/datasrc/iterator_inc.cc b/src/lib/python/isc/datasrc/iterator_inc.cc new file mode 100644 index 0000000000..944fd1795a --- /dev/null +++ b/src/lib/python/isc/datasrc/iterator_inc.cc @@ -0,0 +1,33 @@ +namespace { + +const char* const ZoneIterator_doc = "\ +Read-only iterator to a zone.\n\ +\n\ +You can get an instance of (descendand of) ZoneIterator from\n\ +DataSourceClient.get_iterator() method. The actual concrete\n\ +implementation will be different depending on the actual data source\n\ +used. This is the abstract interface.\n\ +\n\ +There's no way to start iterating from the beginning again or return.\n\ +\n\ +"; + +const char* const ZoneIterator_getNextRRset_doc = "\ +get_next_r_rset() -> isc.dns.ConstRRset\n\ +\n\ +Get next RRset from the zone.\n\ +\n\ +This returns the next RRset in the zone as a shared pointer. The\n\ +shared pointer is used to allow both accessing in-memory data and\n\ +automatic memory management.\n\ +\n\ +Any special order is not guaranteed.\n\ +\n\ +While this can potentially throw anything (including standard\n\ +allocation errors), it should be rare.\n\ +\n\ +Pointer to the next RRset or NULL pointer when the iteration gets to\n\ +the end of the zone.\n\ +\n\ +"; +} // unnamed namespace diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index bb4fb137de..b078876ebc 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -36,6 +36,8 @@ #include "datasrc.h" #include "iterator_python.h" +#include "iterator_inc.cc" + using namespace std; using namespace isc::util::python; using namespace isc::datasrc; @@ -84,7 +86,7 @@ ZoneIterator_destroy(s_ZoneIterator* const self) { // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other // -PyObject* ZoneIterator_GetNextRRset(PyObject* po_self, PyObject*) { +PyObject* ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) { s_ZoneIterator* self = static_cast(po_self); try { isc::dns::ConstRRsetPtr rrset = self->cppobj->getNextRRset(); @@ -117,7 +119,8 @@ PyObject* ZoneIterator_GetNextRRset(PyObject* po_self, PyObject*) { // 3. Argument type // 4. Documentation PyMethodDef ZoneIterator_methods[] = { - { "get_next_rrset", ZoneIterator_GetNextRRset, METH_NOARGS, "TODO" }, + { "get_next_rrset", reinterpret_cast(ZoneIterator_getNextRRset), + METH_NOARGS, ZoneIterator_getNextRRset_doc }, { NULL, NULL, 0, NULL } }; @@ -148,7 +151,7 @@ PyTypeObject zoneiterator_type = { NULL, // tp_setattro NULL, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - "The ZoneIterator class objects is...(TODO COMPLETE THIS)", + ZoneIterator_doc, NULL, // tp_traverse NULL, // tp_clear NULL, // tp_richcompare diff --git a/src/lib/python/isc/datasrc/updater_inc.cc b/src/lib/python/isc/datasrc/updater_inc.cc new file mode 100644 index 0000000000..8903915226 --- /dev/null +++ b/src/lib/python/isc/datasrc/updater_inc.cc @@ -0,0 +1,203 @@ +namespace { + +const char* const ZoneUpdater_doc = "\ +The base class to make updates to a single zone.\n\ +\n\ +On construction, each derived class object will start a\n\ +\"transaction\" for making updates to a specific zone (this means a\n\ +constructor of a derived class would normally take parameters to\n\ +identify the zone to be updated). The underlying realization of a\n\ +\"transaction\" will differ for different derived classes; if it uses\n\ +a general purpose database as a backend, it will involve performing\n\ +some form of \"begin transaction\" statement for the database.\n\ +\n\ +Updates (adding or deleting RRs) are made via add_r_rset() and\n\ +delete_r_rset() methods. Until the commit() method is called the\n\ +changes are local to the updater object. For example, they won't be\n\ +visible via a ZoneFinder object except the one returned by the\n\ +updater's own get_finder() method. The commit() completes the\n\ +transaction and makes the changes visible to others.\n\ +\n\ +This class does not provide an explicit \"rollback\" interface. If\n\ +something wrong or unexpected happens during the updates and the\n\ +caller wants to cancel the intermediate updates, the caller should\n\ +simply destruct the updater object without calling commit(). The\n\ +destructor is supposed to perform the \"rollback\" operation,\n\ +depending on the internal details of the derived class.\n\ +\n\ +This initial implementation provides a quite simple interface of\n\ +adding and deleting RRs (see the description of the related methods).\n\ +It may be revisited as we gain more experiences.\n\ +\n\ +"; +/* +const char* const ZoneUpdater_getFinder_doc = "\ +get_finder() -> ZoneFinder \n\ +\n\ +Return a finder for the zone being updated.\n\ +\n\ +The returned finder provides the functionalities of ZoneFinder for the\n\ +zone as updates are made via the updater. That is, before making any\n\ +update, the finder will be able to find all RRsets that exist in the\n\ +zone at the time the updater is created. If RRsets are added or\n\ +deleted via add_r_rset() or delete_r_rset(), this finder will find the\n\ +added ones or miss the deleted ones respectively.\n\ +\n\ +The finder returned by this method is effective only while the updates\n\ +are performed, i.e., from the construction of the corresponding\n\ +updater until commit() is performed or the updater is destructed\n\ +without commit. The result of a subsequent call to this method (or the\n\ +use of the result) after that is undefined.\n\ +\n\ +A reference to a ZoneFinder for the updated zone\n\ +\n\ +"; +*/ +const char* const ZoneUpdater_addRRset_doc = "\ +add_r_rset(rrset) -> void\n\ +\n\ +Add an RRset to a zone via the updater.\n\ +\n\ +- Whether the RR class is identical to that for the zone to be updated\n\ +- Whether the RRset is not empty, i.e., it has at least one RDATA\n\ +- Whether the RRset is not associated with an RRSIG, i.e., whether\n\ + get_r_rsig() on the RRset returns a NULL pointer.\n\ +\n\ +and otherwise does not check any oddity. For example, it doesn't check\n\ +whether the owner name of the specified RRset is a subdomain of the\n\ +zone's origin; it doesn't care whether or not there is already an\n\ +RRset of the same name and RR type in the zone, and if there is,\n\ +whether any of the existing RRs have duplicate RDATA with the added\n\ +ones. If these conditions matter the calling application must examine\n\ +the existing data beforehand using the ZoneFinder returned by\n\ +get_finder().\n\ +\n\ +The validation requirement on the associated RRSIG is temporary. If we\n\ +find it more reasonable and useful to allow adding a pair of RRset and\n\ +its RRSIG RRset as we gain experiences with the interface, we may\n\ +remove this restriction. Until then we explicitly check it to prevent\n\ +accidental misuse.\n\ +\n\ +Conceptually, on successful call to this method, the zone will have\n\ +the specified RRset, and if there is already an RRset of the same name\n\ +and RR type, these two sets will be \"merged\". \"Merged\" means that\n\ +a subsequent call to ZoneFinder.find() for the name and type will\n\ +result in success and the returned RRset will contain all previously\n\ +existing and newly added RDATAs with the TTL being the minimum of the\n\ +two RRsets. The underlying representation of the \"merged\" RRsets may\n\ +vary depending on the characteristic of the underlying data source.\n\ +For example, if it uses a general purpose database that stores each RR\n\ +of the same RRset separately, it may simply be a larger sets of RRs\n\ +based on both the existing and added RRsets; the TTLs of the RRs may\n\ +be different within the database, and there may even be duplicate RRs\n\ +in different database rows. As long as the RRset returned via\n\ +ZoneFinder.find() conforms to the concept of \"merge\", the actual\n\ +internal representation is up to the implementation.\n\ +\n\ +This method must not be called once commit() is performed. If it calls\n\ +after commit() the implementation must throw a DataSourceError\n\ +exception.\n\ +\n\ +TodoAs noted above we may have to revisit the design details as we\n\ +gain experiences:\n\ +\n\ +- we may want to check (and maybe reject) if there is already a\n\ + duplicate RR (that has the same RDATA).\n\ +- we may want to check (and maybe reject) if there is already an RRset\n\ + of the same name and RR type with different TTL\n\ +- we may even want to check if there is already any RRset of the same\n\ + name and RR type.\n\ +- we may want to add an \"options\" parameter that can control the\n\ + above points\n\ +- we may want to have this method return a value containing the\n\ + information on whether there's a duplicate, etc.\n\ +\n\ +Exceptions:\n\ + DataSourceError Called after commit(), RRset is invalid (see above),\n\ + internal data source error\n\ + std.bad_alloc Resource allocation failure\n\ +\n\ +Parameters:\n\ + rrset The RRset to be added\n\ +\n\ +"; + +const char* const ZoneUpdater_deleteRRset_doc = "\ +delete_r_rset(rrset) -> void\n\ +\n\ +Delete an RRset from a zone via the updater.\n\ +\n\ +Like add_r_rset(), the detailed semantics and behavior of this method\n\ +may have to be revisited in a future version. The following are based\n\ +on the initial implementation decisions.\n\ +\n\ +- Existing RRs that don't match any of the specified RDATAs will\n\ + remain in the zone.\n\ +- Any RRs of the specified RRset that doesn't exist in the zone will\n\ + simply be ignored; the implementation of this method is not supposed\n\ + to check that condition.\n\ +- The TTL of the RRset is ignored; matching is only performed by the\n\ + owner name, RR type and RDATA\n\ +\n\ +Ignoring the TTL may not look sensible, but it's based on the\n\ +observation that it will result in more intuitive result, especially\n\ +when the underlying data source is a general purpose database. See\n\ +also DatabaseAccessor.delete_record_in_zone() on this point. It also\n\ +matches the dynamic update protocol (RFC2136), where TTLs are ignored\n\ +when deleting RRs.\n\ +\n\ +- Whether the RR class is identical to that for the zone to be updated\n\ +- Whether the RRset is not empty, i.e., it has at least one RDATA\n\ +- Whether the RRset is not associated with an RRSIG, i.e., whether\n\ + get_r_rsig() on the RRset returns a NULL pointer.\n\ +\n\ +This method must not be called once commit() is performed. If it calls\n\ +after commit() the implementation must throw a DataSourceError\n\ +exception.\n\ +\n\ +TodoAs noted above we may have to revisit the design details as we\n\ +gain experiences:\n\ +\n\ +- we may want to check (and maybe reject) if some or all of the RRs\n\ + for the specified RRset don't exist in the zone\n\ +- we may want to allow an option to \"delete everything\" for\n\ + specified name and/or specified name + RR type.\n\ +- as mentioned above, we may want to include the TTL in matching the\n\ + deleted RRs\n\ +- we may want to add an \"options\" parameter that can control the\n\ + above points\n\ +- we may want to have this method return a value containing the\n\ + information on whether there's any RRs that are specified but don't\n\ + exit, the number of actually deleted RRs, etc.\n\ +\n\ +Exceptions:\n\ + DataSourceError Called after commit(), RRset is invalid (see above),\n\ + internal data source error\n\ + std.bad_alloc Resource allocation failure\n\ +\n\ +Parameters:\n\ + rrset The RRset to be deleted\n\ +\n\ +"; + +const char* const ZoneUpdater_commit_doc = "\ +commit() -> void\n\ +\n\ +Commit the updates made in the updater to the zone.\n\ +\n\ +This method completes the \"transaction\" started at the creation of\n\ +the updater. After successful completion of this method, the updates\n\ +will be visible outside the scope of the updater. The actual internal\n\ +behavior will defer for different derived classes. For a derived class\n\ +with a general purpose database as a backend, for example, this method\n\ +would perform a \"commit\" statement for the database.\n\ +\n\ +This operation can only be performed at most once. A duplicate call\n\ +must result in a DatasourceError exception.\n\ +\n\ +Exceptions:\n\ + DataSourceError Duplicate call of the method, internal data source\n\ + error\n\ +\n\ +"; +} // unnamed namespace diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index 8bac405f3f..50ea854ff9 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -37,6 +37,8 @@ #include "datasrc.h" #include "updater_python.h" +#include "updater_inc.cc" + using namespace std; using namespace isc::util::python; using namespace isc::datasrc; @@ -87,7 +89,7 @@ ZoneUpdater_destroy(s_ZoneUpdater* const self) { // These are the functions we export // -PyObject* ZoneUpdater_AddRRset(PyObject* po_self, PyObject* args) { +PyObject* ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) { // TODO err handling s_ZoneUpdater* const self = static_cast(po_self); PyObject* rrset_obj; @@ -107,8 +109,7 @@ PyObject* ZoneUpdater_AddRRset(PyObject* po_self, PyObject* args) { } } -PyObject* ZoneUpdater_DeleteRRset(PyObject* po_self, PyObject* args) { - // TODO err handling +PyObject* ZoneUpdater_deleteRRset(PyObject* po_self, PyObject* args) { s_ZoneUpdater* const self = static_cast(po_self); PyObject* rrset_obj; if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { @@ -127,7 +128,7 @@ PyObject* ZoneUpdater_DeleteRRset(PyObject* po_self, PyObject* args) { } } -PyObject* ZoneUpdater_Commit(PyObject* po_self, PyObject*) { +PyObject* ZoneUpdater_commit(PyObject* po_self, PyObject*) { s_ZoneUpdater* const self = static_cast(po_self); try { self->cppobj->commit(); @@ -153,9 +154,12 @@ PyObject* ZoneUpdater_Commit(PyObject* po_self, PyObject*) { // 4. Documentation PyMethodDef ZoneUpdater_methods[] = { /*TODO { "get_finder", ZoneUpdater_GetFinder, METH_NOARGS, "TODO" },*/ - { "add_rrset", ZoneUpdater_AddRRset, METH_VARARGS, "TODO" }, - { "delete_rrset", ZoneUpdater_DeleteRRset, METH_VARARGS, "TODO" }, - { "commit", ZoneUpdater_Commit, METH_NOARGS, "TODO" }, + { "add_rrset", reinterpret_cast(ZoneUpdater_addRRset), METH_VARARGS, + ZoneUpdater_addRRset_doc }, + { "delete_rrset", reinterpret_cast(ZoneUpdater_deleteRRset), METH_VARARGS, + ZoneUpdater_deleteRRset_doc }, + { "commit", reinterpret_cast(ZoneUpdater_commit), METH_NOARGS, + ZoneUpdater_commit_doc }, { NULL, NULL, 0, NULL } }; @@ -185,7 +189,7 @@ PyTypeObject zoneupdater_type = { NULL, // tp_setattro NULL, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - "The ZoneUpdater class objects is...(TODO COMPLETE THIS)", + ZoneUpdater_doc, NULL, // tp_traverse NULL, // tp_clear NULL, // tp_richcompare From 5c3a7ca7b3b28a7a163b0af3cbadc3d8fe7a702b Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 14 Sep 2011 19:10:33 +0200 Subject: [PATCH 740/974] [1179] add zonefinder function to zoneupdater --- src/lib/python/isc/datasrc/finder_python.cc | 3 - src/lib/python/isc/datasrc/updater_python.cc | 75 +++++++++++++++++++- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index 44a4ab5ff2..e6da88a253 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -148,9 +148,6 @@ PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) { return Py_BuildValue("I", 1); } -// These are the functions we export -// For a minimal support, we don't need them. - // This list contains the actual set of functions we have in // python. Each entry has // 1. Python method name diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index 50ea854ff9..1c2198c433 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -33,11 +33,14 @@ #include #include +#include +#include #include "datasrc.h" #include "updater_python.h" #include "updater_inc.cc" +#include "finder_inc.cc" using namespace std; using namespace isc::util::python; @@ -142,9 +145,66 @@ PyObject* ZoneUpdater_commit(PyObject* po_self, PyObject*) { } } - // These are the functions we export -// For a minimal support, we don't need them. +// +PyObject* ZoneUpdater_getClass(PyObject* po_self, PyObject*) { + s_ZoneUpdater* self = static_cast(po_self); + try { + return (isc::dns::python::createRRClassObject(self->cppobj->getFinder().getClass())); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } +} + +PyObject* ZoneUpdater_getOrigin(PyObject* po_self, PyObject*) { + s_ZoneUpdater* self = static_cast(po_self); + try { + return (isc::dns::python::createNameObject(self->cppobj->getFinder().getOrigin())); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } +} + +PyObject* ZoneUpdater_find(PyObject* po_self, PyObject* args) { + s_ZoneUpdater* const self = static_cast(po_self); + PyObject *name; + PyObject *rrtype; + PyObject *target; + int options_int; + if (PyArg_ParseTuple(args, "O!O!OI", &isc::dns::python::name_type, &name, + &isc::dns::python::rrtype_type, &rrtype, + &target, &options_int)) { + try { + ZoneFinder::FindOptions options = static_cast(options_int); + ZoneFinder::FindResult find_result( + self->cppobj->getFinder().find(isc::dns::python::PyName_ToName(name), + isc::dns::python::PyRRType_ToRRType(rrtype), + NULL, + options + )); + ZoneFinder::Result r = find_result.code; + isc::dns::ConstRRsetPtr rrsp = find_result.rrset; + if (rrsp) { + return Py_BuildValue("IO", r, isc::dns::python::createRRsetObject(*rrsp)); + } else { + Py_INCREF(Py_None); + return Py_BuildValue("IO", r, Py_None); + } + } catch (const DataSourceError& dse) { + PyErr_SetString(getDataSourceException("Error"), dse.what()); + return (NULL); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } + } else { + return (NULL); + } + return Py_BuildValue("I", 1); +} + // This list contains the actual set of functions we have in // python. Each entry has @@ -153,13 +213,22 @@ PyObject* ZoneUpdater_commit(PyObject* po_self, PyObject*) { // 3. Argument type // 4. Documentation PyMethodDef ZoneUpdater_methods[] = { -/*TODO { "get_finder", ZoneUpdater_GetFinder, METH_NOARGS, "TODO" },*/ { "add_rrset", reinterpret_cast(ZoneUpdater_addRRset), METH_VARARGS, ZoneUpdater_addRRset_doc }, { "delete_rrset", reinterpret_cast(ZoneUpdater_deleteRRset), METH_VARARGS, ZoneUpdater_deleteRRset_doc }, { "commit", reinterpret_cast(ZoneUpdater_commit), METH_NOARGS, ZoneUpdater_commit_doc }, + // Instead of a getFinder, we implement the finder functionality directly + // This is because ZoneFinder is non-copyable, and we should not create + // a ZoneFinder object from a reference only (which is what is returned + // by getFinder(). Apart from that + { "get_origin", reinterpret_cast(ZoneUpdater_getOrigin), METH_NOARGS, + ZoneFinder_getOrigin_doc }, + { "get_class", reinterpret_cast(ZoneUpdater_getClass), METH_NOARGS, + ZoneFinder_getClass_doc }, + { "find", reinterpret_cast(ZoneUpdater_find), METH_VARARGS, + ZoneFinder_find_doc }, { NULL, NULL, 0, NULL } }; From 7efa61c40b94d3234dd7fc79a0fc7ae0f1b0a105 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 14 Sep 2011 20:45:06 +0200 Subject: [PATCH 741/974] [1179] some added tests, and force ptr cleanup --- src/lib/python/isc/datasrc/finder_python.cc | 4 +- src/lib/python/isc/datasrc/iterator_python.cc | 4 +- .../python/isc/datasrc/tests/datasrc_test.py | 74 +++++++++++++++++-- src/lib/python/isc/datasrc/updater_python.cc | 5 +- 4 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index e6da88a253..616703d600 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -84,7 +84,9 @@ ZoneFinder_init(s_ZoneFinder* self, PyObject* args) { void ZoneFinder_destroy(s_ZoneFinder* const self) { - // cppobj is shared ptr, so no need for explicit delete + // cppobj is a shared ptr, but to make sure things are not destroyed in + // the wrong order, we reset it here. + self->cppobj.reset(); Py_TYPE(self)->tp_free(self); } diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index b078876ebc..403bcaf1de 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -78,7 +78,9 @@ ZoneIterator_init(s_ZoneIterator* self, PyObject* args) { // In many cases you can use it without modification, but check that carefully. void ZoneIterator_destroy(s_ZoneIterator* const self) { - // cppobj is a shared ptr so no need to delete that + // cppobj is a shared ptr, but to make sure things are not destroyed in + // the wrong order, we reset it here. + self->cppobj.reset(); Py_TYPE(self)->tp_free(self); } diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py index e0492b781b..75ee8d2613 100644 --- a/src/lib/python/isc/datasrc/tests/datasrc_test.py +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -152,7 +152,7 @@ class DataSrcUpdater(unittest.TestCase): shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE) - def test_update(self): + def test_update_delete_commit(self): dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE) # first make sure, through a separate finder, that some record exists @@ -170,13 +170,6 @@ class DataSrcUpdater(unittest.TestCase): rrset_to_delete = rrset; - result, rrset = finder.find(isc.dns.Name("doesnotexist.example.com"), - isc.dns.RRType.TXT(), - None, - finder.FIND_DEFAULT) - self.assertEqual(finder.NXDOMAIN, result) - self.assertEqual(None, rrset) - # can't delete rrset with associated sig. Abuse that to force an # exception first, then remove the sig, then delete the record updater = dsc.get_updater(isc.dns.Name("example.com"), True) @@ -185,8 +178,26 @@ class DataSrcUpdater(unittest.TestCase): rrset_to_delete.remove_rrsig() updater.delete_rrset(rrset_to_delete) + + # The record should be gone in the updater, but not in the original + # finder (since we have not committed) + result, rrset = updater.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.NXDOMAIN, result) + self.assertEqual(None, rrset) + + result, rrset = finder.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + updater.commit() + # the record should be gone now in the 'real' finder as well result, rrset = finder.find(isc.dns.Name("www.example.com"), isc.dns.RRType.A(), None, @@ -209,6 +220,53 @@ class DataSrcUpdater(unittest.TestCase): self.assertEqual(finder.SUCCESS, result) self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + def test_update_delete_abort(self): + dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE) + + # first make sure, through a separate finder, that some record exists + result, finder = dsc.find_zone(isc.dns.Name("example.com")) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual(isc.dns.RRClass.IN(), finder.get_class()) + self.assertEqual("example.com.", finder.get_origin().to_text()) + + result, rrset = finder.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + + rrset_to_delete = rrset; + + # can't delete rrset with associated sig. Abuse that to force an + # exception first, then remove the sig, then delete the record + updater = dsc.get_updater(isc.dns.Name("example.com"), True) + self.assertRaises(isc.datasrc.Error, updater.delete_rrset, rrset_to_delete) + + rrset_to_delete.remove_rrsig() + + updater.delete_rrset(rrset_to_delete) + + # The record should be gone in the updater, but not in the original + # finder (since we have not committed) + result, rrset = updater.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.NXDOMAIN, result) + self.assertEqual(None, rrset) + + # destroy the updater, which should make it roll back + updater = None + + # the record should still be available in the 'real' finder as well + result, rrset = finder.find(isc.dns.Name("www.example.com"), + isc.dns.RRType.A(), + None, + finder.FIND_DEFAULT) + self.assertEqual(finder.SUCCESS, result) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + if __name__ == "__main__": isc.log.init("bind10") diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index 1c2198c433..7d5229fca9 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -86,14 +86,15 @@ ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args) { // In many cases you can use it without modification, but check that carefully. void ZoneUpdater_destroy(s_ZoneUpdater* const self) { - // cppobj is a shared ptr so should take care of itself + // cppobj is a shared ptr, but to make sure things are not destroyed in + // the wrong order, we reset it here. + self->cppobj.reset(); Py_TYPE(self)->tp_free(self); } // These are the functions we export // PyObject* ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) { - // TODO err handling s_ZoneUpdater* const self = static_cast(po_self); PyObject* rrset_obj; if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { From 6d6353cea42ed088df3c2c90c4c2741a1b8b2871 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 15 Sep 2011 00:00:24 +0200 Subject: [PATCH 742/974] [1179] use N instead of O where we have constructors in BuildValue --- src/lib/python/isc/datasrc/client_python.cc | 3 ++- src/lib/python/isc/datasrc/finder_python.cc | 6 +++--- src/lib/python/isc/datasrc/tests/datasrc_test.py | 2 +- src/lib/python/isc/datasrc/updater_python.cc | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index 1abe82f698..a338d02758 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -90,7 +90,8 @@ DataSourceClient_findZone(PyObject* po_self, PyObject* args) { result::Result r = find_result.code; ZoneFinderPtr zfp = find_result.zone_finder; - return Py_BuildValue("IO", r, createZoneFinderObject(zfp)); + // Use N instead of O so refcount isn't increased twice + return (Py_BuildValue("IN", r, createZoneFinderObject(zfp))); } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index 616703d600..d098eb209a 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -132,10 +132,10 @@ PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) { ZoneFinder::Result r = find_result.code; isc::dns::ConstRRsetPtr rrsp = find_result.rrset; if (rrsp) { - return Py_BuildValue("IO", r, isc::dns::python::createRRsetObject(*rrsp)); + // Use N instead of O so the refcount isn't increased twice + return (Py_BuildValue("IN", r, isc::dns::python::createRRsetObject(*rrsp))); } else { - Py_INCREF(Py_None); - return Py_BuildValue("IO", r, Py_None); + return (Py_BuildValue("IO", r, Py_None)); } } catch (const DataSourceError& dse) { PyErr_SetString(getDataSourceException("Error"), dse.what()); diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py index 75ee8d2613..472a0463b8 100644 --- a/src/lib/python/isc/datasrc/tests/datasrc_test.py +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -145,13 +145,13 @@ class DataSrcClient(unittest.TestCase): None, "foo") + class DataSrcUpdater(unittest.TestCase): def setUp(self): # Make a fresh copy of the writable database with all original content shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE) - def test_update_delete_commit(self): dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE) diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index 7d5229fca9..2ee875d50d 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -188,9 +188,9 @@ PyObject* ZoneUpdater_find(PyObject* po_self, PyObject* args) { ZoneFinder::Result r = find_result.code; isc::dns::ConstRRsetPtr rrsp = find_result.rrset; if (rrsp) { - return Py_BuildValue("IO", r, isc::dns::python::createRRsetObject(*rrsp)); + // Use N instead of O so the refcount isn't increased twice + return Py_BuildValue("IN", r, isc::dns::python::createRRsetObject(*rrsp)); } else { - Py_INCREF(Py_None); return Py_BuildValue("IO", r, Py_None); } } catch (const DataSourceError& dse) { From 1f81b4916fa3bd0cbf4f41cc7ad8f13450aa6481 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 15 Sep 2011 01:03:05 +0200 Subject: [PATCH 743/974] [1179] add some missing returns --- src/lib/python/isc/datasrc/client_python.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index a338d02758..1c895dbad5 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -110,8 +110,10 @@ DataSourceClient_getIterator(PyObject* po_self, PyObject* args) { return (createZoneIteratorObject(self->cppobj->getIterator(isc::dns::python::PyName_ToName(name_obj)))); } catch (const isc::NotImplemented& ne) { PyErr_SetString(getDataSourceException("NotImplemented"), ne.what()); + return (NULL); } catch (const DataSourceError& dse) { PyErr_SetString(getDataSourceException("Error"), dse.what()); + return (NULL); } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); @@ -132,8 +134,10 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) { return (createZoneUpdaterObject(self->cppobj->getUpdater(isc::dns::python::PyName_ToName(name_obj), replace))); } catch (const isc::NotImplemented& ne) { PyErr_SetString(getDataSourceException("NotImplemented"), ne.what()); + return (NULL); } catch (const DataSourceError& dse) { PyErr_SetString(getDataSourceException("Error"), dse.what()); + return (NULL); } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); From 259955ba65c102bd36ec818ca4193aab311e983d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 19 Sep 2011 00:30:49 +0200 Subject: [PATCH 744/974] [1179] some moving around of code for consistency --- src/lib/python/isc/datasrc/client_python.cc | 25 +++++++------------ src/lib/python/isc/datasrc/client_python.h | 9 ------- src/lib/python/isc/datasrc/datasrc.cc | 10 ++++++++ src/lib/python/isc/datasrc/finder_python.cc | 25 +++++++------------ src/lib/python/isc/datasrc/finder_python.h | 10 -------- src/lib/python/isc/datasrc/iterator_python.cc | 25 +++++++------------ src/lib/python/isc/datasrc/iterator_python.h | 9 ------- src/lib/python/isc/datasrc/updater_python.cc | 25 +++++++------------ src/lib/python/isc/datasrc/updater_python.h | 8 ------ 9 files changed, 46 insertions(+), 100 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index 1c895dbad5..291a10793b 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -47,23 +47,14 @@ using namespace isc::util::python; using namespace isc::datasrc; using namespace isc::datasrc::python; -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// DataSourceClient -// - -// Trivial constructor. -s_DataSourceClient::s_DataSourceClient() : cppobj(NULL) { -} - namespace { +// The s_* Class simply covers one instantiation of the object +class s_DataSourceClient : public PyObject { +public: + s_DataSourceClient() : cppobj(NULL) {}; + DataSourceClient* cppobj; +}; + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer DataSourceClientContainer; @@ -269,6 +260,7 @@ PyTypeObject datasourceclient_type = { 0 // tp_version_tag }; +namespace internal { // Module Initialization, all statics are initialized here bool initModulePart_DataSourceClient(PyObject* mod) { @@ -295,6 +287,7 @@ initModulePart_DataSourceClient(PyObject* mod) { return (true); } +} // namespace internal } // namespace python } // namespace datasrc diff --git a/src/lib/python/isc/datasrc/client_python.h b/src/lib/python/isc/datasrc/client_python.h index ea2c014370..b20fb6b4c7 100644 --- a/src/lib/python/isc/datasrc/client_python.h +++ b/src/lib/python/isc/datasrc/client_python.h @@ -23,17 +23,8 @@ class DataSourceClient; namespace python { -// The s_* Class simply covers one instantiation of the object -class s_DataSourceClient : public PyObject { -public: - s_DataSourceClient(); - DataSourceClient* cppobj; -}; - extern PyTypeObject datasourceclient_type; -bool initModulePart_DataSourceClient(PyObject* mod); - } // namespace python } // namespace datasrc } // namespace isc diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc index 88cad283da..3c1da3cffe 100644 --- a/src/lib/python/isc/datasrc/datasrc.cc +++ b/src/lib/python/isc/datasrc/datasrc.cc @@ -53,6 +53,14 @@ getDataSourceException(const char* ex_name) { } return (ex_obj); } + +namespace internal { +bool initModulePart_DataSourceClient(PyObject* mod); +bool initModulePart_ZoneFinder(PyObject* mod); +bool initModulePart_ZoneIterator(PyObject* mod); +bool initModulePart_ZoneUpdater(PyObject* mod); +} // end namespace internal + } // end namespace python } // end namespace datasrc } // end namespace isc @@ -78,6 +86,8 @@ PyModuleDef iscDataSrc = { } // end anonymous namespace +using namespace isc::datasrc::python::internal; + PyMODINIT_FUNC PyInit_datasrc(void) { PyObject* mod = PyModule_Create(&iscDataSrc); diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index d098eb209a..2f7cb50b44 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -47,23 +47,14 @@ using namespace isc::util::python; using namespace isc::datasrc; using namespace isc::datasrc::python; -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// Zone Finder -// - -// Trivial constructor. -s_ZoneFinder::s_ZoneFinder() : cppobj(ZoneFinderPtr()) { -} - namespace { +// The s_* Class simply covers one instantiation of the object +class s_ZoneFinder : public PyObject { +public: + s_ZoneFinder() : cppobj(ZoneFinderPtr()) {}; + ZoneFinderPtr cppobj; +}; + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer ZoneFinderContainer; @@ -221,6 +212,7 @@ PyTypeObject zonefinder_type = { 0 // tp_version_tag }; +namespace internal { // Module Initialization, all statics are initialized here bool initModulePart_ZoneFinder(PyObject* mod) { @@ -259,6 +251,7 @@ initModulePart_ZoneFinder(PyObject* mod) { return (true); } +} // namespace internal PyObject* createZoneFinderObject(isc::datasrc::ZoneFinderPtr source) { diff --git a/src/lib/python/isc/datasrc/finder_python.h b/src/lib/python/isc/datasrc/finder_python.h index 7dfc34e06d..5f2404e342 100644 --- a/src/lib/python/isc/datasrc/finder_python.h +++ b/src/lib/python/isc/datasrc/finder_python.h @@ -22,20 +22,10 @@ namespace datasrc { namespace python { -// The s_* Class simply covers one instantiation of the object -class s_ZoneFinder : public PyObject { -public: - s_ZoneFinder(); - ZoneFinderPtr cppobj; -}; - extern PyTypeObject zonefinder_type; -bool initModulePart_ZoneFinder(PyObject* mod); - PyObject* createZoneFinderObject(isc::datasrc::ZoneFinderPtr source); - } // namespace python } // namespace datasrc } // namespace isc diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index 403bcaf1de..342a5868bf 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -43,23 +43,14 @@ using namespace isc::util::python; using namespace isc::datasrc; using namespace isc::datasrc::python; -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// Zone Iterator -// - -// Trivial constructor. -s_ZoneIterator::s_ZoneIterator() : cppobj(ZoneIteratorPtr()) { -} - namespace { +// The s_* Class simply covers one instantiation of the object +class s_ZoneIterator : public PyObject { +public: + s_ZoneIterator() : cppobj(ZoneIteratorPtr()) {}; + ZoneIteratorPtr cppobj; +}; + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer ZoneIteratorContainer; @@ -182,6 +173,7 @@ PyTypeObject zoneiterator_type = { 0 // tp_version_tag }; +namespace internal { // Module Initialization, all statics are initialized here bool initModulePart_ZoneIterator(PyObject* mod) { @@ -199,6 +191,7 @@ initModulePart_ZoneIterator(PyObject* mod) { return (true); } +} // namespace internal PyObject* createZoneIteratorObject(isc::datasrc::ZoneIteratorPtr source) { diff --git a/src/lib/python/isc/datasrc/iterator_python.h b/src/lib/python/isc/datasrc/iterator_python.h index 7ad4d36531..b457740159 100644 --- a/src/lib/python/isc/datasrc/iterator_python.h +++ b/src/lib/python/isc/datasrc/iterator_python.h @@ -23,17 +23,8 @@ class DataSourceClient; namespace python { -// The s_* Class simply covers one instantiation of the object -class s_ZoneIterator : public PyObject { -public: - s_ZoneIterator(); - ZoneIteratorPtr cppobj; -}; - extern PyTypeObject zoneiterator_type; -bool initModulePart_ZoneIterator(PyObject* mod); - PyObject* createZoneIteratorObject(isc::datasrc::ZoneIteratorPtr source); diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index 2ee875d50d..8acf5b9e21 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -47,23 +47,14 @@ using namespace isc::util::python; using namespace isc::datasrc; using namespace isc::datasrc::python; -// -// Definition of the classes -// - -// For each class, we need a struct, a helper functions (init, destroy, -// and static wrappers around the methods we export), a list of methods, -// and a type description - -// -// Zone Updater -// - -// Trivial constructor. -s_ZoneUpdater::s_ZoneUpdater() : cppobj(ZoneUpdaterPtr()) { -} - namespace { +// The s_* Class simply covers one instantiation of the object +class s_ZoneUpdater : public PyObject { +public: + s_ZoneUpdater() : cppobj(ZoneUpdaterPtr()) {}; + ZoneUpdaterPtr cppobj; +}; + // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer ZoneUpdaterContainer; @@ -288,6 +279,7 @@ PyTypeObject zoneupdater_type = { 0 // tp_version_tag }; +namespace internal { // Module Initialization, all statics are initialized here bool initModulePart_ZoneUpdater(PyObject* mod) { @@ -305,6 +297,7 @@ initModulePart_ZoneUpdater(PyObject* mod) { return (true); } +} // namespace internal PyObject* createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source) { diff --git a/src/lib/python/isc/datasrc/updater_python.h b/src/lib/python/isc/datasrc/updater_python.h index e3aec21b48..3886aa3065 100644 --- a/src/lib/python/isc/datasrc/updater_python.h +++ b/src/lib/python/isc/datasrc/updater_python.h @@ -23,17 +23,9 @@ class DataSourceClient; namespace python { -// The s_* Class simply covers one instantiation of the object -class s_ZoneUpdater : public PyObject { -public: - s_ZoneUpdater(); - ZoneUpdaterPtr cppobj; -}; extern PyTypeObject zoneupdater_type; -bool initModulePart_ZoneUpdater(PyObject* mod); - PyObject* createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source); From 4e544fba3459913e23f86dc5e628665bd288c483 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 19 Sep 2011 01:39:46 +0200 Subject: [PATCH 745/974] [1179] extra_dist for inc files --- src/lib/python/isc/datasrc/Makefile.am | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am index 9a0aaa8f84..e5f20c18f4 100644 --- a/src/lib/python/isc/datasrc/Makefile.am +++ b/src/lib/python/isc/datasrc/Makefile.am @@ -27,6 +27,11 @@ datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/python/libpydnspp.la datasrc_la_LIBADD += $(PYTHON_LIB) datasrc_la_LIBADD += $(SQLITE_LIBS) +EXTRA_DIST = client_inc.cc +EXTRA_DIST += finder_inc.cc +EXTRA_DIST += iterator_inc.cc +EXTRA_DIST += updater_inc.cc + CLEANDIRS = __pycache__ clean-local: From c3424869801ea8811106f8f97928ed5cd71efbff Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 20 Sep 2011 11:23:04 -0700 Subject: [PATCH 746/974] [1101] use pre-generated forwarding .py files under log_messages srcdir instead of dynamically creating them so that all of in-src separate-builddir and distcheck will succeed. --- configure.ac | 1 - src/bin/bind10/Makefile.am | 5 ----- src/bin/cmdctl/Makefile.am | 8 ------- src/bin/stats/Makefile.am | 16 -------------- src/bin/xfrin/Makefile.am | 8 ------- src/bin/xfrout/Makefile.am | 8 ------- src/bin/zonemgr/Makefile.am | 8 ------- src/lib/python/isc/config/Makefile.am | 16 -------------- src/lib/python/isc/log_messages/Makefile.am | 21 +++++++++++++++++++ .../{__init__.py.in => __init__.py} | 0 .../isc/log_messages/bind10_messages.py | 1 + .../isc/log_messages/cfgmgr_messages.py | 1 + .../isc/log_messages/cmdctl_messages.py | 1 + .../isc/log_messages/config_messages.py | 1 + .../isc/log_messages/notify_out_messages.py | 1 + .../isc/log_messages/stats_httpd_messages.py | 1 + .../python/isc/log_messages/stats_messages.py | 1 + .../python/isc/log_messages/xfrin_messages.py | 1 + .../isc/log_messages/xfrout_messages.py | 1 + .../isc/log_messages/zonemgr_messages.py | 1 + src/lib/python/isc/notify/Makefile.am | 10 +-------- 21 files changed, 32 insertions(+), 79 deletions(-) rename src/lib/python/isc/log_messages/{__init__.py.in => __init__.py} (100%) create mode 100644 src/lib/python/isc/log_messages/bind10_messages.py create mode 100644 src/lib/python/isc/log_messages/cfgmgr_messages.py create mode 100644 src/lib/python/isc/log_messages/cmdctl_messages.py create mode 100644 src/lib/python/isc/log_messages/config_messages.py create mode 100644 src/lib/python/isc/log_messages/notify_out_messages.py create mode 100644 src/lib/python/isc/log_messages/stats_httpd_messages.py create mode 100644 src/lib/python/isc/log_messages/stats_messages.py create mode 100644 src/lib/python/isc/log_messages/xfrin_messages.py create mode 100644 src/lib/python/isc/log_messages/xfrout_messages.py create mode 100644 src/lib/python/isc/log_messages/zonemgr_messages.py diff --git a/configure.ac b/configure.ac index c560c19367..300c48ddfa 100644 --- a/configure.ac +++ b/configure.ac @@ -953,7 +953,6 @@ AC_OUTPUT([doc/version.ent src/lib/python/isc/cc/tests/cc_test src/lib/python/isc/notify/tests/notify_out_test src/lib/python/isc/log/tests/log_console.py - src/lib/python/isc/log_messages/__init__.py src/lib/python/isc/log_messages/work/__init__.py src/lib/dns/gen-rdatacode.py src/lib/python/bind10_config.py diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am index 7dc7807f59..053eb8bdc2 100644 --- a/src/bin/bind10/Makefile.am +++ b/src/bin/bind10/Makefile.am @@ -19,8 +19,6 @@ EXTRA_DIST = bob.spec man_MANS = bind10.8 EXTRA_DIST += $(man_MANS) bind10.xml bind10_messages.mes -BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/bind10_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/bind10_messages.py CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/bind10_messages.pyc EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/bind10_messages.py @@ -35,9 +33,6 @@ $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py : bind10_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/bind10_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/bind10_messages.py: Makefile - echo "from work.bind10_messages import *" > $@ - # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix bind10: bind10_src.py $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am index fff44b08e1..e302fa6b87 100644 --- a/src/bin/cmdctl/Makefile.am +++ b/src/bin/cmdctl/Makefile.am @@ -25,11 +25,6 @@ CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.pyc -BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/cmdctl_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/cmdctl_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/cmdctl_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/cmdctl_messages.py - man_MANS = b10-cmdctl.8 EXTRA_DIST += $(man_MANS) b10-cmdctl.xml cmdctl_messages.mes @@ -47,9 +42,6 @@ $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py : cmdctl_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/cmdctl_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/cmdctl_messages.py: Makefile - echo "from work.cmdctl_messages import *" > $@ - # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix b10-cmdctl: cmdctl.py $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py $(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@ diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index 0ed4c1d78f..3289765a30 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -25,16 +25,6 @@ EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl EXTRA_DIST += stats_messages.mes stats_httpd_messages.mes -BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/stats_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/stats_messages.py - -BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/stats_httpd_messages.py - if ENABLE_MAN b10-stats.8: b10-stats.xml @@ -49,16 +39,10 @@ $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py : stats_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/stats_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/stats_messages.py: Makefile - echo "from work.stats_messages import *" > $@ - $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py : stats_httpd_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/stats_httpd_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/stats_httpd_messages.py: Makefile - echo "from work.stats_httpd_messages import *" > $@ - # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix b10-stats: stats.py $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats.py >$@ diff --git a/src/bin/xfrin/Makefile.am b/src/bin/xfrin/Makefile.am index f88b4d27f8..8d80b220fb 100644 --- a/src/bin/xfrin/Makefile.am +++ b/src/bin/xfrin/Makefile.am @@ -18,11 +18,6 @@ man_MANS = b10-xfrin.8 EXTRA_DIST = $(man_MANS) b10-xfrin.xml EXTRA_DIST += xfrin.spec xfrin_messages.mes -BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/xfrin_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/xfrin_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/xfrin_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/xfrin_messages.py - if ENABLE_MAN b10-xfrin.8: b10-xfrin.xml @@ -35,9 +30,6 @@ $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py : xfrin_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/xfrin_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/xfrin_messages.py: Makefile - echo "from work.xfrin_messages import *" > $@ - # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix b10-xfrin: xfrin.py $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ diff --git a/src/bin/xfrout/Makefile.am b/src/bin/xfrout/Makefile.am index aae2e6aa4a..6100e64bf7 100644 --- a/src/bin/xfrout/Makefile.am +++ b/src/bin/xfrout/Makefile.am @@ -17,11 +17,6 @@ CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.pyc man_MANS = b10-xfrout.8 EXTRA_DIST = $(man_MANS) b10-xfrout.xml xfrout_messages.mes -BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/xfrout_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/xfrout_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/xfrout_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/xfrout_messages.py - if ENABLE_MAN b10-xfrout.8: b10-xfrout.xml @@ -34,9 +29,6 @@ $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py : xfrout_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/xfrout_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/xfrout_messages.py: Makefile - echo "from work.xfrout_messages import *" > $@ - xfrout.spec: xfrout.spec.pre $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.spec.pre >$@ diff --git a/src/bin/zonemgr/Makefile.am b/src/bin/zonemgr/Makefile.am index 637b31aef5..aa427fdf2e 100644 --- a/src/bin/zonemgr/Makefile.am +++ b/src/bin/zonemgr/Makefile.am @@ -17,11 +17,6 @@ CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.pyc man_MANS = b10-zonemgr.8 EXTRA_DIST = $(man_MANS) b10-zonemgr.xml zonemgr_messages.mes -BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/zonemgr_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/zonemgr_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/zonemgr_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/zonemgr_messages.py - if ENABLE_MAN b10-zonemgr.8: b10-zonemgr.xml @@ -34,9 +29,6 @@ $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py : zonemgr_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/zonemgr_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/zonemgr_messages.py: Makefile - echo "from work.zonemgr_messages import *" > $@ - zonemgr.spec: zonemgr.spec.pre $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.spec.pre >$@ diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am index 62a372222e..ef696fb4c1 100644 --- a/src/lib/python/isc/config/Makefile.am +++ b/src/lib/python/isc/config/Makefile.am @@ -18,30 +18,14 @@ CLEANDIRS = __pycache__ EXTRA_DIST = cfgmgr_messages.mes config_messages.mes -BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/cfgmgr_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/cfgmgr_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/cfgmgr_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/cfgmgr_messages.py - -BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/config_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/config_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/config_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/config_messages.py - # Define rule to build logging source files from message file $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py : cfgmgr_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/cfgmgr_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/cfgmgr_messages.py: Makefile - echo "from work.cfgmgr_messages import *" > $@ - $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py : config_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/config_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/config_messages.py: Makefile - echo "from work.config_messages import *" > $@ - clean-local: rm -rf $(CLEANDIRS) diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am index dc060a12d8..b9bc4c8a8e 100644 --- a/src/lib/python/isc/log_messages/Makefile.am +++ b/src/lib/python/isc/log_messages/Makefile.am @@ -1,7 +1,28 @@ SUBDIRS = work EXTRA_DIST = __init__.py +EXTRA_DIST += bind10_messages.py +EXTRA_DIST += cmdctl_messages.py +EXTRA_DIST += stats_messages.py +EXTRA_DIST += stats_httpd_messages.py +EXTRA_DIST += xfrin_messages.py +EXTRA_DIST += xfrout_messages.py +EXTRA_DIST += zonemgr_messages.py +EXTRA_DIST += cfgmgr_messages.py +EXTRA_DIST += config_messages.py +EXTRA_DIST += notify_out_messages.py + CLEANFILES = __init__.pyc +CLEANFILES += bind10_messages.pyc +CLEANFILES += cmdctl_messages.pyc +CLEANFILES += stats_messages.pyc +CLEANFILES += stats_httpd_messages.pyc +CLEANFILES += xfrin_messages.pyc +CLEANFILES += xfrout_messages.pyc +CLEANFILES += zonemgr_messages.pyc +CLEANFILES += cfgmgr_messages.pyc +CLEANFILES += config_messages.pyc +CLEANFILES += notify_out_messages.pyc CLEANDIRS = __pycache__ diff --git a/src/lib/python/isc/log_messages/__init__.py.in b/src/lib/python/isc/log_messages/__init__.py similarity index 100% rename from src/lib/python/isc/log_messages/__init__.py.in rename to src/lib/python/isc/log_messages/__init__.py diff --git a/src/lib/python/isc/log_messages/bind10_messages.py b/src/lib/python/isc/log_messages/bind10_messages.py new file mode 100644 index 0000000000..68ce94ca6a --- /dev/null +++ b/src/lib/python/isc/log_messages/bind10_messages.py @@ -0,0 +1 @@ +from work.bind10_messages import * diff --git a/src/lib/python/isc/log_messages/cfgmgr_messages.py b/src/lib/python/isc/log_messages/cfgmgr_messages.py new file mode 100644 index 0000000000..55571003ba --- /dev/null +++ b/src/lib/python/isc/log_messages/cfgmgr_messages.py @@ -0,0 +1 @@ +from work.cfgmgr_messages import * diff --git a/src/lib/python/isc/log_messages/cmdctl_messages.py b/src/lib/python/isc/log_messages/cmdctl_messages.py new file mode 100644 index 0000000000..7283d5a15e --- /dev/null +++ b/src/lib/python/isc/log_messages/cmdctl_messages.py @@ -0,0 +1 @@ +from work.cmdctl_messages import * diff --git a/src/lib/python/isc/log_messages/config_messages.py b/src/lib/python/isc/log_messages/config_messages.py new file mode 100644 index 0000000000..c5579752f2 --- /dev/null +++ b/src/lib/python/isc/log_messages/config_messages.py @@ -0,0 +1 @@ +from work.config_messages import * diff --git a/src/lib/python/isc/log_messages/notify_out_messages.py b/src/lib/python/isc/log_messages/notify_out_messages.py new file mode 100644 index 0000000000..6aa37ea5ab --- /dev/null +++ b/src/lib/python/isc/log_messages/notify_out_messages.py @@ -0,0 +1 @@ +from work.notify_out_messages import * diff --git a/src/lib/python/isc/log_messages/stats_httpd_messages.py b/src/lib/python/isc/log_messages/stats_httpd_messages.py new file mode 100644 index 0000000000..7782c34a8d --- /dev/null +++ b/src/lib/python/isc/log_messages/stats_httpd_messages.py @@ -0,0 +1 @@ +from work.stats_httpd_messages import * diff --git a/src/lib/python/isc/log_messages/stats_messages.py b/src/lib/python/isc/log_messages/stats_messages.py new file mode 100644 index 0000000000..1324cfcf0f --- /dev/null +++ b/src/lib/python/isc/log_messages/stats_messages.py @@ -0,0 +1 @@ +from work.stats_messages import * diff --git a/src/lib/python/isc/log_messages/xfrin_messages.py b/src/lib/python/isc/log_messages/xfrin_messages.py new file mode 100644 index 0000000000..b412519a3d --- /dev/null +++ b/src/lib/python/isc/log_messages/xfrin_messages.py @@ -0,0 +1 @@ +from work.xfrin_messages import * diff --git a/src/lib/python/isc/log_messages/xfrout_messages.py b/src/lib/python/isc/log_messages/xfrout_messages.py new file mode 100644 index 0000000000..2093d5caca --- /dev/null +++ b/src/lib/python/isc/log_messages/xfrout_messages.py @@ -0,0 +1 @@ +from work.xfrout_messages import * diff --git a/src/lib/python/isc/log_messages/zonemgr_messages.py b/src/lib/python/isc/log_messages/zonemgr_messages.py new file mode 100644 index 0000000000..b3afe9ccdf --- /dev/null +++ b/src/lib/python/isc/log_messages/zonemgr_messages.py @@ -0,0 +1 @@ +from work.zonemgr_messages import * diff --git a/src/lib/python/isc/notify/Makefile.am b/src/lib/python/isc/notify/Makefile.am index 496c64d217..c247ab8cd7 100644 --- a/src/lib/python/isc/notify/Makefile.am +++ b/src/lib/python/isc/notify/Makefile.am @@ -9,12 +9,7 @@ pylogmessagedir = $(pyexecdir)/isc/log_messages/ EXTRA_DIST = notify_out_messages.mes -BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/notify_out_messages.py -CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/notify_out_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/notify_out_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/notify_out_messages.py - -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py +CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.pyc CLEANDIRS = __pycache__ @@ -23,8 +18,5 @@ $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py : notify_out_messages.mes $(top_builddir)/src/lib/log/compiler/message \ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/notify_out_messages.mes -$(PYTHON_LOGMSGPKG_DIR)/notify_out_messages.py: Makefile - echo "from work.notify_out_messages import *" > $@ - clean-local: rm -rf $(CLEANDIRS) From 715fee7daf2f966261d997e1b39888f14fb28a45 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 20 Sep 2011 13:21:05 -0700 Subject: [PATCH 747/974] [1101] forgot to save one cleanup change. --- src/bin/bind10/Makefile.am | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am index 053eb8bdc2..5ec0c9f4a6 100644 --- a/src/bin/bind10/Makefile.am +++ b/src/bin/bind10/Makefile.am @@ -19,9 +19,6 @@ EXTRA_DIST = bob.spec man_MANS = bind10.8 EXTRA_DIST += $(man_MANS) bind10.xml bind10_messages.mes -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/bind10_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/bind10_messages.py - if ENABLE_MAN bind10.8: bind10.xml From b25df34f6a7582baff54dab59c4e033f6db4e42c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 20 Sep 2011 22:43:35 +0200 Subject: [PATCH 748/974] [1179] some more consistency moves --- src/lib/python/isc/datasrc/client_python.cc | 38 ++---- src/lib/python/isc/datasrc/datasrc.cc | 111 ++++++++++++++++-- src/lib/python/isc/datasrc/finder_python.cc | 47 +------- src/lib/python/isc/datasrc/iterator_python.cc | 23 +--- src/lib/python/isc/datasrc/updater_python.cc | 29 ++--- 5 files changed, 129 insertions(+), 119 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index 291a10793b..501a0f0a38 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -86,6 +86,9 @@ DataSourceClient_findZone(PyObject* po_self, PyObject* args) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } else { return (NULL); @@ -108,6 +111,9 @@ DataSourceClient_getIterator(PyObject* po_self, PyObject* args) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } else { return (NULL); @@ -132,6 +138,9 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } else { return (NULL); @@ -260,35 +269,6 @@ PyTypeObject datasourceclient_type = { 0 // tp_version_tag }; -namespace internal { -// Module Initialization, all statics are initialized here -bool -initModulePart_DataSourceClient(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&datasourceclient_type) < 0) { - return (false); - } - void* dscp = &datasourceclient_type; - if (PyModule_AddObject(mod, "DataSourceClient", static_cast(dscp)) < 0) { - return (false); - } - Py_INCREF(&datasourceclient_type); - - isc::dns::python::addClassVariable(datasourceclient_type, "SUCCESS", - Py_BuildValue("I", result::SUCCESS)); - isc::dns::python::addClassVariable(datasourceclient_type, "EXIST", - Py_BuildValue("I", result::EXIST)); - isc::dns::python::addClassVariable(datasourceclient_type, "NOTFOUND", - Py_BuildValue("I", result::NOTFOUND)); - isc::dns::python::addClassVariable(datasourceclient_type, "PARTIALMATCH", - Py_BuildValue("I", result::PARTIALMATCH)); - - return (true); -} -} // namespace internal - } // namespace python } // namespace datasrc } // namespace isc diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc index 3c1da3cffe..dd1011f73a 100644 --- a/src/lib/python/isc/datasrc/datasrc.cc +++ b/src/lib/python/isc/datasrc/datasrc.cc @@ -29,9 +29,12 @@ #include "updater_python.h" #include +#include +using namespace isc::datasrc; using namespace isc::datasrc::python; using namespace isc::util::python; +using namespace isc::dns::python; namespace isc { namespace datasrc { @@ -54,19 +57,111 @@ getDataSourceException(const char* ex_name) { return (ex_obj); } -namespace internal { -bool initModulePart_DataSourceClient(PyObject* mod); -bool initModulePart_ZoneFinder(PyObject* mod); -bool initModulePart_ZoneIterator(PyObject* mod); -bool initModulePart_ZoneUpdater(PyObject* mod); -} // end namespace internal - } // end namespace python } // end namespace datasrc } // end namespace isc namespace { +bool +initModulePart_DataSourceClient(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&datasourceclient_type) < 0) { + return (false); + } + void* dscp = &datasourceclient_type; + if (PyModule_AddObject(mod, "DataSourceClient", static_cast(dscp)) < 0) { + return (false); + } + Py_INCREF(&datasourceclient_type); + + addClassVariable(datasourceclient_type, "SUCCESS", + Py_BuildValue("I", result::SUCCESS)); + addClassVariable(datasourceclient_type, "EXIST", + Py_BuildValue("I", result::EXIST)); + addClassVariable(datasourceclient_type, "NOTFOUND", + Py_BuildValue("I", result::NOTFOUND)); + addClassVariable(datasourceclient_type, "PARTIALMATCH", + Py_BuildValue("I", result::PARTIALMATCH)); + + return (true); +} + +bool +initModulePart_ZoneFinder(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&zonefinder_type) < 0) { + return (false); + } + void* zip = &zonefinder_type; + if (PyModule_AddObject(mod, "ZoneFinder", static_cast(zip)) < 0) { + return (false); + } + Py_INCREF(&zonefinder_type); + + addClassVariable(zonefinder_type, "SUCCESS", + Py_BuildValue("I", ZoneFinder::SUCCESS)); + addClassVariable(zonefinder_type, "DELEGATION", + Py_BuildValue("I", ZoneFinder::DELEGATION)); + addClassVariable(zonefinder_type, "NXDOMAIN", + Py_BuildValue("I", ZoneFinder::NXDOMAIN)); + addClassVariable(zonefinder_type, "NXRRSET", + Py_BuildValue("I", ZoneFinder::NXRRSET)); + addClassVariable(zonefinder_type, "CNAME", + Py_BuildValue("I", ZoneFinder::CNAME)); + addClassVariable(zonefinder_type, "DNAME", + Py_BuildValue("I", ZoneFinder::DNAME)); + + addClassVariable(zonefinder_type, "FIND_DEFAULT", + Py_BuildValue("I", ZoneFinder::FIND_DEFAULT)); + addClassVariable(zonefinder_type, "FIND_GLUE_OK", + Py_BuildValue("I", ZoneFinder::FIND_GLUE_OK)); + addClassVariable(zonefinder_type, "FIND_DNSSEC", + Py_BuildValue("I", ZoneFinder::FIND_DNSSEC)); + + + return (true); +} + +bool +initModulePart_ZoneIterator(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&zoneiterator_type) < 0) { + return (false); + } + void* zip = &zoneiterator_type; + if (PyModule_AddObject(mod, "ZoneIterator", static_cast(zip)) < 0) { + return (false); + } + Py_INCREF(&zoneiterator_type); + + return (true); +} + +bool +initModulePart_ZoneUpdater(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&zoneupdater_type) < 0) { + return (false); + } + void* zip = &zoneupdater_type; + if (PyModule_AddObject(mod, "ZoneUpdater", static_cast(zip)) < 0) { + return (false); + } + Py_INCREF(&zoneupdater_type); + + return (true); +} + + PyObject* po_DataSourceError; PyObject* po_NotImplemented; @@ -86,8 +181,6 @@ PyModuleDef iscDataSrc = { } // end anonymous namespace -using namespace isc::datasrc::python::internal; - PyMODINIT_FUNC PyInit_datasrc(void) { PyObject* mod = PyModule_Create(&iscDataSrc); diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index 2f7cb50b44..bba83bbf67 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -100,6 +100,9 @@ PyObject* ZoneFinder_getOrigin(PyObject* po_self, PyObject*) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } @@ -134,6 +137,9 @@ PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } else { return (NULL); @@ -212,47 +218,6 @@ PyTypeObject zonefinder_type = { 0 // tp_version_tag }; -namespace internal { -// Module Initialization, all statics are initialized here -bool -initModulePart_ZoneFinder(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&zonefinder_type) < 0) { - return (false); - } - void* zip = &zonefinder_type; - if (PyModule_AddObject(mod, "ZoneFinder", static_cast(zip)) < 0) { - return (false); - } - Py_INCREF(&zonefinder_type); - - isc::dns::python::addClassVariable(zonefinder_type, "SUCCESS", - Py_BuildValue("I", ZoneFinder::SUCCESS)); - isc::dns::python::addClassVariable(zonefinder_type, "DELEGATION", - Py_BuildValue("I", ZoneFinder::DELEGATION)); - isc::dns::python::addClassVariable(zonefinder_type, "NXDOMAIN", - Py_BuildValue("I", ZoneFinder::NXDOMAIN)); - isc::dns::python::addClassVariable(zonefinder_type, "NXRRSET", - Py_BuildValue("I", ZoneFinder::NXRRSET)); - isc::dns::python::addClassVariable(zonefinder_type, "CNAME", - Py_BuildValue("I", ZoneFinder::CNAME)); - isc::dns::python::addClassVariable(zonefinder_type, "DNAME", - Py_BuildValue("I", ZoneFinder::DNAME)); - - isc::dns::python::addClassVariable(zonefinder_type, "FIND_DEFAULT", - Py_BuildValue("I", ZoneFinder::FIND_DEFAULT)); - isc::dns::python::addClassVariable(zonefinder_type, "FIND_GLUE_OK", - Py_BuildValue("I", ZoneFinder::FIND_GLUE_OK)); - isc::dns::python::addClassVariable(zonefinder_type, "FIND_DNSSEC", - Py_BuildValue("I", ZoneFinder::FIND_DNSSEC)); - - - return (true); -} -} // namespace internal - PyObject* createZoneFinderObject(isc::datasrc::ZoneFinderPtr source) { s_ZoneFinder* py_zi = static_cast( diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index 342a5868bf..a30df21d81 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -96,6 +96,9 @@ PyObject* ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } @@ -173,26 +176,6 @@ PyTypeObject zoneiterator_type = { 0 // tp_version_tag }; -namespace internal { -// Module Initialization, all statics are initialized here -bool -initModulePart_ZoneIterator(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&zoneiterator_type) < 0) { - return (false); - } - void* zip = &zoneiterator_type; - if (PyModule_AddObject(mod, "ZoneIterator", static_cast(zip)) < 0) { - return (false); - } - Py_INCREF(&zoneiterator_type); - - return (true); -} -} // namespace internal - PyObject* createZoneIteratorObject(isc::datasrc::ZoneIteratorPtr source) { s_ZoneIterator* py_zi = static_cast( diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index 8acf5b9e21..fab37617f3 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -146,6 +146,9 @@ PyObject* ZoneUpdater_getClass(PyObject* po_self, PyObject*) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } @@ -156,6 +159,9 @@ PyObject* ZoneUpdater_getOrigin(PyObject* po_self, PyObject*) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } @@ -190,6 +196,9 @@ PyObject* ZoneUpdater_find(PyObject* po_self, PyObject* args) { } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + return (NULL); } } else { return (NULL); @@ -279,26 +288,6 @@ PyTypeObject zoneupdater_type = { 0 // tp_version_tag }; -namespace internal { -// Module Initialization, all statics are initialized here -bool -initModulePart_ZoneUpdater(PyObject* mod) { - // We initialize the static description object with PyType_Ready(), - // then add it to the module. This is not just a check! (leaving - // this out results in segmentation faults) - if (PyType_Ready(&zoneupdater_type) < 0) { - return (false); - } - void* zip = &zoneupdater_type; - if (PyModule_AddObject(mod, "ZoneUpdater", static_cast(zip)) < 0) { - return (false); - } - Py_INCREF(&zoneupdater_type); - - return (true); -} -} // namespace internal - PyObject* createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source) { s_ZoneUpdater* py_zi = static_cast( From 0fa8006ade38ac7206ff57934f3bb866be6407a2 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 20 Sep 2011 23:18:35 +0200 Subject: [PATCH 749/974] [1179] style fixes --- src/lib/python/isc/datasrc/client_python.cc | 60 ++++++++------- src/lib/python/isc/datasrc/finder_python.cc | 52 ++++++------- src/lib/python/isc/datasrc/iterator_python.cc | 32 +++----- src/lib/python/isc/datasrc/updater_python.cc | 75 +++++++++++-------- 4 files changed, 113 insertions(+), 106 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index 501a0f0a38..c53a846702 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -44,6 +44,7 @@ using namespace std; using namespace isc::util::python; +using namespace isc::dns::python; using namespace isc::datasrc; using namespace isc::datasrc::python; @@ -59,25 +60,16 @@ public: typedef CPPPyObjectContainer DataSourceClientContainer; -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// - -// General creation and destruction -int DataSourceClient_init(s_DataSourceClient* self, PyObject* args); -void DataSourceClient_destroy(s_DataSourceClient* self); - // These are the functions we export // PyObject* DataSourceClient_findZone(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); PyObject *name; - if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name)) { + if (PyArg_ParseTuple(args, "O!", &name_type, &name)) { try { DataSourceClient::FindResult find_result( - self->cppobj->findZone(isc::dns::python::PyName_ToName(name))); + self->cppobj->findZone(PyName_ToName(name))); result::Result r = find_result.code; ZoneFinderPtr zfp = find_result.zone_finder; @@ -87,7 +79,8 @@ DataSourceClient_findZone(PyObject* po_self, PyObject* args) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } else { @@ -99,11 +92,13 @@ PyObject* DataSourceClient_getIterator(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); PyObject *name_obj; - if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name_obj)) { + if (PyArg_ParseTuple(args, "O!", &name_type, &name_obj)) { try { - return (createZoneIteratorObject(self->cppobj->getIterator(isc::dns::python::PyName_ToName(name_obj)))); + return (createZoneIteratorObject( + self->cppobj->getIterator(PyName_ToName(name_obj)))); } catch (const isc::NotImplemented& ne) { - PyErr_SetString(getDataSourceException("NotImplemented"), ne.what()); + PyErr_SetString(getDataSourceException("NotImplemented"), + ne.what()); return (NULL); } catch (const DataSourceError& dse) { PyErr_SetString(getDataSourceException("Error"), dse.what()); @@ -112,7 +107,8 @@ DataSourceClient_getIterator(PyObject* po_self, PyObject* args) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } else { @@ -125,12 +121,16 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); PyObject *name_obj; PyObject *replace_obj; - if (PyArg_ParseTuple(args, "O!O", &isc::dns::python::name_type, &name_obj, &replace_obj) && PyBool_Check(replace_obj)) { + if (PyArg_ParseTuple(args, "O!O", &name_type, &name_obj, &replace_obj) && + PyBool_Check(replace_obj)) { bool replace = (replace_obj != Py_False); try { - return (createZoneUpdaterObject(self->cppobj->getUpdater(isc::dns::python::PyName_ToName(name_obj), replace))); + return (createZoneUpdaterObject( + self->cppobj->getUpdater(PyName_ToName(name_obj), + replace))); } catch (const isc::NotImplemented& ne) { - PyErr_SetString(getDataSourceException("NotImplemented"), ne.what()); + PyErr_SetString(getDataSourceException("NotImplemented"), + ne.what()); return (NULL); } catch (const DataSourceError& dse) { PyErr_SetString(getDataSourceException("Error"), dse.what()); @@ -139,7 +139,8 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } else { @@ -156,12 +157,13 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) { // 3. Argument type // 4. Documentation PyMethodDef DataSourceClient_methods[] = { - { "find_zone", reinterpret_cast(DataSourceClient_findZone), METH_VARARGS, - DataSourceClient_findZone_doc }, - { "get_iterator", reinterpret_cast(DataSourceClient_getIterator), METH_VARARGS, + { "find_zone", reinterpret_cast(DataSourceClient_findZone), + METH_VARARGS, DataSourceClient_findZone_doc }, + { "get_iterator", + reinterpret_cast(DataSourceClient_getIterator), METH_VARARGS, DataSourceClient_getIterator_doc }, - { "get_updater", reinterpret_cast(DataSourceClient_getUpdater), METH_VARARGS, - DataSourceClient_getUpdater_doc }, + { "get_updater", reinterpret_cast(DataSourceClient_getUpdater), + METH_VARARGS, DataSourceClient_getUpdater_doc }, { NULL, NULL, 0, NULL } }; @@ -222,9 +224,9 @@ namespace python { PyTypeObject datasourceclient_type = { PyVarObject_HEAD_INIT(NULL, 0) "datasrc.DataSourceClient", - sizeof(s_DataSourceClient), // tp_basicsize + sizeof(s_DataSourceClient), // tp_basicsize 0, // tp_itemsize - reinterpret_cast(DataSourceClient_destroy), // tp_dealloc + reinterpret_cast(DataSourceClient_destroy),// tp_dealloc NULL, // tp_print NULL, // tp_getattr NULL, // tp_setattr @@ -247,7 +249,7 @@ PyTypeObject datasourceclient_type = { 0, // tp_weaklistoffset NULL, // tp_iter NULL, // tp_iternext - DataSourceClient_methods, // tp_methods + DataSourceClient_methods, // tp_methods NULL, // tp_members NULL, // tp_getset NULL, // tp_base @@ -255,7 +257,7 @@ PyTypeObject datasourceclient_type = { NULL, // tp_descr_get NULL, // tp_descr_set 0, // tp_dictoffset - reinterpret_cast(DataSourceClient_init), // tp_init + reinterpret_cast(DataSourceClient_init),// tp_init NULL, // tp_alloc PyType_GenericNew, // tp_new NULL, // tp_free diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index bba83bbf67..e7be8f8354 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -44,6 +44,7 @@ using namespace std; using namespace isc::util::python; +using namespace isc::dns::python; using namespace isc::datasrc; using namespace isc::datasrc::python; @@ -58,11 +59,6 @@ public: // Shortcut type which would be convenient for adding class variables safely. typedef CPPPyObjectContainer ZoneFinderContainer; -// -// We declare the functions here, the definitions are below -// the type definition of the object, since both can use the other -// - // General creation and destruction int ZoneFinder_init(s_ZoneFinder* self, PyObject* args) { @@ -83,43 +79,48 @@ ZoneFinder_destroy(s_ZoneFinder* const self) { // These are the functions we export // -PyObject* ZoneFinder_getClass(PyObject* po_self, PyObject*) { +PyObject* +ZoneFinder_getClass(PyObject* po_self, PyObject*) { s_ZoneFinder* self = static_cast(po_self); try { - return (isc::dns::python::createRRClassObject(self->cppobj->getClass())); + return (createRRClassObject(self->cppobj->getClass())); } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } } -PyObject* ZoneFinder_getOrigin(PyObject* po_self, PyObject*) { +PyObject* +ZoneFinder_getOrigin(PyObject* po_self, PyObject*) { s_ZoneFinder* self = static_cast(po_self); try { - return (isc::dns::python::createNameObject(self->cppobj->getOrigin())); + return (createNameObject(self->cppobj->getOrigin())); } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } -PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) { +PyObject* +ZoneFinder_find(PyObject* po_self, PyObject* args) { s_ZoneFinder* const self = static_cast(po_self); PyObject *name; PyObject *rrtype; PyObject *target; int options_int; - if (PyArg_ParseTuple(args, "O!O!OI", &isc::dns::python::name_type, &name, - &isc::dns::python::rrtype_type, &rrtype, + if (PyArg_ParseTuple(args, "O!O!OI", &name_type, &name, + &rrtype_type, &rrtype, &target, &options_int)) { try { - ZoneFinder::FindOptions options = static_cast(options_int); + ZoneFinder::FindOptions options = + static_cast(options_int); ZoneFinder::FindResult find_result( - self->cppobj->find(isc::dns::python::PyName_ToName(name), - isc::dns::python::PyRRType_ToRRType(rrtype), + self->cppobj->find(PyName_ToName(name), + PyRRType_ToRRType(rrtype), NULL, options )); @@ -127,7 +128,7 @@ PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) { isc::dns::ConstRRsetPtr rrsp = find_result.rrset; if (rrsp) { // Use N instead of O so the refcount isn't increased twice - return (Py_BuildValue("IN", r, isc::dns::python::createRRsetObject(*rrsp))); + return (Py_BuildValue("IN", r, createRRsetObject(*rrsp))); } else { return (Py_BuildValue("IO", r, Py_None)); } @@ -138,7 +139,8 @@ PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } else { @@ -154,10 +156,10 @@ PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) { // 3. Argument type // 4. Documentation PyMethodDef ZoneFinder_methods[] = { - { "get_origin", reinterpret_cast(ZoneFinder_getOrigin), METH_NOARGS, - ZoneFinder_getOrigin_doc }, - { "get_class", reinterpret_cast(ZoneFinder_getClass), METH_NOARGS, - ZoneFinder_getClass_doc }, + { "get_origin", reinterpret_cast(ZoneFinder_getOrigin), + METH_NOARGS, ZoneFinder_getOrigin_doc }, + { "get_class", reinterpret_cast(ZoneFinder_getClass), + METH_NOARGS, ZoneFinder_getClass_doc }, { "find", reinterpret_cast(ZoneFinder_find), METH_VARARGS, ZoneFinder_find_doc }, { NULL, NULL, 0, NULL } @@ -171,9 +173,9 @@ namespace python { PyTypeObject zonefinder_type = { PyVarObject_HEAD_INIT(NULL, 0) "datasrc.ZoneFinder", - sizeof(s_ZoneFinder), // tp_basicsize + sizeof(s_ZoneFinder), // tp_basicsize 0, // tp_itemsize - reinterpret_cast(ZoneFinder_destroy), // tp_dealloc + reinterpret_cast(ZoneFinder_destroy),// tp_dealloc NULL, // tp_print NULL, // tp_getattr NULL, // tp_setattr @@ -196,7 +198,7 @@ PyTypeObject zonefinder_type = { 0, // tp_weaklistoffset NULL, // tp_iter NULL, // tp_iternext - ZoneFinder_methods, // tp_methods + ZoneFinder_methods, // tp_methods NULL, // tp_members NULL, // tp_getset NULL, // tp_base diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index a30df21d81..216bd67e95 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -40,6 +40,7 @@ using namespace std; using namespace isc::util::python; +using namespace isc::dns::python; using namespace isc::datasrc; using namespace isc::datasrc::python; @@ -52,8 +53,8 @@ public: }; // Shortcut type which would be convenient for adding class variables safely. -typedef CPPPyObjectContainer ZoneIteratorContainer; - +typedef CPPPyObjectContainer + ZoneIteratorContainer; // General creation and destruction int @@ -79,14 +80,15 @@ ZoneIterator_destroy(s_ZoneIterator* const self) { // We declare the functions here, the definitions are below // the type definition of the object, since both can use the other // -PyObject* ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) { +PyObject* +ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) { s_ZoneIterator* self = static_cast(po_self); try { isc::dns::ConstRRsetPtr rrset = self->cppobj->getNextRRset(); if (!rrset) { Py_RETURN_NONE; } - return (isc::dns::python::createRRsetObject(*rrset)); + return (createRRsetObject(*rrset)); } catch (const isc::Exception& isce) { // isc::Unexpected is thrown when we call getNextRRset() when we are // already done iterating ('iterating past end') @@ -97,26 +99,16 @@ PyObject* ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } -// These are the functions we export -// - -// These are the functions we export -// For a minimal support, we don't need them. - -// This list contains the actual set of functions we have in -// python. Each entry has -// 1. Python method name -// 2. Our static function here -// 3. Argument type -// 4. Documentation PyMethodDef ZoneIterator_methods[] = { - { "get_next_rrset", reinterpret_cast(ZoneIterator_getNextRRset), - METH_NOARGS, ZoneIterator_getNextRRset_doc }, + { "get_next_rrset", + reinterpret_cast(ZoneIterator_getNextRRset), METH_NOARGS, + ZoneIterator_getNextRRset_doc }, { NULL, NULL, 0, NULL } }; @@ -131,7 +123,7 @@ PyTypeObject zoneiterator_type = { "datasrc.ZoneIterator", sizeof(s_ZoneIterator), // tp_basicsize 0, // tp_itemsize - reinterpret_cast(ZoneIterator_destroy), // tp_dealloc + reinterpret_cast(ZoneIterator_destroy),// tp_dealloc NULL, // tp_print NULL, // tp_getattr NULL, // tp_setattr diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index fab37617f3..c45dad4fc0 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -44,6 +44,7 @@ using namespace std; using namespace isc::util::python; +using namespace isc::dns::python; using namespace isc::datasrc; using namespace isc::datasrc::python; @@ -85,12 +86,13 @@ ZoneUpdater_destroy(s_ZoneUpdater* const self) { // These are the functions we export // -PyObject* ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) { +PyObject* +ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) { s_ZoneUpdater* const self = static_cast(po_self); PyObject* rrset_obj; - if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { + if (PyArg_ParseTuple(args, "O!", &rrset_type, &rrset_obj)) { try { - self->cppobj->addRRset(isc::dns::python::PyRRset_ToRRset(rrset_obj)); + self->cppobj->addRRset(PyRRset_ToRRset(rrset_obj)); Py_RETURN_NONE; } catch (const DataSourceError& dse) { PyErr_SetString(getDataSourceException("Error"), dse.what()); @@ -104,12 +106,13 @@ PyObject* ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) { } } -PyObject* ZoneUpdater_deleteRRset(PyObject* po_self, PyObject* args) { +PyObject* +ZoneUpdater_deleteRRset(PyObject* po_self, PyObject* args) { s_ZoneUpdater* const self = static_cast(po_self); PyObject* rrset_obj; - if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) { + if (PyArg_ParseTuple(args, "O!", &rrset_type, &rrset_obj)) { try { - self->cppobj->deleteRRset(isc::dns::python::PyRRset_ToRRset(rrset_obj)); + self->cppobj->deleteRRset(PyRRset_ToRRset(rrset_obj)); Py_RETURN_NONE; } catch (const DataSourceError& dse) { PyErr_SetString(getDataSourceException("Error"), dse.what()); @@ -123,7 +126,8 @@ PyObject* ZoneUpdater_deleteRRset(PyObject* po_self, PyObject* args) { } } -PyObject* ZoneUpdater_commit(PyObject* po_self, PyObject*) { +PyObject* +ZoneUpdater_commit(PyObject* po_self, PyObject*) { s_ZoneUpdater* const self = static_cast(po_self); try { self->cppobj->commit(); @@ -139,46 +143,52 @@ PyObject* ZoneUpdater_commit(PyObject* po_self, PyObject*) { // These are the functions we export // -PyObject* ZoneUpdater_getClass(PyObject* po_self, PyObject*) { +PyObject* +ZoneUpdater_getClass(PyObject* po_self, PyObject*) { s_ZoneUpdater* self = static_cast(po_self); try { - return (isc::dns::python::createRRClassObject(self->cppobj->getFinder().getClass())); + return (createRRClassObject(self->cppobj->getFinder().getClass())); } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } -PyObject* ZoneUpdater_getOrigin(PyObject* po_self, PyObject*) { +PyObject* +ZoneUpdater_getOrigin(PyObject* po_self, PyObject*) { s_ZoneUpdater* self = static_cast(po_self); try { - return (isc::dns::python::createNameObject(self->cppobj->getFinder().getOrigin())); + return (createNameObject(self->cppobj->getFinder().getOrigin())); } catch (const std::exception& exc) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } -PyObject* ZoneUpdater_find(PyObject* po_self, PyObject* args) { +PyObject* +ZoneUpdater_find(PyObject* po_self, PyObject* args) { s_ZoneUpdater* const self = static_cast(po_self); PyObject *name; PyObject *rrtype; PyObject *target; int options_int; - if (PyArg_ParseTuple(args, "O!O!OI", &isc::dns::python::name_type, &name, - &isc::dns::python::rrtype_type, &rrtype, + if (PyArg_ParseTuple(args, "O!O!OI", &name_type, &name, + &rrtype_type, &rrtype, &target, &options_int)) { try { - ZoneFinder::FindOptions options = static_cast(options_int); + ZoneFinder::FindOptions options = + static_cast(options_int); ZoneFinder::FindResult find_result( - self->cppobj->getFinder().find(isc::dns::python::PyName_ToName(name), - isc::dns::python::PyRRType_ToRRType(rrtype), + self->cppobj->getFinder().find(PyName_ToName(name), + PyRRType_ToRRType(rrtype), NULL, options )); @@ -186,7 +196,7 @@ PyObject* ZoneUpdater_find(PyObject* po_self, PyObject* args) { isc::dns::ConstRRsetPtr rrsp = find_result.rrset; if (rrsp) { // Use N instead of O so the refcount isn't increased twice - return Py_BuildValue("IN", r, isc::dns::python::createRRsetObject(*rrsp)); + return Py_BuildValue("IN", r, createRRsetObject(*rrsp)); } else { return Py_BuildValue("IO", r, Py_None); } @@ -197,7 +207,8 @@ PyObject* ZoneUpdater_find(PyObject* po_self, PyObject* args) { PyErr_SetString(getDataSourceException("Error"), exc.what()); return (NULL); } catch (...) { - PyErr_SetString(getDataSourceException("Error"), "Unexpected exception"); + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); return (NULL); } } else { @@ -214,20 +225,20 @@ PyObject* ZoneUpdater_find(PyObject* po_self, PyObject* args) { // 3. Argument type // 4. Documentation PyMethodDef ZoneUpdater_methods[] = { - { "add_rrset", reinterpret_cast(ZoneUpdater_addRRset), METH_VARARGS, - ZoneUpdater_addRRset_doc }, - { "delete_rrset", reinterpret_cast(ZoneUpdater_deleteRRset), METH_VARARGS, - ZoneUpdater_deleteRRset_doc }, + { "add_rrset", reinterpret_cast(ZoneUpdater_addRRset), + METH_VARARGS, ZoneUpdater_addRRset_doc }, + { "delete_rrset", reinterpret_cast(ZoneUpdater_deleteRRset), + METH_VARARGS, ZoneUpdater_deleteRRset_doc }, { "commit", reinterpret_cast(ZoneUpdater_commit), METH_NOARGS, ZoneUpdater_commit_doc }, // Instead of a getFinder, we implement the finder functionality directly // This is because ZoneFinder is non-copyable, and we should not create // a ZoneFinder object from a reference only (which is what is returned // by getFinder(). Apart from that - { "get_origin", reinterpret_cast(ZoneUpdater_getOrigin), METH_NOARGS, - ZoneFinder_getOrigin_doc }, - { "get_class", reinterpret_cast(ZoneUpdater_getClass), METH_NOARGS, - ZoneFinder_getClass_doc }, + { "get_origin", reinterpret_cast(ZoneUpdater_getOrigin), + METH_NOARGS, ZoneFinder_getOrigin_doc }, + { "get_class", reinterpret_cast(ZoneUpdater_getClass), + METH_NOARGS, ZoneFinder_getClass_doc }, { "find", reinterpret_cast(ZoneUpdater_find), METH_VARARGS, ZoneFinder_find_doc }, { NULL, NULL, 0, NULL } @@ -241,9 +252,9 @@ namespace python { PyTypeObject zoneupdater_type = { PyVarObject_HEAD_INIT(NULL, 0) "datasrc.ZoneUpdater", - sizeof(s_ZoneUpdater), // tp_basicsize + sizeof(s_ZoneUpdater), // tp_basicsize 0, // tp_itemsize - reinterpret_cast(ZoneUpdater_destroy), // tp_dealloc + reinterpret_cast(ZoneUpdater_destroy),// tp_dealloc NULL, // tp_print NULL, // tp_getattr NULL, // tp_setattr @@ -266,7 +277,7 @@ PyTypeObject zoneupdater_type = { 0, // tp_weaklistoffset NULL, // tp_iter NULL, // tp_iternext - ZoneUpdater_methods, // tp_methods + ZoneUpdater_methods, // tp_methods NULL, // tp_members NULL, // tp_getset NULL, // tp_base From ccb4c0aa696918c579a0b80448fc93606152ec93 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 00:00:34 +0200 Subject: [PATCH 750/974] [1179] column width in test --- .../python/isc/datasrc/tests/datasrc_test.py | 159 ++++++++++++++---- 1 file changed, 125 insertions(+), 34 deletions(-) diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py index 472a0463b8..206d202c94 100644 --- a/src/lib/python/isc/datasrc/tests/datasrc_test.py +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -36,37 +36,113 @@ class DataSrcClient(unittest.TestCase): # for RRSIGS, the TTL's are currently modified. This test should # start failing when we fix that. rrs = dsc.get_iterator(isc.dns.Name("sql1.example.com.")) - self.assertEqual("sql1.example.com. 3600 IN DNSKEY 256 3 5 AwEAAdYdRhBAEY67R/8G1N5AjGF6asIiNh/pNGeQ8xDQP13JN2lo+sNqWcmpYNhuVqRbLB+mamsU1XcCICSBvAlSmfz/ZUdafX23knArTlALxMmspcfdpqun3Yr3YYnztuj06rV7RqmveYckWvAUXVYMSMQZfJ305fs0dE/xLztL/CzZ\nsql1.example.com. 3600 IN DNSKEY 257 3 5 AwEAAbaKDSa9XEFTsjSYpUTHRotTS9Tz3krfDucugW5UokGQKC26QlyHXlPTZkC+aRFUs/dicJX2kopndLcnlNAPWiKnKtrsFSCnIJDBZIyvcKq+9RXmV3HK3bUdHnQZ88IZWBRmWKfZ6wnzHo53kdYKAemTErkztaX3lRRPLYWpxRcDPEjysXT3Lh0vfL5D+CIO1yKw/q7C+v6+/kYAxc2lfbNE3HpklSuF+dyX4nXxWgzbcFuLz5Bwfq6ZJ9RYe/kNkA0uMWNa1KkGeRh8gg22kgD/KT5hPTnpezUWLvoY5Qc7IB3T0y4n2JIwiF2ZrZYVrWgDjRWAzGsxJiJyjd6w2k0=\n", rrs.get_next_rrset().to_text()) - self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\nsql1.example.com. 3600 IN NS dns02.example.com.\nsql1.example.com. 3600 IN NS dns03.example.com.\n", rrs.get_next_rrset().to_text()) - self.assertEqual("sql1.example.com. 7200 IN NSEC www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY\n", rrs.get_next_rrset().to_text()) - self.assertEqual("sql1.example.com. 3600 IN RRSIG SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG DNSKEY 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG DNSKEY 5 3 3600 20100322084536 20100220084536 33313 sql1.example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) - self.assertEqual("sql1.example.com. 3600 IN SOA master.example.com. admin.example.com. 678 3600 1800 2419200 7200\n", rrs.get_next_rrset().to_text()) - self.assertEqual("www.sql1.example.com. 3600 IN A 192.0.2.100\n", rrs.get_next_rrset().to_text()) - self.assertEqual("www.sql1.example.com. 7200 IN NSEC sql1.example.com. A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("www.sql1.example.com. 3600 IN RRSIG A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nwww.sql1.example.com. 3600 IN RRSIG NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 3600 IN DNSKEY 256 3 5 AwEAAdYdRh" + + "BAEY67R/8G1N5AjGF6asIiNh/pNGeQ8xDQP13JN2lo+sNqWcmpY" + + "NhuVqRbLB+mamsU1XcCICSBvAlSmfz/ZUdafX23knArTlALxMms" + + "pcfdpqun3Yr3YYnztuj06rV7RqmveYckWvAUXVYMSMQZfJ305fs" + + "0dE/xLztL/CzZ\nsql1.example.com. 3600 IN DNSKEY 257" + + " 3 5 AwEAAbaKDSa9XEFTsjSYpUTHRotTS9Tz3krfDucugW5Uok" + + "GQKC26QlyHXlPTZkC+aRFUs/dicJX2kopndLcnlNAPWiKnKtrsF" + + "SCnIJDBZIyvcKq+9RXmV3HK3bUdHnQZ88IZWBRmWKfZ6wnzHo53" + + "kdYKAemTErkztaX3lRRPLYWpxRcDPEjysXT3Lh0vfL5D+CIO1yK" + + "w/q7C+v6+/kYAxc2lfbNE3HpklSuF+dyX4nXxWgzbcFuLz5Bwfq" + + "6ZJ9RYe/kNkA0uMWNa1KkGeRh8gg22kgD/KT5hPTnpezUWLvoY5" + + "Qc7IB3T0y4n2JIwiF2ZrZYVrWgDjRWAzGsxJiJyjd6w2k0=\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\nsq" + + "l1.example.com. 3600 IN NS dns02.example.com.\nsql1" + + ".example.com. 3600 IN NS dns03.example.com.\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 7200 IN NSEC www.sql1.example.com" + + ". NS SOA RRSIG NSEC DNSKEY\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 3600 IN RRSIG SOA 5 3 3600 201003" + + "22084536 20100220084536 12447 sql1.example.com. FAK" + + "EFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG NS 5" + + " 3 3600 20100322084536 20100220084536 12447 sql1.ex" + + "ample.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600" + + " IN RRSIG NSEC 5 3 7200 20100322084536 201002200845" + + "36 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.e" + + "xample.com. 3600 IN RRSIG DNSKEY 5 3 3600 201003220" + + "84536 20100220084536 12447 sql1.example.com. FAKEFA" + + "KEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG DNSKEY " + + "5 3 3600 20100322084536 20100220084536 33313 sql1.e" + + "xample.com. FAKEFAKEFAKEFAKE\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("sql1.example.com. 3600 IN SOA master.example.com. a" + + "dmin.example.com. 678 3600 1800 2419200 7200\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("www.sql1.example.com. 3600 IN A 192.0.2.100\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("www.sql1.example.com. 7200 IN NSEC sql1.example.com" + + ". A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("www.sql1.example.com. 3600 IN RRSIG A 5 4 3600 2010" + + "0322084536 20100220084536 12447 sql1.example.com. F" + + "AKEFAKEFAKEFAKE\nwww.sql1.example.com. 3600 IN RRSI" + + "G NSEC 5 4 7200 20100322084536 20100220084536 12447" + + " sql1.example.com. FAKEFAKEFAKEFAKE\n", + rrs.get_next_rrset().to_text()) self.assertEqual(None, rrs.get_next_rrset()) # TODO should we catch this (iterating past end) and just return None # instead of failing? self.assertRaises(isc.datasrc.Error, rrs.get_next_rrset) rrs = dsc.get_iterator(isc.dns.Name("example.com")) - self.assertEqual("*.wild.example.com. 3600 IN A 192.0.2.255\n", rrs.get_next_rrset().to_text()) - self.assertEqual("*.wild.example.com. 7200 IN NSEC www.example.com. A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("*.wild.example.com. 3600 IN RRSIG A 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n*.wild.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) - self.assertEqual("cname-ext.example.com. 3600 IN CNAME www.sql1.example.com.\n", rrs.get_next_rrset().to_text()) - self.assertEqual("cname-ext.example.com. 7200 IN NSEC cname-int.example.com. CNAME RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("cname-ext.example.com. 3600 IN RRSIG CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\ncname-ext.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) - self.assertEqual("cname-int.example.com. 3600 IN CNAME www.example.com.\n", rrs.get_next_rrset().to_text()) - self.assertEqual("cname-int.example.com. 7200 IN NSEC dname.example.com. CNAME RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("cname-int.example.com. 3600 IN RRSIG CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\ncname-int.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) - self.assertEqual("dname.example.com. 3600 IN DNAME sql1.example.com.\n", rrs.get_next_rrset().to_text()) - self.assertEqual("dname.example.com. 7200 IN NSEC dns01.example.com. DNAME RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("dname.example.com. 3600 IN RRSIG DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\ndname.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) - self.assertEqual("dns01.example.com. 3600 IN A 192.0.2.1\n", rrs.get_next_rrset().to_text()) - self.assertEqual("dns01.example.com. 7200 IN NSEC dns02.example.com. A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("dns01.example.com. 3600 IN RRSIG A 5 3 3600 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\ndns01.example.com. 3600 IN RRSIG NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) + self.assertEqual("*.wild.example.com. 3600 IN A 192.0.2.255\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("*.wild.example.com. 7200 IN NSEC www.example.com. A" + + " RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("*.wild.example.com. 3600 IN RRSIG A 5 3 3600 201003" + + "22084538 20100220084538 33495 example.com. FAKEFAKE" + + "FAKEFAKE\n*.wild.example.com. 3600 IN RRSIG NSEC 5 " + + "3 7200 20100322084538 20100220084538 33495 example." + + "com. FAKEFAKEFAKEFAKE\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("cname-ext.example.com. 3600 IN CNAME www.sql1.examp" + + "le.com.\n", rrs.get_next_rrset().to_text()) + self.assertEqual("cname-ext.example.com. 7200 IN NSEC cname-int.examp" + + "le.com. CNAME RRSIG NSEC\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("cname-ext.example.com. 3600 IN RRSIG CNAME 5 3 3600" + + " 20100322084538 20100220084538 33495 example.com. F" + + "AKEFAKEFAKEFAKE\ncname-ext.example.com. 3600 IN RRS" + + "IG NSEC 5 3 7200 20100322084538 20100220084538 3349" + + "5 example.com. FAKEFAKEFAKEFAKE\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("cname-int.example.com. 3600 IN CNAME www.example.co" + + "m.\n", rrs.get_next_rrset().to_text()) + self.assertEqual("cname-int.example.com. 7200 IN NSEC dname.example.c" + + "om. CNAME RRSIG NSEC\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("cname-int.example.com. 3600 IN RRSIG CNAME 5 3 3600" + + " 20100322084538 20100220084538 33495 example.com. F" + + "AKEFAKEFAKEFAKE\ncname-int.example.com. 3600 IN RRS" + + "IG NSEC 5 3 7200 20100322084538 20100220084538 3349" + + "5 example.com. FAKEFAKEFAKEFAKE\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("dname.example.com. 3600 IN DNAME sql1.example.com.\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("dname.example.com. 7200 IN NSEC dns01.example.com. " + + "DNAME RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("dname.example.com. 3600 IN RRSIG DNAME 5 3 3600 201" + + "00322084538 20100220084538 33495 example.com. FAKEF" + + "AKEFAKEFAKE\ndname.example.com. 3600 IN RRSIG NSEC " + + "5 3 7200 20100322084538 20100220084538 33495 exampl" + + "e.com. FAKEFAKEFAKEFAKE\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("dns01.example.com. 3600 IN A 192.0.2.1\n", + rrs.get_next_rrset().to_text()) + self.assertEqual("dns01.example.com. 7200 IN NSEC dns02.example.com. " + + "A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) + self.assertEqual("dns01.example.com. 3600 IN RRSIG A 5 3 3600 2010032" + + "2084538 20100220084538 33495 example.com. FAKEFAKEF" + + "AKEFAKE\ndns01.example.com. 3600 IN RRSIG NSEC 5 3 " + + "7200 20100322084538 20100220084538 33495 example.co" + + "m. FAKEFAKEFAKEFAKE\n", + rrs.get_next_rrset().to_text()) # there are more than 80 RRs in this zone... let's just count the rest - # hmm. this makes me think we might want a real iterator returned from get_iterator() + # hmm. this makes me think we might want a real iterator returned from + # get_iterator() rrset = rrs.get_next_rrset() count = 0 while rrset is not None: @@ -92,14 +168,18 @@ class DataSrcClient(unittest.TestCase): None, finder.FIND_DEFAULT) self.assertEqual(finder.SUCCESS, result) - self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", + rrset.to_text()) result, rrset = finder.find(isc.dns.Name("www.sql1.example.com"), isc.dns.RRType.A(), None, finder.FIND_DEFAULT) self.assertEqual(finder.DELEGATION, result) - self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\nsql1.example.com. 3600 IN NS dns02.example.com.\nsql1.example.com. 3600 IN NS dns03.example.com.\n", rrset.to_text()) + self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\n" + + "sql1.example.com. 3600 IN NS dns02.example.com.\n" + + "sql1.example.com. 3600 IN NS dns03.example.com.\n", + rrset.to_text()) result, rrset = finder.find(isc.dns.Name("doesnotexist.example.com"), isc.dns.RRType.A(), @@ -127,7 +207,9 @@ class DataSrcClient(unittest.TestCase): None, finder.FIND_DEFAULT) self.assertEqual(finder.CNAME, result) - self.assertEqual("cname-ext.example.com. 3600 IN CNAME www.sql1.example.com.\n", rrset.to_text()) + self.assertEqual( + "cname-ext.example.com. 3600 IN CNAME www.sql1.example.com.\n", + rrset.to_text()) self.assertRaises(TypeError, finder.find, "foo", @@ -166,14 +248,16 @@ class DataSrcUpdater(unittest.TestCase): None, finder.FIND_DEFAULT) self.assertEqual(finder.SUCCESS, result) - self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", + rrset.to_text()) rrset_to_delete = rrset; # can't delete rrset with associated sig. Abuse that to force an # exception first, then remove the sig, then delete the record updater = dsc.get_updater(isc.dns.Name("example.com"), True) - self.assertRaises(isc.datasrc.Error, updater.delete_rrset, rrset_to_delete) + self.assertRaises(isc.datasrc.Error, updater.delete_rrset, + rrset_to_delete) rrset_to_delete.remove_rrsig() @@ -193,9 +277,12 @@ class DataSrcUpdater(unittest.TestCase): None, finder.FIND_DEFAULT) self.assertEqual(finder.SUCCESS, result) - self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", + rrset.to_text()) updater.commit() + # second commit should raise exception + updater.commit() # the record should be gone now in the 'real' finder as well result, rrset = finder.find(isc.dns.Name("www.example.com"), @@ -218,7 +305,8 @@ class DataSrcUpdater(unittest.TestCase): None, finder.FIND_DEFAULT) self.assertEqual(finder.SUCCESS, result) - self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", + rrset.to_text()) def test_update_delete_abort(self): dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE) @@ -234,14 +322,16 @@ class DataSrcUpdater(unittest.TestCase): None, finder.FIND_DEFAULT) self.assertEqual(finder.SUCCESS, result) - self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", + rrset.to_text()) rrset_to_delete = rrset; # can't delete rrset with associated sig. Abuse that to force an # exception first, then remove the sig, then delete the record updater = dsc.get_updater(isc.dns.Name("example.com"), True) - self.assertRaises(isc.datasrc.Error, updater.delete_rrset, rrset_to_delete) + self.assertRaises(isc.datasrc.Error, updater.delete_rrset, + rrset_to_delete) rrset_to_delete.remove_rrsig() @@ -265,7 +355,8 @@ class DataSrcUpdater(unittest.TestCase): None, finder.FIND_DEFAULT) self.assertEqual(finder.SUCCESS, result) - self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", rrset.to_text()) + self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n", + rrset.to_text()) if __name__ == "__main__": From c38112d8b59bfb6e73b5fbc637fa9eaaae42c52d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 00:50:08 +0200 Subject: [PATCH 751/974] [master] update run_bind10 to include new lib path This should fix the failed systest --- src/bin/bind10/run_bind10.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in index b5b9721542..30e7322d04 100755 --- a/src/bin/bind10/run_bind10.sh.in +++ b/src/bin/bind10/run_bind10.sh.in @@ -30,7 +30,7 @@ export PYTHONPATH # required by loadable python modules. SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@ if test $SET_ENV_LIBRARY_PATH = yes; then - @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ + @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ export @ENV_LIBRARY_PATH@ fi From f7b5370a9bf82b0b480b75275349d8570ee83c4c Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Wed, 21 Sep 2011 11:05:30 +0800 Subject: [PATCH 752/974] [master] Merge trac1112 and update the ChangeLog --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index a71c8a8221..6f84a0764c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +286. [func] ocean + libdns++: Implement the HINFO rrtype support according to RFC1034, + and RFC1035. + (Trac #1112, git 12d62d54d33fbb1572a1aa3089b0d547d02924aa) + 285. [bug] jelte sqlite3 data source: fixed a race condition on initial startup, when the database has not been initialized yet, and multiple From 766db4a6100e34e6a29aa9c849b60ba80b551389 Mon Sep 17 00:00:00 2001 From: Ocean Wang Date: Wed, 21 Sep 2011 16:45:02 +0800 Subject: [PATCH 753/974] [master] Add missing entries to src/lib/dns/Makefile.am to fix distcheck errors --- src/lib/dns/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 1827b707a7..3d4a663079 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -30,6 +30,8 @@ EXTRA_DIST += rdata/generic/dnskey_48.cc EXTRA_DIST += rdata/generic/dnskey_48.h EXTRA_DIST += rdata/generic/ds_43.cc EXTRA_DIST += rdata/generic/ds_43.h +EXTRA_DIST += rdata/generic/hinfo_13.cc +EXTRA_DIST += rdata/generic/hinfo_13.h EXTRA_DIST += rdata/generic/mx_15.cc EXTRA_DIST += rdata/generic/mx_15.h EXTRA_DIST += rdata/generic/naptr_35.cc From a59c7f28a458842b4edce2d6639639b17a85eb9f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 21 Sep 2011 10:53:09 +0200 Subject: [PATCH 754/974] [1177] Don't propagate name exceptions from findPreviousName They should be turned into DataSourceError instead, as they mean bad data in the DB. --- src/lib/datasrc/database.cc | 18 ++++++++++++++++-- src/lib/datasrc/tests/database_unittest.cc | 9 +++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index bb5be93412..79c966d77c 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -614,8 +614,22 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, Name DatabaseClient::Finder::findPreviousName(const Name& name) const { - return (Name(accessor_->findPreviousName(zone_id_, - name.reverse().toText()))); + const string str(accessor_->findPreviousName(zone_id_, + name.reverse().toText())); + try { + return (Name(str)); + } + /* + * To avoid having the same code many times, we just catch all the + * exceptions and handle them in a common code below + */ + catch (const isc::dns::EmptyLabel&) {} + catch (const isc::dns::TooLongLabel&) {} + catch (const isc::dns::BadLabelType&) {} + catch (const isc::dns::BadEscape&) {} + catch (const isc::dns::TooLongName&) {} + catch (const isc::dns::IncompleteName&) {} + isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName"); } Name diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 36099ec96a..886ee4d96e 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -566,6 +566,8 @@ public: return ("www.example.org."); } else if (rname == "org.example.badnsec2.") { return ("badnsec1.example.org."); + } else if (rname == "org.example.brokenname.") { + return ("brokenname...example.org."); } else if (rname == "org.example.notimplnsec." || rname == "org.example.wild.here.") { isc_throw(isc::NotImplemented, "Not implemented in this test"); @@ -2347,4 +2349,11 @@ TEST_F(MockDatabaseClientTest, missingNSEC) { DataSourceError); } +TEST_F(MockDatabaseClientTest, badName) { + shared_ptr finder(this->getFinder()); + + EXPECT_THROW(finder->findPreviousName(Name("brokenname.example.org.")), + DataSourceError); +} + } From ec2793914d1090db8c8d94a2f9b92ed97b1a6cba Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 21 Sep 2011 11:07:26 +0200 Subject: [PATCH 755/974] [1177] Log when DNSSEC not supported --- src/lib/datasrc/database.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 79c966d77c..c27443efb3 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -587,6 +587,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, catch (const isc::NotImplemented&) { // Well, they want DNSSEC, but there is no available. // So we don't provide anything. + LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED). + arg(accessor_->getDBName()).arg(name); } } // Something is not here and we didn't decide yet what From 30c277567f64d09c11cadcb173eef066efdaea07 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 21 Sep 2011 11:07:42 +0200 Subject: [PATCH 756/974] [1177] Remove FIXME comments As the concerns there seem to be false ones. --- src/lib/datasrc/datasrc_messages.mes | 5 +++++ src/lib/datasrc/tests/database_unittest.cc | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index efb88fdfe9..04ad6101f0 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/src/lib/datasrc/datasrc_messages.mes @@ -63,6 +63,11 @@ The maximum allowed number of items of the hotspot cache is set to the given number. If there are too many, some of them will be dropped. The size of 0 means no limit. +% DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED %1 doesn't support DNSSEC when asked for NSEC data covering %2 +The datasource tried to provide an NSEC proof that the named domain does not +exist, but the database backend doesn't support DNSSEC. No proof is included +in the answer as a result. + % DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3 Debug information. The database data source is looking up records with the given name and type in the database. diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 886ee4d96e..c9e777d2b1 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -1674,15 +1674,12 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) { TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) { // Same as NXDOMAIN_NSEC, but with empty non-terminal - // - // FIXME: Is the nonexistence of this node really the correct proof - // we need? shared_ptr finder(this->getFinder()); this->expected_rdatas_.push_back("empty.nonterminal.example.org. NSEC"); doFindTest(*finder, isc::dns::Name("nonterminal.example.org."), isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), this->rrttl_, - ZoneFinder::NXRRSET, // FIXME: Do we want to have specific code? + ZoneFinder::NXRRSET, this->expected_rdatas_, this->expected_sig_rdatas_, Name("l.example.org."), ZoneFinder::FIND_DNSSEC); From dd340b32df88083fdc17f682094b451f7dcdf6d6 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 21 Sep 2011 12:55:25 +0200 Subject: [PATCH 757/974] [1177] Empty nonterminal asterisk --- src/lib/datasrc/database.cc | 21 ++++++++++++++++++++- src/lib/datasrc/tests/database_unittest.cc | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index c27443efb3..3088c82874 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -536,11 +536,30 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, } else if (hasSubdomains(wildcard)) { // Empty non-terminal asterisk records_found = true; - get_cover = dnssec_data; LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_WILDCARD_EMPTY). arg(accessor_->getDBName()).arg(wildcard). arg(name); + if (dnssec_data) { + // Which one should contain the NSEC record? + const Name + coverName(findPreviousName(Name(wildcard))); + // Get the record and copy it out + found = getRRsets(coverName.toText(), nsec_types, + true); + const FoundIterator + nci(found.second.find(RRType::NSEC())); + if (nci != found.second.end()) { + result_status = WILDCARD_NXRRSET; + result_rrset = nci->second; + } else { + // The previous doesn't contain NSEC, bug? + isc_throw(DataSourceError, "No NSEC in " + + coverName.toText() + ", but it was " + "returned as previous - " + "accessor error?"); + } + } break; } } diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index c9e777d2b1..f9c81e7734 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -172,6 +172,7 @@ const char* const TEST_RECORDS[][5] = { {"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"}, {"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"}, {"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"}, + {"bao.example.org.", "NSEC", "3600", "", "wild.*.foo.*.bar.example.org. NSEC"}, {"*.cnamewild.example.org.", "CNAME", "3600", "", "www.example.org."}, {"*.nswild.example.org.", "NS", "3600", "", "ns.example.com."}, // For finding previous, this one is the last one in the zone @@ -568,6 +569,8 @@ public: return ("badnsec1.example.org."); } else if (rname == "org.example.brokenname.") { return ("brokenname...example.org."); + } else if (rname == "org.example.bar.*.") { + return ("bao.example.org."); } else if (rname == "org.example.notimplnsec." || rname == "org.example.wild.here.") { isc_throw(isc::NotImplemented, "Not implemented in this test"); @@ -1590,6 +1593,23 @@ TYPED_TEST(DatabaseClientTest, wildcard) { // DNSSEC logic handle it? } + const char* negative_dnssec_names[] = { + "a.bar.example.org.", + "foo.baz.bar.example.org.", + "a.foo.bar.example.org.", + NULL + }; + + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("wild.*.foo.*.bar.example.org. NSEC"); + this->expected_sig_rdatas_.clear(); + for (const char** name(negative_dnssec_names); *name != NULL; ++ name) { + doFindTest(*finder, isc::dns::Name(*name), this->qtype_, + RRType::NSEC(), this->rrttl_, ZoneFinder::WILDCARD_NXRRSET, + this->expected_rdatas_, this->expected_sig_rdatas_, + Name("bao.example.org."), ZoneFinder::FIND_DNSSEC); + } + // Some strange things in the wild node this->expected_rdatas_.clear(); this->expected_rdatas_.push_back("www.example.org."); From 7cc32b7915532354ed7e2fd15f7ca5a9b9b64610 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 21 Sep 2011 13:09:12 +0200 Subject: [PATCH 758/974] [1177] SQLite3Accessor NSEC cleanups --- src/lib/datasrc/sqlite3_accessor.cc | 58 ++++++++++++++--------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 2093dcbea2..ed2f84e6ac 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -71,10 +71,16 @@ const char* const text_statements[NUM_STATEMENTS] = { "AND rdtype=?3 AND rdata=?4", "SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE "WHERE zone_id = ?1 ORDER BY name, rdtype", + /* + * The ones for finding previous name. The first of them takes + * biggest smaller than something (therefore previous to the something), + * the second takes biggest (used in case when there's no previous, + * to "wrap around"). + */ "SELECT name FROM records " // FIND_PREVIOUS "WHERE zone_id=?1 AND rdtype = 'NSEC' AND " - "rname < $2 ORDER BY rname DESC LIMIT 1", // FIND_PREVIOUS_WRAP - "SELECT name FROM records " + "rname < $2 ORDER BY rname DESC LIMIT 1", + "SELECT name FROM records " // FIND_PREVIOUS_WRAP "WHERE zone_id = ?1 AND rdtype = 'NSEC' " "ORDER BY rname DESC LIMIT 1" }; @@ -403,7 +409,7 @@ namespace { // Conversion to plain char const char* -convertToPlainCharInternal(const unsigned char* ucp, sqlite3 *db) { +convertToPlainChar(const unsigned char* ucp, sqlite3 *db) { if (ucp == NULL) { // The field can really be NULL, in which case we return an // empty string, or sqlite may have run out of memory, in @@ -498,7 +504,8 @@ private: void copyColumn(std::string (&data)[COLUMN_COUNT], int column) { data[column] = convertToPlainChar(sqlite3_column_text(statement_, - column)); + column), + accessor_->dbparameters_->db_); } void bindZoneId(const int zone_id) { @@ -525,16 +532,6 @@ private: statement_ = NULL; } - // This helper method converts from the unsigned char* type (used by - // sqlite3) to char* (wanted by std::string). Technically these types - // might not be directly convertable - // In case sqlite3_column_text() returns NULL, we just make it an - // empty string, unless it was caused by a memory error - const char* convertToPlainChar(const unsigned char* ucp) { - return (convertToPlainCharInternal(ucp, - accessor_->dbparameters_->db_)); - } - const IteratorType iterator_type_; boost::shared_ptr accessor_; sqlite3_stmt *statement_; @@ -682,24 +679,24 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname) sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]); sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS]); - int rc = sqlite3_bind_int(dbparameters_->statements_[FIND_PREVIOUS], 1, - zone_id); - if (rc != SQLITE_OK) { + if (sqlite3_bind_int(dbparameters_->statements_[FIND_PREVIOUS], 1, + zone_id) != SQLITE_OK) { isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id << - " to SQL statement (find previous)"); + " to SQL statement (find previous): " << + sqlite3_errmsg(dbparameters_->db_)); } - rc = sqlite3_bind_text(dbparameters_->statements_[FIND_PREVIOUS], 2, - rname.c_str(), -1, SQLITE_STATIC); - if (rc != SQLITE_OK) { + if (sqlite3_bind_text(dbparameters_->statements_[FIND_PREVIOUS], 2, + rname.c_str(), -1, SQLITE_STATIC) != SQLITE_OK) { isc_throw(SQLite3Error, "Could not bind name " << rname << - " to SQL statement (find previous)"); + " to SQL statement (find previous): " << + sqlite3_errmsg(dbparameters_->db_)); } std::string result; - rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]); + int rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]); if (rc == SQLITE_ROW) { // We found it - result = convertToPlainCharInternal(sqlite3_column_text(dbparameters_-> + result = convertToPlainChar(sqlite3_column_text(dbparameters_-> statements_[FIND_PREVIOUS], 0), dbparameters_->db_); } sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]); @@ -710,18 +707,19 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname) sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); - int rc = sqlite3_bind_int( - dbparameters_->statements_[FIND_PREVIOUS_WRAP], 1, zone_id); - if (rc != SQLITE_OK) { + if (sqlite3_bind_int( + dbparameters_->statements_[FIND_PREVIOUS_WRAP], 1, zone_id) != + SQLITE_OK) { isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id << - " to SQL statement (find previous wrap)"); + " to SQL statement (find previous wrap): " << + sqlite3_errmsg(dbparameters_->db_)); } rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); if (rc == SQLITE_ROW) { // We found it result = - convertToPlainCharInternal(sqlite3_column_text(dbparameters_-> + convertToPlainChar(sqlite3_column_text(dbparameters_-> statements_[FIND_PREVIOUS_WRAP], 0), dbparameters_->db_); } sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); @@ -733,7 +731,7 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname) if (rc != SQLITE_ROW && rc != SQLITE_DONE) { // Some kind of error - isc_throw(SQLite3Error, "Could get data for previous name"); + isc_throw(SQLite3Error, "Could not get data for previous name"); } return (result); From a6cbb14cc9c986d109983087313225829f1c91fe Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 21 Sep 2011 13:21:09 +0200 Subject: [PATCH 759/974] [1177] More tests for SQLite3Accessor NSEC --- .../datasrc/tests/sqlite3_accessor_unittest.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index afc1638110..4bcf2528d5 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -357,16 +357,30 @@ TEST_F(SQLite3AccessorTest, findPrevious) { // A name that doesn't exist EXPECT_EQ("dns01.example.com.", accessor->findPreviousName(1, "com.example.dns01x.")); + // Largest name + EXPECT_EQ("www.example.com.", + accessor->findPreviousName(1, "com.example.wwww")); // Wrap around EXPECT_EQ("www.example.com.", accessor->findPreviousName(1, "com.example.")); + // Out of zone before and after + EXPECT_EQ("www.example.com.", + accessor->findPreviousName(1, "bb.example.")); + EXPECT_EQ("www.example.com.", + accessor->findPreviousName(1, "org.example.")); + // Case insensitive? + EXPECT_EQ("dns01.example.com.", + accessor->findPreviousName(1, "com.exaMple.DNS02.")); + // A name that doesn't exist + EXPECT_EQ("dns01.example.com.", + accessor->findPreviousName(1, "com.exaMple.DNS01X.")); } TEST_F(SQLite3AccessorTest, findPreviousNoData) { // This one doesn't hold any NSEC records, so it shouldn't work // The underlying DB/data don't support DNSSEC, so it's not implemented // (does it make sense? Or different exception here?) - EXPECT_THROW(accessor->findPreviousName(3, "com.example."), + EXPECT_THROW(accessor->findPreviousName(3, "com.example.sql2.www."), isc::NotImplemented); } From 7a061c2e82d62e2b275cb5a8d7460dce7d36f050 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 21 Sep 2011 13:27:40 +0200 Subject: [PATCH 760/974] [1177] More docs for DatabaseAccessor::findPreviousName --- src/lib/datasrc/database.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index c269e87190..709ae77832 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -487,7 +487,10 @@ public: * and the DNSSEC order correspond (eg. org.example.a is followed * by org.example.a.b which is followed by org.example.b, etc). * \param zone_id The zone to look through. - * \return The previous name. + * \return The previous name, or the last name in the zone, if there's + * no previous one (including out-of-zone cases). + * \note This function must return previous/last name even in case + * the queried rname does not exist in the zone. * * \throw DataSourceError if there's a problem with the database. * \throw NotImplemented if this database doesn't support DNSSEC. From bfa93c0ee79935bf37d379065e219ba0afb0c4e3 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 13:59:18 +0200 Subject: [PATCH 761/974] [1179] make iterator a real iterator --- src/lib/python/isc/datasrc/iterator_python.cc | 28 +++++++++++++++++-- .../python/isc/datasrc/tests/datasrc_test.py | 8 ++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index 216bd67e95..fa47b6ac07 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -83,6 +83,11 @@ ZoneIterator_destroy(s_ZoneIterator* const self) { PyObject* ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) { s_ZoneIterator* self = static_cast(po_self); + if (!self->cppobj) { + PyErr_SetString(getDataSourceException("Error"), + "get_next_rrset() called past end of iterator"); + return (NULL); + } try { isc::dns::ConstRRsetPtr rrset = self->cppobj->getNextRRset(); if (!rrset) { @@ -105,6 +110,23 @@ ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) { } } +PyObject* +ZoneIterator_iter(PyObject *self) { + return (self); +} + +PyObject* +ZoneIterator_next(PyObject* self) { + PyObject *result = ZoneIterator_getNextRRset(self, NULL); + // iter_next must return NULL without error instead of Py_None + if (result == Py_None) { + Py_DECREF(result); + return (NULL); + } else { + return (result); + } +} + PyMethodDef ZoneIterator_methods[] = { { "get_next_rrset", reinterpret_cast(ZoneIterator_getNextRRset), METH_NOARGS, @@ -144,8 +166,10 @@ PyTypeObject zoneiterator_type = { NULL, // tp_clear NULL, // tp_richcompare 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext + //reinterpret_cast(ZoneIterator_iter), // tp_iter + //reinterpret_cast(ZoneIterator_next), // tp_iternext + ZoneIterator_iter, + ZoneIterator_next, ZoneIterator_methods, // tp_methods NULL, // tp_members NULL, // tp_getset diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py index 206d202c94..9115c9f5cd 100644 --- a/src/lib/python/isc/datasrc/tests/datasrc_test.py +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -141,13 +141,9 @@ class DataSrcClient(unittest.TestCase): "m. FAKEFAKEFAKEFAKE\n", rrs.get_next_rrset().to_text()) # there are more than 80 RRs in this zone... let's just count the rest - # hmm. this makes me think we might want a real iterator returned from - # get_iterator() - rrset = rrs.get_next_rrset() count = 0 - while rrset is not None: + for rrset in rrs: count = count + 1 - rrset = rrs.get_next_rrset() self.assertEqual(40, count) # TODO should we catch this (iterating past end) and just return None # instead of failing? @@ -282,7 +278,7 @@ class DataSrcUpdater(unittest.TestCase): updater.commit() # second commit should raise exception - updater.commit() + self.assertRaises(isc.datasrc.Error, updater.commit) # the record should be gone now in the 'real' finder as well result, rrset = finder.find(isc.dns.Name("www.example.com"), From 03da93322b956e003882c09a8d4ea949f790dbc4 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 15:34:08 +0200 Subject: [PATCH 762/974] [1179] add some tests --- src/lib/python/isc/datasrc/iterator_python.cc | 7 +++---- src/lib/python/isc/datasrc/tests/datasrc_test.py | 13 +++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index fa47b6ac07..029dec78b5 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -112,6 +112,7 @@ ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) { PyObject* ZoneIterator_iter(PyObject *self) { + Py_INCREF(self); return (self); } @@ -166,10 +167,8 @@ PyTypeObject zoneiterator_type = { NULL, // tp_clear NULL, // tp_richcompare 0, // tp_weaklistoffset - //reinterpret_cast(ZoneIterator_iter), // tp_iter - //reinterpret_cast(ZoneIterator_next), // tp_iternext - ZoneIterator_iter, - ZoneIterator_next, + ZoneIterator_iter, // tp_iter + ZoneIterator_next, // tp_iternext ZoneIterator_methods, // tp_methods NULL, // tp_members NULL, // tp_getset diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py index 9115c9f5cd..0f40927010 100644 --- a/src/lib/python/isc/datasrc/tests/datasrc_test.py +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -30,6 +30,10 @@ NEW_DB_FILE = TESTDATA_WRITE_PATH + "new_db.sqlite3" class DataSrcClient(unittest.TestCase): + def test_construct(self): + # can't construct directly + self.assertRaises(TypeError, isc.datasrc.ZoneIterator) + def test_iterate(self): dsc = isc.datasrc.DataSourceClient(READ_ZONE_DB_FILE) @@ -151,6 +155,10 @@ class DataSrcClient(unittest.TestCase): self.assertRaises(TypeError, dsc.get_iterator, "asdf") + def test_construct(self): + # can't construct directly + self.assertRaises(TypeError, isc.datasrc.ZoneFinder) + def test_find(self): dsc = isc.datasrc.DataSourceClient(READ_ZONE_DB_FILE) @@ -230,7 +238,12 @@ class DataSrcUpdater(unittest.TestCase): # Make a fresh copy of the writable database with all original content shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE) + def test_construct(self): + # can't construct directly + self.assertRaises(TypeError, isc.datasrc.ZoneUpdater) + def test_update_delete_commit(self): + dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE) # first make sure, through a separate finder, that some record exists From d9dd4c5a7438c152f6c9ae2bcc4c9f5ee598728b Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 15:45:17 +0200 Subject: [PATCH 763/974] [1179] direct count --- src/lib/python/isc/datasrc/tests/datasrc_test.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py index 0f40927010..57f56b937a 100644 --- a/src/lib/python/isc/datasrc/tests/datasrc_test.py +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -146,9 +146,7 @@ class DataSrcClient(unittest.TestCase): rrs.get_next_rrset().to_text()) # there are more than 80 RRs in this zone... let's just count the rest count = 0 - for rrset in rrs: - count = count + 1 - self.assertEqual(40, count) + self.assertEqual(40, len(list(rrs))) # TODO should we catch this (iterating past end) and just return None # instead of failing? self.assertRaises(isc.datasrc.Error, rrs.get_next_rrset) From 0aa89cf84c78a9ee8b97a51c17b3982324021f81 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 15:51:04 +0200 Subject: [PATCH 764/974] [1179] doc update --- src/lib/python/isc/datasrc/client_inc.cc | 2 +- src/lib/python/isc/datasrc/datasrc.cc | 2 +- src/lib/python/isc/datasrc/iterator_inc.cc | 3 +- src/lib/python/isc/datasrc/updater_inc.cc | 40 +++++----------------- 4 files changed, 13 insertions(+), 34 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_inc.cc b/src/lib/python/isc/datasrc/client_inc.cc index a126cf3757..e5cd1c809c 100644 --- a/src/lib/python/isc/datasrc/client_inc.cc +++ b/src/lib/python/isc/datasrc/client_inc.cc @@ -157,7 +157,7 @@ specific derived class implementation).\n\ If replace is true, any existing RRs of the zone will be deleted on\n\ successful completion of updates (after commit() on the updater); if\n\ it's false, the existing RRs will be intact unless explicitly deleted\n\ -by delete_r_rset() on the updater.\n\ +by delete_rrset() on the updater.\n\ \n\ A data source can be \"read only\" or can prohibit partial updates. In\n\ such cases this method will result in an isc.NotImplemented exception\n\ diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc index dd1011f73a..f0e65d27a2 100644 --- a/src/lib/python/isc/datasrc/datasrc.cc +++ b/src/lib/python/isc/datasrc/datasrc.cc @@ -172,7 +172,7 @@ PyModuleDef iscDataSrc = { "These bindings are close match to the C++ API, but they are not complete " "(some parts are not needed) and some are done in more python-like ways.", -1, - NULL,// TODO do we need module-level functions? + NULL, NULL, NULL, NULL, diff --git a/src/lib/python/isc/datasrc/iterator_inc.cc b/src/lib/python/isc/datasrc/iterator_inc.cc index 944fd1795a..3ef1caf343 100644 --- a/src/lib/python/isc/datasrc/iterator_inc.cc +++ b/src/lib/python/isc/datasrc/iterator_inc.cc @@ -10,10 +10,11 @@ used. This is the abstract interface.\n\ \n\ There's no way to start iterating from the beginning again or return.\n\ \n\ +The ZoneIterator is a python iterator, and can be iterated over directly.\n\ "; const char* const ZoneIterator_getNextRRset_doc = "\ -get_next_r_rset() -> isc.dns.ConstRRset\n\ +get_next_rrset() -> isc.dns.ConstRRset\n\ \n\ Get next RRset from the zone.\n\ \n\ diff --git a/src/lib/python/isc/datasrc/updater_inc.cc b/src/lib/python/isc/datasrc/updater_inc.cc index 8903915226..851aaa20fb 100644 --- a/src/lib/python/isc/datasrc/updater_inc.cc +++ b/src/lib/python/isc/datasrc/updater_inc.cc @@ -11,11 +11,11 @@ identify the zone to be updated). The underlying realization of a\n\ a general purpose database as a backend, it will involve performing\n\ some form of \"begin transaction\" statement for the database.\n\ \n\ -Updates (adding or deleting RRs) are made via add_r_rset() and\n\ -delete_r_rset() methods. Until the commit() method is called the\n\ +Updates (adding or deleting RRs) are made via add_rrset() and\n\ +delete_rrset() methods. Until the commit() method is called the\n\ changes are local to the updater object. For example, they won't be\n\ visible via a ZoneFinder object except the one returned by the\n\ -updater's own get_finder() method. The commit() completes the\n\ +updater's own find() method. The commit() completes the\n\ transaction and makes the changes visible to others.\n\ \n\ This class does not provide an explicit \"rollback\" interface. If\n\ @@ -30,38 +30,16 @@ adding and deleting RRs (see the description of the related methods).\n\ It may be revisited as we gain more experiences.\n\ \n\ "; -/* -const char* const ZoneUpdater_getFinder_doc = "\ -get_finder() -> ZoneFinder \n\ -\n\ -Return a finder for the zone being updated.\n\ -\n\ -The returned finder provides the functionalities of ZoneFinder for the\n\ -zone as updates are made via the updater. That is, before making any\n\ -update, the finder will be able to find all RRsets that exist in the\n\ -zone at the time the updater is created. If RRsets are added or\n\ -deleted via add_r_rset() or delete_r_rset(), this finder will find the\n\ -added ones or miss the deleted ones respectively.\n\ -\n\ -The finder returned by this method is effective only while the updates\n\ -are performed, i.e., from the construction of the corresponding\n\ -updater until commit() is performed or the updater is destructed\n\ -without commit. The result of a subsequent call to this method (or the\n\ -use of the result) after that is undefined.\n\ -\n\ -A reference to a ZoneFinder for the updated zone\n\ -\n\ -"; -*/ + const char* const ZoneUpdater_addRRset_doc = "\ -add_r_rset(rrset) -> void\n\ +add_rrset(rrset) -> void\n\ \n\ Add an RRset to a zone via the updater.\n\ \n\ - Whether the RR class is identical to that for the zone to be updated\n\ - Whether the RRset is not empty, i.e., it has at least one RDATA\n\ - Whether the RRset is not associated with an RRSIG, i.e., whether\n\ - get_r_rsig() on the RRset returns a NULL pointer.\n\ + get_rrsig() on the RRset returns a NULL pointer.\n\ \n\ and otherwise does not check any oddity. For example, it doesn't check\n\ whether the owner name of the specified RRset is a subdomain of the\n\ @@ -123,11 +101,11 @@ Parameters:\n\ "; const char* const ZoneUpdater_deleteRRset_doc = "\ -delete_r_rset(rrset) -> void\n\ +delete_rrset(rrset) -> void\n\ \n\ Delete an RRset from a zone via the updater.\n\ \n\ -Like add_r_rset(), the detailed semantics and behavior of this method\n\ +Like add_rrset(), the detailed semantics and behavior of this method\n\ may have to be revisited in a future version. The following are based\n\ on the initial implementation decisions.\n\ \n\ @@ -149,7 +127,7 @@ when deleting RRs.\n\ - Whether the RR class is identical to that for the zone to be updated\n\ - Whether the RRset is not empty, i.e., it has at least one RDATA\n\ - Whether the RRset is not associated with an RRSIG, i.e., whether\n\ - get_r_rsig() on the RRset returns a NULL pointer.\n\ + get_rrsig() on the RRset returns a NULL pointer.\n\ \n\ This method must not be called once commit() is performed. If it calls\n\ after commit() the implementation must throw a DataSourceError\n\ From 2c5b2fc19c21dd12747eb960baee65759847a118 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 15:52:04 +0200 Subject: [PATCH 765/974] [1179] another doc update --- src/lib/python/isc/datasrc/client_inc.cc | 4 ++-- src/lib/python/isc/datasrc/updater_inc.cc | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_inc.cc b/src/lib/python/isc/datasrc/client_inc.cc index e5cd1c809c..3b72308272 100644 --- a/src/lib/python/isc/datasrc/client_inc.cc +++ b/src/lib/python/isc/datasrc/client_inc.cc @@ -102,7 +102,7 @@ Returns an iterator to the given zone.\n\ This allows for traversing the whole zone. The returned object can\n\ provide the RRsets one by one.\n\ \n\ -This throws DataSourceError when the zone does not exist in the\n\ +This throws isc.datasrc.Error when the zone does not exist in the\n\ datasource.\n\ \n\ The default implementation throws isc.NotImplemented. This allows for\n\ @@ -165,7 +165,7 @@ unconditionally or when replace is false).\n\ \n\ Exceptions:\n\ NotImplemented The underlying data source does not support updates.\n\ - DataSourceError Internal error in the underlying data source.\n\ + isc.datasrc.Error Internal error in the underlying data source.\n\ std.bad_alloc Resource allocation failure.\n\ \n\ Parameters:\n\ diff --git a/src/lib/python/isc/datasrc/updater_inc.cc b/src/lib/python/isc/datasrc/updater_inc.cc index 851aaa20fb..dc313d2810 100644 --- a/src/lib/python/isc/datasrc/updater_inc.cc +++ b/src/lib/python/isc/datasrc/updater_inc.cc @@ -73,7 +73,7 @@ ZoneFinder.find() conforms to the concept of \"merge\", the actual\n\ internal representation is up to the implementation.\n\ \n\ This method must not be called once commit() is performed. If it calls\n\ -after commit() the implementation must throw a DataSourceError\n\ +after commit() the implementation must throw a isc.datasrc.Error\n\ exception.\n\ \n\ TodoAs noted above we may have to revisit the design details as we\n\ @@ -91,7 +91,7 @@ gain experiences:\n\ information on whether there's a duplicate, etc.\n\ \n\ Exceptions:\n\ - DataSourceError Called after commit(), RRset is invalid (see above),\n\ + isc.datasrc.Error Called after commit(), RRset is invalid (see above),\n\ internal data source error\n\ std.bad_alloc Resource allocation failure\n\ \n\ @@ -130,7 +130,7 @@ when deleting RRs.\n\ get_rrsig() on the RRset returns a NULL pointer.\n\ \n\ This method must not be called once commit() is performed. If it calls\n\ -after commit() the implementation must throw a DataSourceError\n\ +after commit() the implementation must throw a isc.datasrc.Error\n\ exception.\n\ \n\ TodoAs noted above we may have to revisit the design details as we\n\ @@ -149,7 +149,7 @@ gain experiences:\n\ exit, the number of actually deleted RRs, etc.\n\ \n\ Exceptions:\n\ - DataSourceError Called after commit(), RRset is invalid (see above),\n\ + isc.datasrc.Error Called after commit(), RRset is invalid (see above),\n\ internal data source error\n\ std.bad_alloc Resource allocation failure\n\ \n\ @@ -171,10 +171,10 @@ with a general purpose database as a backend, for example, this method\n\ would perform a \"commit\" statement for the database.\n\ \n\ This operation can only be performed at most once. A duplicate call\n\ -must result in a DatasourceError exception.\n\ +must result in a isc.datasrc.Error exception.\n\ \n\ Exceptions:\n\ - DataSourceError Duplicate call of the method, internal data source\n\ + isc.datasrc.Error Duplicate call of the method, internal data source\n\ error\n\ \n\ "; From 9b4326dc093b71bcd77a527111ea6778795bf068 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 15:57:55 +0200 Subject: [PATCH 766/974] [1179] removed some unused includes --- src/lib/python/isc/datasrc/client_python.cc | 3 --- src/lib/python/isc/datasrc/finder_python.cc | 3 --- src/lib/python/isc/datasrc/iterator_python.cc | 3 --- src/lib/python/isc/datasrc/updater_python.cc | 3 --- 4 files changed, 12 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index c53a846702..e6f18725b5 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -20,9 +20,6 @@ // http://docs.python.org/py3k/extending/extending.html#a-simple-example #include -#include -#include - #include #include diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index e7be8f8354..b1f97119d8 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -20,9 +20,6 @@ // http://docs.python.org/py3k/extending/extending.html#a-simple-example #include -#include -#include - #include #include diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index 029dec78b5..1797bb4ffe 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -20,9 +20,6 @@ // http://docs.python.org/py3k/extending/extending.html#a-simple-example #include -#include -#include - #include #include diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index c45dad4fc0..5c1cd44eac 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -20,9 +20,6 @@ // http://docs.python.org/py3k/extending/extending.html#a-simple-example #include -#include -#include - #include #include From b6709a7001e4812c4ed774ef0ff3111fb654d199 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 22 Sep 2011 09:50:11 -0700 Subject: [PATCH 767/974] [1101] added a helper shell script to generate dummy python scripts. --- src/lib/python/isc/log_messages/gen-forwarder.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 src/lib/python/isc/log_messages/gen-forwarder.sh diff --git a/src/lib/python/isc/log_messages/gen-forwarder.sh b/src/lib/python/isc/log_messages/gen-forwarder.sh new file mode 100755 index 0000000000..84c2450159 --- /dev/null +++ b/src/lib/python/isc/log_messages/gen-forwarder.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +MODULE_NAME=$1 +if test -z $MODULE_NAME; then + echo 'Usage: gen-forwarder.sh module_name' + exit 1 +fi + +echo "from work.${MODULE_NAME}_messages import *" > ${MODULE_NAME}_messages.py +echo "Forwarder python script is generated. Make sure to perform:" +echo "git add ${MODULE_NAME}_messages.py" +echo "and add the following to Makefile.am:" +echo "EXTRA_DIST += ${MODULE_NAME}_messages.py" +echo "CLEANFILES += ${MODULE_NAME}_messages.pyc" From bd0c874dda60a0f5e235b653e1bb63716cb385f8 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 22 Sep 2011 09:51:11 -0700 Subject: [PATCH 768/974] [1101] cleanup: remove unnecessary variable from configure.ac --- configure.ac | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 300c48ddfa..ee5d79bcb7 100644 --- a/configure.ac +++ b/configure.ac @@ -155,12 +155,10 @@ fi PYTHON_SITEPKG_DIR=${pyexecdir} AC_SUBST(PYTHON_SITEPKG_DIR) -# These will be commonly used in various Makefile.am's that need to generate +# This will be commonly used in various Makefile.am's that need to generate # python log messages. PYTHON_LOGMSGPKG_DIR="\$(top_builddir)/src/lib/python/isc/log_messages" AC_SUBST(PYTHON_LOGMSGPKG_DIR) -PYTHON_LOGMSGPKG_SRCDIR="\$(top_srcdir)/src/lib/python/isc/log_messages" -AC_SUBST(PYTHON_LOGMSGPKG_SRCDIR) # This is python package paths commonly used in python tests. See # README of log_messages for why it's included. From 68cf1ccf20ecfcc1e06de69fcd50d13cf8b5e1e0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 22 Sep 2011 09:52:56 -0700 Subject: [PATCH 769/974] [1101] updated README so that it matches the latest implementation. --- src/lib/python/isc/log_messages/README | 43 ++++++++++---------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/lib/python/isc/log_messages/README b/src/lib/python/isc/log_messages/README index ebb33370cb..c96f78c730 100644 --- a/src/lib/python/isc/log_messages/README +++ b/src/lib/python/isc/log_messages/README @@ -24,36 +24,34 @@ which ensures the right directory is chosen. A python module or program that defines its own log messages needs to make sure that the setup described above is implemented. It's a complicated process, but can generally be done by following a common -pattern. The following are a sample snippet for Makefile.in for a -module named "mymodule" (which is supposed to be generated from a file -"mymodule_messages.mes"). In many cases it should work simply by -replacing 'mymodule' with the actual module name. +pattern: + +1. Create the dummy script (see above) for the module and update + Makefile.am in this directory accordingly. See (and use) + a helper shell script named gen-forwarder.sh. +2. Update Makefil.am of the module that defines the log message. The + following are a sample snippet for Makefile.am for a module named + "mymodule" (which is supposed to be generated from a file + "mymodule_messages.mes"). In many cases it should work simply by + replacing 'mymodule' with the actual module name. ==================== begin Makefile.am additions =================== -nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.py pylogmessagedir = $(pyexecdir)/isc/log_messages/ -CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.pyc +CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.pyc EXTRA_DIST = mymodule_messages.mes -BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py -CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.pyc -EXTRA_DIST += $(PYTHON_LOGMSGPKG_SRCDIR)/mymodule_messages.py - -$(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py : mymodule_messages.mes +$(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.py : mymodule_messages.mes $(top_builddir)/src/lib/log/compiler/message \ - -d $(PYTHON_LOGMSGPKG_DIR) -p $(srcdir)/mymodule_messages.mes - -$(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py: Makefile - echo "from work.mymodule_messages import *" > $@ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/mymodule_messages.mes # This rule ensures mymodule_messages.py is (re)generated as a result of # 'make'. If there's no other appropriate target, specify # mymodule_messages.py in BUILT_SOURCES. -mymodule: $(PYTHON_LOGMSGPKG_DIR)/mymodule_messages.py +mymodule: $(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.py ===================== end Makefile.am additions ==================== Notes: @@ -67,11 +65,4 @@ Notes: used for other scripts in the same Makefile.am file. - $(PYTHON_LOGMSGPKG_DIR) should be set to point to this directory (or the corresponding build directory if it's different) by the - configure script. $(PYTHON_LOGMSGPKG_SRCDIR) should be set to point - to this directory by the configure script. -- The four lines starting from BUILT_SOURCES and the second make rule - are for preparing the dummy python file under the source tree. - Note that EXTRA_DIST is specified so that the python script is - placed in the source tree (not only in the build tree when these - two are different). If you don't like this trick, you could - directly add the file in this directory alternatively. + configure script. From 842fc917163f0b8cb2a703a4c7fe078d944932e8 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 22 Sep 2011 14:17:55 -0700 Subject: [PATCH 770/974] [master] changelog entry for #1101 --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 6f84a0764c..d0565e18c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +287. [bug]* jinmei + Python script files for log messages (xxx_messages.py) should have + been installed under the "isc" package. This fix itself should + be a transparent change without affecting existing configurations + or other operational practices, but you may want to clean up the + python files from the common directly (such as "site-packages"). + (Trac #1101, git 0eb576518f81c3758c7dbaa2522bd8302b1836b3) + 286. [func] ocean libdns++: Implement the HINFO rrtype support according to RFC1034, and RFC1035. From 4c86025464db4603ec07490169aaf4b77868057b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 22 Sep 2011 23:10:00 -0700 Subject: [PATCH 771/974] [1177] fixed a few minor typo directly. --- src/lib/datasrc/database.cc | 2 +- src/lib/datasrc/database.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 3088c82874..50b2a4d7f9 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -308,7 +308,7 @@ namespace { // convenient. // // While this is not straightforward use of the + operator, some mathematical -// conventions do allow summing sets with elements (usually in communitative +// conventions do allow summing sets with elements (usually in commutative // way, we define only one order of the operator parameters, as the other // isn't used). // diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 709ae77832..1284f57db5 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -659,7 +659,7 @@ public: * \param name Which domain name should be scanned. * \param types List of types the caller is interested in. * \param check_ns If this is set to true, it checks nothing lives - * together with NS record (with few little exceptions, like RRSET + * together with NS record (with few little exceptions, like RRSIG * or NSEC). This check is meant for non-apex NS records. * \param construct_name If this is NULL, the resulting RRsets have * their name set to name. If it is not NULL, it overrides the name From c89f3a2f43fd7fe70bcb199fad0ccf94364b1ebe Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 23 Sep 2011 11:14:20 +0200 Subject: [PATCH 772/974] [1177] Comment about ignoring NSEC3 in check --- src/lib/datasrc/database.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 50b2a4d7f9..74d2d88f15 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -253,6 +253,9 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types, // NSEC and RRSIG can coexist with anything, otherwise // we've seen something that can't live together with potential // CNAME or NS + // + // NSEC3 lives in separate namespace from everything, therefore + // we just ignore it here for these checks as well. seen_other = true; } } catch (const InvalidRRType&) { From ff1bd2a00278bc753a7d035fd5020ff936df1882 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 23 Sep 2011 11:15:39 +0200 Subject: [PATCH 773/974] [1177] Don't overload operator+ It seems I have a strange sense of what is intuitive, not everybody finds it as intuitive as me O:-). --- src/lib/datasrc/database.cc | 81 +++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 74d2d88f15..f70b9fc30e 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -306,20 +306,47 @@ DatabaseClient::Finder::hasSubdomains(const std::string& name) { // Some manipulation with RRType sets namespace { -// To conveniently put the RRTypes into the sets. This is not really clean -// design, but it is hidden inside this file and makes the calls much more -// convenient. -// -// While this is not straightforward use of the + operator, some mathematical -// conventions do allow summing sets with elements (usually in commutative -// way, we define only one order of the operator parameters, as the other -// isn't used). -// -// This arguably produces more readable code than having bunch of proxy -// functions and set.insert calls scattered through the code. -std::set operator +(std::set set, const RRType& type) { - set.insert(type); - return (set); +// Bunch of functions to construct specific sets of RRTypes we will +// ask from it. +typedef std::set WantedTypes; + +const WantedTypes& +NSEC_TYPES() { + static bool initialized(false); + static WantedTypes result; + + if (!initialized) { + result.insert(RRType::NSEC()); + initialized = true; + } + return (result); +} + +const WantedTypes& +DELEGATION_TYPES() { + static bool initialized(false); + static WantedTypes result; + + if (!initialized) { + result.insert(RRType::DNAME()); + result.insert(RRType::NS()); + initialized = true; + } + return (result); +} + +const WantedTypes& +FINAL_TYPES() { + static bool initialized(false); + static WantedTypes result; + + if (!initialized) { + result.insert(RRType::CNAME()); + result.insert(RRType::NS()); + result.insert(RRType::NSEC()); + initialized = true; + } + return (result); } } @@ -344,8 +371,6 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // In case we are in GLUE_OK mode and start matching wildcards, // we can't do it under NS, so we store it here to check isc::dns::RRsetPtr first_ns; - // This is used at multiple places - static const WantedTypes nsec_types(WantedTypes() + RRType::NSEC()); // First, do we have any kind of delegation (NS/DNAME) here? const Name origin(getOrigin()); @@ -363,10 +388,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, for (int i(remove_labels); i > 0; --i) { Name superdomain(name.split(i)); // Look if there's NS or DNAME (but ignore the NS in origin) - static const WantedTypes delegation_types(WantedTypes() + - RRType::DNAME() + - RRType::NS()); - found = getRRsets(superdomain.toText(), delegation_types, + found = getRRsets(superdomain.toText(), DELEGATION_TYPES(), i != remove_labels); if (found.first) { // It contains some RRs, so it exists. @@ -412,9 +434,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // It is special if there's a CNAME or NS, DNAME is ignored here // And we don't consider the NS in origin - static const WantedTypes final_types(WantedTypes() + RRType::CNAME() + - RRType::NS() + RRType::NSEC()); - found = getRRsets(name.toText(), final_types + type, name != origin); + WantedTypes final_types(FINAL_TYPES()); + final_types.insert(type); + found = getRRsets(name.toText(), final_types, name != origin); records_found = found.first; // NS records, CNAME record and Wanted Type records @@ -463,11 +485,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, const string wildcard("*." + superdomain.toText()); const string construct_name(name.toText()); // TODO What do we do about DNAME here? - static const WantedTypes wildcard_types(WantedTypes() + - RRType::CNAME() + - RRType::NS() + - RRType::NSEC()); - found = getRRsets(wildcard, wildcard_types + type, true, + // The types are the same as with original query + found = getRRsets(wildcard, final_types, true, &construct_name); if (found.first) { if (first_ns) { @@ -517,7 +536,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // However, we need to get the RRset in the // name of the wildcard, not the constructed // one, so we walk it again - found = getRRsets(wildcard, nsec_types, + found = getRRsets(wildcard, NSEC_TYPES(), true); result_rrset = found.second.find(RRType::NSEC())-> @@ -548,7 +567,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, const Name coverName(findPreviousName(Name(wildcard))); // Get the record and copy it out - found = getRRsets(coverName.toText(), nsec_types, + found = getRRsets(coverName.toText(), NSEC_TYPES(), true); const FoundIterator nci(found.second.find(RRType::NSEC())); @@ -592,7 +611,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // Which one should contain the NSEC record? const Name coverName(findPreviousName(name)); // Get the record and copy it out - found = getRRsets(coverName.toText(), nsec_types, true); + found = getRRsets(coverName.toText(), NSEC_TYPES(), true); const FoundIterator nci(found.second.find(RRType::NSEC())); if (nci != found.second.end()) { From 09349cf206ee9e68618713b97e621b7ef2a6c0a9 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 23 Sep 2011 11:35:12 +0200 Subject: [PATCH 774/974] [1177] Don't wrap around in previous --- src/lib/datasrc/database.h | 13 ++++-- src/lib/datasrc/sqlite3_accessor.cc | 44 ++++--------------- src/lib/datasrc/tests/database_unittest.cc | 9 +--- .../tests/sqlite3_accessor_unittest.cc | 7 +-- src/lib/datasrc/zone.h | 5 ++- 5 files changed, 24 insertions(+), 54 deletions(-) diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 1284f57db5..5e66f336fa 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -487,13 +487,18 @@ public: * and the DNSSEC order correspond (eg. org.example.a is followed * by org.example.a.b which is followed by org.example.b, etc). * \param zone_id The zone to look through. - * \return The previous name, or the last name in the zone, if there's - * no previous one (including out-of-zone cases). - * \note This function must return previous/last name even in case + * \return The previous name. + * \note This function must return previous name even in case * the queried rname does not exist in the zone. + * \note This method must skip under-the-zone-cut data (glue data). + * This might be implemented by looking for NSEC records (as glue + * data don't have them) in the zone or in some other way. * * \throw DataSourceError if there's a problem with the database. - * \throw NotImplemented if this database doesn't support DNSSEC. + * \throw NotImplemented if this database doesn't support DNSSEC + * or there's no previous name for the queried one (the NSECs + * might be missing or the queried name is less or equal the + * apex of the zone). */ virtual std::string findPreviousName(int zone_id, const std::string& rname) const = 0; diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index ed2f84e6ac..22e1f30e18 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -48,8 +48,7 @@ enum StatementID { DEL_RECORD = 8, ITERATE = 9, FIND_PREVIOUS = 10, - FIND_PREVIOUS_WRAP = 11, - NUM_STATEMENTS = 12 + NUM_STATEMENTS = 11 }; const char* const text_statements[NUM_STATEMENTS] = { @@ -72,17 +71,13 @@ const char* const text_statements[NUM_STATEMENTS] = { "SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE "WHERE zone_id = ?1 ORDER BY name, rdtype", /* - * The ones for finding previous name. The first of them takes - * biggest smaller than something (therefore previous to the something), - * the second takes biggest (used in case when there's no previous, - * to "wrap around"). + * This one looks for previous name with NSEC record. It is done by + * using the reversed name. The NSEC is checked because we need to + * skip glue data, which don't have the NSEC. */ "SELECT name FROM records " // FIND_PREVIOUS "WHERE zone_id=?1 AND rdtype = 'NSEC' AND " - "rname < $2 ORDER BY rname DESC LIMIT 1", - "SELECT name FROM records " // FIND_PREVIOUS_WRAP - "WHERE zone_id = ?1 AND rdtype = 'NSEC' " - "ORDER BY rname DESC LIMIT 1" + "rname < $2 ORDER BY rname DESC LIMIT 1" }; struct SQLite3Parameters { @@ -702,31 +697,10 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname) sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]); if (rc == SQLITE_DONE) { - // Nothing previous, wrap around (is it needed for anything? - // Well, just for completeness) - sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); - sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); - - if (sqlite3_bind_int( - dbparameters_->statements_[FIND_PREVIOUS_WRAP], 1, zone_id) != - SQLITE_OK) { - isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id << - " to SQL statement (find previous wrap): " << - sqlite3_errmsg(dbparameters_->db_)); - } - - rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); - if (rc == SQLITE_ROW) { - // We found it - result = - convertToPlainChar(sqlite3_column_text(dbparameters_-> - statements_[FIND_PREVIOUS_WRAP], 0), dbparameters_->db_); - } - sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]); - if (rc == SQLITE_DONE) { - // No NSEC records, this DB doesn't support DNSSEC - isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC"); - } + // No NSEC records here, this DB doesn't support DNSSEC or + // we asked before the apex + isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC or " + "query before apex"); } if (rc != SQLITE_ROW && rc != SQLITE_DONE) { diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f9c81e7734..69151cecb0 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -175,8 +175,6 @@ const char* const TEST_RECORDS[][5] = { {"bao.example.org.", "NSEC", "3600", "", "wild.*.foo.*.bar.example.org. NSEC"}, {"*.cnamewild.example.org.", "CNAME", "3600", "", "www.example.org."}, {"*.nswild.example.org.", "NS", "3600", "", "ns.example.com."}, - // For finding previous, this one is the last one in the zone - {"zzz.example.org.", "NSEC", "3600", "", "example.org NSEC"}, // For NSEC empty non-terminal {"l.example.org.", "NSEC", "3600", "", "empty.nonterminal.example.org. NSEC"}, {"empty.nonterminal.example.org.", "A", "3600", "", "192.0.2.1"}, @@ -558,9 +556,7 @@ public: if (id == -1) { isc_throw(isc::NotImplemented, "Test not implemented behaviour"); } else if (id == 42) { - if (rname == "org.example.") { - return ("zzz.example.org."); - } else if (rname == "org.example.nonterminal.") { + if (rname == "org.example.nonterminal.") { return ("l.example.org."); } else if (rname == "org.example.www2." || rname == "org.example.www1.") { @@ -2331,9 +2327,6 @@ TYPED_TEST(DatabaseClientTest, previous) { EXPECT_EQ(Name("www.example.org."), finder->findPreviousName(Name("www2.example.org."))); - // Check wrap around - EXPECT_EQ(Name("zzz.example.org."), - finder->findPreviousName(Name("example.org."))); // Check a name that doesn't exist there EXPECT_EQ(Name("www.example.org."), finder->findPreviousName(Name("www1.example.org."))); diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 4bcf2528d5..9c084dd699 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -360,12 +360,7 @@ TEST_F(SQLite3AccessorTest, findPrevious) { // Largest name EXPECT_EQ("www.example.com.", accessor->findPreviousName(1, "com.example.wwww")); - // Wrap around - EXPECT_EQ("www.example.com.", - accessor->findPreviousName(1, "com.example.")); - // Out of zone before and after - EXPECT_EQ("www.example.com.", - accessor->findPreviousName(1, "bb.example.")); + // Out of zone after the last name EXPECT_EQ("www.example.com.", accessor->findPreviousName(1, "org.example.")); // Case insensitive? diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 794e46dc7c..89e4003b6c 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -219,12 +219,15 @@ public: /// however it is recommended to stick to the ones listed here. The user /// of this method should be able to handle any exceptions. /// + /// This method does not include under-zone-cut data (glue data). + /// /// \param query The name for which one we look for a previous one. The /// queried name doesn't have to exist in the zone. /// \return The preceding name /// /// \throw NotImplemented in case the data source backend doesn't support - /// DNSSEC. + /// DNSSEC or there is no previous in the zone (NSEC records might be + /// missing in the DB, the queried name is less or equal to the apex). /// \throw DataSourceError for low-level or internal datasource errors /// (like broken connection to database, wrong data living there). /// \throw std::bad_alloc For allocation errors. From b902e70583a9dfb1ee410e297e2da4c8b944ba8d Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 23 Sep 2011 11:48:43 +0200 Subject: [PATCH 775/974] [1177] Test for skipping glue data --- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 9c084dd699..87708c7bcd 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -369,6 +369,12 @@ TEST_F(SQLite3AccessorTest, findPrevious) { // A name that doesn't exist EXPECT_EQ("dns01.example.com.", accessor->findPreviousName(1, "com.exaMple.DNS01X.")); + // The DB contains foo.bar.example.com., which would be in between + // these two names. However, that one does not have an NSEC record, + // which is how this database recognizes glue data, so it should + // be skipped. + EXPECT_EQ("example.com.", + accessor->findPreviousName(1, "com.example.cname-ext.")); } TEST_F(SQLite3AccessorTest, findPreviousNoData) { From 06a24c688282b61dd2ce5b6c00608bee34ae3563 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 23 Sep 2011 12:16:36 +0200 Subject: [PATCH 776/974] [1177] Don't assume findPreviousName contains NSEC So don't say it's necessary bug, it might be badly signed zone as well. --- src/lib/datasrc/database.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index f70b9fc30e..591099cfed 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -575,11 +575,12 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_status = WILDCARD_NXRRSET; result_rrset = nci->second; } else { - // The previous doesn't contain NSEC, bug? + // The previous doesn't contain NSEC. + // Badly signed zone or a bug? isc_throw(DataSourceError, "No NSEC in " + coverName.toText() + ", but it was " "returned as previous - " - "accessor error?"); + "accessor error? Badly signed zone?"); } } break; @@ -618,11 +619,12 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, result_status = NXDOMAIN; result_rrset = nci->second; } else { - // The previous doesn't contain NSEC, bug? + // The previous doesn't contain NSEC. + // Badly signed zone or a bug? isc_throw(DataSourceError, "No NSEC in " + coverName.toText() + ", but it was " "returned as previous - " - "accessor error?"); + "accessor error? Badly signed zone?"); } } catch (const isc::NotImplemented&) { From d49e3c5e79e00b59e518c4bc1f71882adf721696 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 15:57:32 +0200 Subject: [PATCH 777/974] [1179] different way to test all output from get_iterator() --- .../python/isc/datasrc/tests/datasrc_test.py | 224 ++++++++++-------- 1 file changed, 121 insertions(+), 103 deletions(-) diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py index 57f56b937a..15ceb805e7 100644 --- a/src/lib/python/isc/datasrc/tests/datasrc_test.py +++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py @@ -28,125 +28,143 @@ BROKEN_DB_FILE = TESTDATA_PATH + "brokendb.sqlite3" WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "rwtest.sqlite3.copied" NEW_DB_FILE = TESTDATA_WRITE_PATH + "new_db.sqlite3" +def add_rrset(rrset_list, name, rrclass, rrtype, ttl, rdatas): + rrset_to_add = isc.dns.RRset(name, rrclass, rrtype, ttl) + if rdatas is not None: + for rdata in rdatas: + rrset_to_add.add_rdata(isc.dns.Rdata(rrtype, rrclass, rdata)) + rrset_list.append(rrset_to_add) + +# helper function, we have no direct rrset comparison atm +def rrsets_equal(a, b): + # no accessor for sigs either (so this only checks name, class, type, ttl, + # and rdata) + # also, because of the fake data in rrsigs, if the type is rrsig, the + # rdata is not checked + return a.get_name() == b.get_name() and\ + a.get_class() == b.get_class() and\ + a.get_type() == b.get_type() and \ + a.get_ttl() == b.get_ttl() and\ + (a.get_type() == isc.dns.RRType.RRSIG() or + sorted(a.get_rdata()) == sorted(b.get_rdata())) + +# returns true if rrset is in expected_rrsets +# will remove the rrset from expected_rrsets if found +def check_for_rrset(expected_rrsets, rrset): + for cur_rrset in expected_rrsets[:]: + if rrsets_equal(cur_rrset, rrset): + expected_rrsets.remove(cur_rrset) + return True + return False + class DataSrcClient(unittest.TestCase): def test_construct(self): # can't construct directly self.assertRaises(TypeError, isc.datasrc.ZoneIterator) + def test_iterate(self): dsc = isc.datasrc.DataSourceClient(READ_ZONE_DB_FILE) # for RRSIGS, the TTL's are currently modified. This test should # start failing when we fix that. rrs = dsc.get_iterator(isc.dns.Name("sql1.example.com.")) - self.assertEqual("sql1.example.com. 3600 IN DNSKEY 256 3 5 AwEAAdYdRh" + - "BAEY67R/8G1N5AjGF6asIiNh/pNGeQ8xDQP13JN2lo+sNqWcmpY" + - "NhuVqRbLB+mamsU1XcCICSBvAlSmfz/ZUdafX23knArTlALxMms" + - "pcfdpqun3Yr3YYnztuj06rV7RqmveYckWvAUXVYMSMQZfJ305fs" + - "0dE/xLztL/CzZ\nsql1.example.com. 3600 IN DNSKEY 257" + - " 3 5 AwEAAbaKDSa9XEFTsjSYpUTHRotTS9Tz3krfDucugW5Uok" + - "GQKC26QlyHXlPTZkC+aRFUs/dicJX2kopndLcnlNAPWiKnKtrsF" + - "SCnIJDBZIyvcKq+9RXmV3HK3bUdHnQZ88IZWBRmWKfZ6wnzHo53" + - "kdYKAemTErkztaX3lRRPLYWpxRcDPEjysXT3Lh0vfL5D+CIO1yK" + - "w/q7C+v6+/kYAxc2lfbNE3HpklSuF+dyX4nXxWgzbcFuLz5Bwfq" + - "6ZJ9RYe/kNkA0uMWNa1KkGeRh8gg22kgD/KT5hPTnpezUWLvoY5" + - "Qc7IB3T0y4n2JIwiF2ZrZYVrWgDjRWAzGsxJiJyjd6w2k0=\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\nsq" + - "l1.example.com. 3600 IN NS dns02.example.com.\nsql1" + - ".example.com. 3600 IN NS dns03.example.com.\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("sql1.example.com. 7200 IN NSEC www.sql1.example.com" + - ". NS SOA RRSIG NSEC DNSKEY\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("sql1.example.com. 3600 IN RRSIG SOA 5 3 3600 201003" + - "22084536 20100220084536 12447 sql1.example.com. FAK" + - "EFAKEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG NS 5" + - " 3 3600 20100322084536 20100220084536 12447 sql1.ex" + - "ample.com. FAKEFAKEFAKEFAKE\nsql1.example.com. 3600" + - " IN RRSIG NSEC 5 3 7200 20100322084536 201002200845" + - "36 12447 sql1.example.com. FAKEFAKEFAKEFAKE\nsql1.e" + - "xample.com. 3600 IN RRSIG DNSKEY 5 3 3600 201003220" + - "84536 20100220084536 12447 sql1.example.com. FAKEFA" + - "KEFAKEFAKE\nsql1.example.com. 3600 IN RRSIG DNSKEY " + - "5 3 3600 20100322084536 20100220084536 33313 sql1.e" + - "xample.com. FAKEFAKEFAKEFAKE\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("sql1.example.com. 3600 IN SOA master.example.com. a" + - "dmin.example.com. 678 3600 1800 2419200 7200\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("www.sql1.example.com. 3600 IN A 192.0.2.100\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("www.sql1.example.com. 7200 IN NSEC sql1.example.com" + - ". A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("www.sql1.example.com. 3600 IN RRSIG A 5 4 3600 2010" + - "0322084536 20100220084536 12447 sql1.example.com. F" + - "AKEFAKEFAKEFAKE\nwww.sql1.example.com. 3600 IN RRSI" + - "G NSEC 5 4 7200 20100322084536 20100220084536 12447" + - " sql1.example.com. FAKEFAKEFAKEFAKE\n", - rrs.get_next_rrset().to_text()) - self.assertEqual(None, rrs.get_next_rrset()) + + # we do not know the order in which they are returned by the iterator + # but we do want to check them, so we put all records into one list + # sort it (doesn't matter which way it is sorted, as long as it is + # sorted) + + # RRset is (atm) an unorderable type, and within an rrset, the + # rdatas and rrsigs may also be in random order. In theory the + # rrsets themselves can be returned in any order. + # + # So we create a second list with all rrsets we expect, and for each + # rrset we get from the iterator, see if it is in that list, and + # remove it. + # + # When the iterator is empty, we check no rrsets are left in the + # list of expected ones + expected_rrset_list = [] + + name = isc.dns.Name("sql1.example.com") + rrclass = isc.dns.RRClass.IN() + add_rrset(expected_rrset_list, name, rrclass, + isc.dns.RRType.DNSKEY(), isc.dns.RRTTL(3600), + [ + "256 3 5 AwEAAdYdRhBAEY67R/8G1N5AjGF6asIiNh/pNGeQ8xDQP13J"+ + "N2lo+sNqWcmpYNhuVqRbLB+mamsU1XcCICSBvAlSmfz/ZUdafX23knAr"+ + "TlALxMmspcfdpqun3Yr3YYnztuj06rV7RqmveYckWvAUXVYMSMQZfJ30"+ + "5fs0dE/xLztL/CzZ", + "257 3 5 AwEAAbaKDSa9XEFTsjSYpUTHRotTS9Tz3krfDucugW5UokGQ"+ + "KC26QlyHXlPTZkC+aRFUs/dicJX2kopndLcnlNAPWiKnKtrsFSCnIJDB"+ + "ZIyvcKq+9RXmV3HK3bUdHnQZ88IZWBRmWKfZ6wnzHo53kdYKAemTErkz"+ + "taX3lRRPLYWpxRcDPEjysXT3Lh0vfL5D+CIO1yKw/q7C+v6+/kYAxc2l"+ + "fbNE3HpklSuF+dyX4nXxWgzbcFuLz5Bwfq6ZJ9RYe/kNkA0uMWNa1KkG"+ + "eRh8gg22kgD/KT5hPTnpezUWLvoY5Qc7IB3T0y4n2JIwiF2ZrZYVrWgD"+ + "jRWAzGsxJiJyjd6w2k0=" + ]) + add_rrset(expected_rrset_list, name, rrclass, + isc.dns.RRType.NS(), isc.dns.RRTTL(3600), + [ + "dns01.example.com.", + "dns02.example.com.", + "dns03.example.com." + ]) + add_rrset(expected_rrset_list, name, rrclass, + isc.dns.RRType.NSEC(), isc.dns.RRTTL(7200), + [ + "www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY" + ]) + # For RRSIGS, we can't add the fake data through the API, so we + # simply pass no rdata at all (which is skipped by the check later) + add_rrset(expected_rrset_list, name, rrclass, + isc.dns.RRType.RRSIG(), isc.dns.RRTTL(3600), None) + add_rrset(expected_rrset_list, name, rrclass, + isc.dns.RRType.SOA(), isc.dns.RRTTL(3600), + [ + "master.example.com. admin.example.com. 678 3600 1800 2419200 7200" + ]) + name = isc.dns.Name("www.sql1.example.com.") + add_rrset(expected_rrset_list, name, rrclass, + isc.dns.RRType.A(), isc.dns.RRTTL(3600), + [ + "192.0.2.100" + ]) + name = isc.dns.Name("www.sql1.example.com.") + add_rrset(expected_rrset_list, name, rrclass, + isc.dns.RRType.NSEC(), isc.dns.RRTTL(7200), + [ + "sql1.example.com. A RRSIG NSEC" + ]) + add_rrset(expected_rrset_list, name, rrclass, + isc.dns.RRType.RRSIG(), isc.dns.RRTTL(3600), None) + + # rrs is an iterator, but also has direct get_next_rrset(), use + # the latter one here + rrset_to_check = rrs.get_next_rrset() + while (rrset_to_check != None): + self.assertTrue(check_for_rrset(expected_rrset_list, + rrset_to_check), + "Unexpected rrset returned by iterator:\n" + + rrset_to_check.to_text()) + rrset_to_check = rrs.get_next_rrset() + + # Now check there are none left + self.assertEqual(0, len(expected_rrset_list), + "RRset(s) not returned by iterator: " + + str([rrset.to_text() for rrset in expected_rrset_list ] + )) + # TODO should we catch this (iterating past end) and just return None # instead of failing? self.assertRaises(isc.datasrc.Error, rrs.get_next_rrset) - rrs = dsc.get_iterator(isc.dns.Name("example.com")) - self.assertEqual("*.wild.example.com. 3600 IN A 192.0.2.255\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("*.wild.example.com. 7200 IN NSEC www.example.com. A" + - " RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("*.wild.example.com. 3600 IN RRSIG A 5 3 3600 201003" + - "22084538 20100220084538 33495 example.com. FAKEFAKE" + - "FAKEFAKE\n*.wild.example.com. 3600 IN RRSIG NSEC 5 " + - "3 7200 20100322084538 20100220084538 33495 example." + - "com. FAKEFAKEFAKEFAKE\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("cname-ext.example.com. 3600 IN CNAME www.sql1.examp" + - "le.com.\n", rrs.get_next_rrset().to_text()) - self.assertEqual("cname-ext.example.com. 7200 IN NSEC cname-int.examp" + - "le.com. CNAME RRSIG NSEC\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("cname-ext.example.com. 3600 IN RRSIG CNAME 5 3 3600" + - " 20100322084538 20100220084538 33495 example.com. F" + - "AKEFAKEFAKEFAKE\ncname-ext.example.com. 3600 IN RRS" + - "IG NSEC 5 3 7200 20100322084538 20100220084538 3349" + - "5 example.com. FAKEFAKEFAKEFAKE\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("cname-int.example.com. 3600 IN CNAME www.example.co" + - "m.\n", rrs.get_next_rrset().to_text()) - self.assertEqual("cname-int.example.com. 7200 IN NSEC dname.example.c" + - "om. CNAME RRSIG NSEC\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("cname-int.example.com. 3600 IN RRSIG CNAME 5 3 3600" + - " 20100322084538 20100220084538 33495 example.com. F" + - "AKEFAKEFAKEFAKE\ncname-int.example.com. 3600 IN RRS" + - "IG NSEC 5 3 7200 20100322084538 20100220084538 3349" + - "5 example.com. FAKEFAKEFAKEFAKE\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("dname.example.com. 3600 IN DNAME sql1.example.com.\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("dname.example.com. 7200 IN NSEC dns01.example.com. " + - "DNAME RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("dname.example.com. 3600 IN RRSIG DNAME 5 3 3600 201" + - "00322084538 20100220084538 33495 example.com. FAKEF" + - "AKEFAKEFAKE\ndname.example.com. 3600 IN RRSIG NSEC " + - "5 3 7200 20100322084538 20100220084538 33495 exampl" + - "e.com. FAKEFAKEFAKEFAKE\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("dns01.example.com. 3600 IN A 192.0.2.1\n", - rrs.get_next_rrset().to_text()) - self.assertEqual("dns01.example.com. 7200 IN NSEC dns02.example.com. " + - "A RRSIG NSEC\n", rrs.get_next_rrset().to_text()) - self.assertEqual("dns01.example.com. 3600 IN RRSIG A 5 3 3600 2010032" + - "2084538 20100220084538 33495 example.com. FAKEFAKEF" + - "AKEFAKE\ndns01.example.com. 3600 IN RRSIG NSEC 5 3 " + - "7200 20100322084538 20100220084538 33495 example.co" + - "m. FAKEFAKEFAKEFAKE\n", - rrs.get_next_rrset().to_text()) - # there are more than 80 RRs in this zone... let's just count the rest - count = 0 - self.assertEqual(40, len(list(rrs))) + rrets = dsc.get_iterator(isc.dns.Name("example.com")) + # there are more than 80 RRs in this zone... let's just count them + # (already did a full check of the smaller zone above) + self.assertEqual(55, len(list(rrets))) # TODO should we catch this (iterating past end) and just return None # instead of failing? self.assertRaises(isc.datasrc.Error, rrs.get_next_rrset) From 58845974d57ee0cd0b261b00d1ededccc7bde105 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 16:00:58 +0200 Subject: [PATCH 778/974] [1179] update variable name in __init__.py --- src/lib/python/isc/datasrc/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py index e78103a1bf..f320283ed4 100644 --- a/src/lib/python/isc/datasrc/__init__.py +++ b/src/lib/python/isc/datasrc/__init__.py @@ -2,8 +2,8 @@ from isc.datasrc.master import * from isc.datasrc.sqlite3_ds import * for base in sys.path[:]: - loglibdir = os.path.join(base, 'isc/datasrc/.libs') - if os.path.exists(loglibdir): - sys.path.insert(0, loglibdir) + datasrclibdir = os.path.join(base, 'isc/datasrc/.libs') + if os.path.exists(datasrclibdir): + sys.path.insert(0, datasrclibdir) from datasrc import * From eefa62a767ec09c20d679876842e15e9d3742499 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 16:01:41 +0200 Subject: [PATCH 779/974] [1179] make that datasrc_libdir --- src/lib/python/isc/datasrc/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py index f320283ed4..05911f7a53 100644 --- a/src/lib/python/isc/datasrc/__init__.py +++ b/src/lib/python/isc/datasrc/__init__.py @@ -2,8 +2,8 @@ from isc.datasrc.master import * from isc.datasrc.sqlite3_ds import * for base in sys.path[:]: - datasrclibdir = os.path.join(base, 'isc/datasrc/.libs') - if os.path.exists(datasrclibdir): - sys.path.insert(0, datasrclibdir) + datasrc_libdir = os.path.join(base, 'isc/datasrc/.libs') + if os.path.exists(datasrc_libdir): + sys.path.insert(0, datasrc_libdir) from datasrc import * From 2c8b76ed408547789f2e26ad76773e40e316a392 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 16:53:37 +0200 Subject: [PATCH 780/974] [1179] remove some unneceesary comments, and share find() code --- src/lib/python/isc/datasrc/client_python.cc | 9 -- src/lib/python/isc/datasrc/datasrc.cc | 10 +- src/lib/python/isc/datasrc/datasrc.h | 2 +- src/lib/python/isc/datasrc/finder_python.cc | 130 ++++++++++-------- src/lib/python/isc/datasrc/iterator_python.cc | 2 - src/lib/python/isc/datasrc/updater_python.cc | 18 ++- 6 files changed, 90 insertions(+), 81 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc index e6f18725b5..984eabf594 100644 --- a/src/lib/python/isc/datasrc/client_python.cc +++ b/src/lib/python/isc/datasrc/client_python.cc @@ -57,8 +57,6 @@ public: typedef CPPPyObjectContainer DataSourceClientContainer; -// These are the functions we export -// PyObject* DataSourceClient_findZone(PyObject* po_self, PyObject* args) { s_DataSourceClient* const self = static_cast(po_self); @@ -145,8 +143,6 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) { } } -// These are the functions we export - // This list contains the actual set of functions we have in // python. Each entry has // 1. Python method name @@ -164,9 +160,6 @@ PyMethodDef DataSourceClient_methods[] = { { NULL, NULL, 0, NULL } }; -// This is a template of typical code logic of python class initialization -// with C++ backend. You'll need to adjust it according to details of the -// actual C++ class. int DataSourceClient_init(s_DataSourceClient* self, PyObject* args) { // TODO: we should use the factory function which hasn't been written @@ -201,8 +194,6 @@ DataSourceClient_init(s_DataSourceClient* self, PyObject* args) { return (-1); } -// This is a template of typical code logic of python object destructor. -// In many cases you can use it without modification, but check that carefully. void DataSourceClient_destroy(s_DataSourceClient* const self) { delete self->cppobj; diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc index f0e65d27a2..4b0324a4d3 100644 --- a/src/lib/python/isc/datasrc/datasrc.cc +++ b/src/lib/python/isc/datasrc/datasrc.cc @@ -43,11 +43,11 @@ PyObject* getDataSourceException(const char* ex_name) { PyObject* ex_obj = NULL; - PyObject* acl_module = PyImport_AddModule("isc.datasrc"); - if (acl_module != NULL) { - PyObject* acl_dict = PyModule_GetDict(acl_module); - if (acl_dict != NULL) { - ex_obj = PyDict_GetItemString(acl_dict, ex_name); + PyObject* datasrc_module = PyImport_AddModule("isc.datasrc"); + if (datasrc_module != NULL) { + PyObject* datasrc_dict = PyModule_GetDict(datasrc_module); + if (datasrc_dict != NULL) { + ex_obj = PyDict_GetItemString(datasrc_dict, ex_name); } } diff --git a/src/lib/python/isc/datasrc/datasrc.h b/src/lib/python/isc/datasrc/datasrc.h index 9ad1212ae0..d82881b9ce 100644 --- a/src/lib/python/isc/datasrc/datasrc.h +++ b/src/lib/python/isc/datasrc/datasrc.h @@ -29,7 +29,7 @@ namespace python { // C/C++ symbols defined in that module. So we get access to these object // using the Python interpretor through this wrapper function. // -// The __init__.py file should ensure isc.acl.acl has been loaded by the time +// The __init__.py file should ensure isc.datasrc has been loaded by the time // whenever this function is called, and there shouldn't be any operation // within this function that can fail (such as dynamic memory allocation), // so this function should always succeed. Yet there may be an overlooked diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index b1f97119d8..42b90347e0 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -45,66 +45,16 @@ using namespace isc::dns::python; using namespace isc::datasrc; using namespace isc::datasrc::python; -namespace { -// The s_* Class simply covers one instantiation of the object -class s_ZoneFinder : public PyObject { -public: - s_ZoneFinder() : cppobj(ZoneFinderPtr()) {}; - ZoneFinderPtr cppobj; -}; - -// Shortcut type which would be convenient for adding class variables safely. -typedef CPPPyObjectContainer ZoneFinderContainer; - -// General creation and destruction -int -ZoneFinder_init(s_ZoneFinder* self, PyObject* args) { - // can't be called directly - PyErr_SetString(PyExc_TypeError, - "ZoneFinder cannot be constructed directly"); - - return (-1); -} - -void -ZoneFinder_destroy(s_ZoneFinder* const self) { - // cppobj is a shared ptr, but to make sure things are not destroyed in - // the wrong order, we reset it here. - self->cppobj.reset(); - Py_TYPE(self)->tp_free(self); -} - -// These are the functions we export -// -PyObject* -ZoneFinder_getClass(PyObject* po_self, PyObject*) { - s_ZoneFinder* self = static_cast(po_self); - try { - return (createRRClassObject(self->cppobj->getClass())); - } catch (const std::exception& exc) { - PyErr_SetString(getDataSourceException("Error"), exc.what()); - return (NULL); - } -} - -PyObject* -ZoneFinder_getOrigin(PyObject* po_self, PyObject*) { - s_ZoneFinder* self = static_cast(po_self); - try { - return (createNameObject(self->cppobj->getOrigin())); - } catch (const std::exception& exc) { - PyErr_SetString(getDataSourceException("Error"), exc.what()); - return (NULL); - } catch (...) { +namespace isc_datasrc_internal { +// This is the shared code for the find() call in the finder and the updater +// Is is intentionally not available through any header, nor at our standard +// namespace. +PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) { + if (finder == NULL) { PyErr_SetString(getDataSourceException("Error"), - "Unexpected exception"); + "Internal error in find() wrapper; finder object NULL"); return (NULL); } -} - -PyObject* -ZoneFinder_find(PyObject* po_self, PyObject* args) { - s_ZoneFinder* const self = static_cast(po_self); PyObject *name; PyObject *rrtype; PyObject *target; @@ -116,7 +66,7 @@ ZoneFinder_find(PyObject* po_self, PyObject* args) { ZoneFinder::FindOptions options = static_cast(options_int); ZoneFinder::FindResult find_result( - self->cppobj->find(PyName_ToName(name), + finder->find(PyName_ToName(name), PyRRType_ToRRType(rrtype), NULL, options @@ -146,6 +96,69 @@ ZoneFinder_find(PyObject* po_self, PyObject* args) { return Py_BuildValue("I", 1); } +} // end namespace internal + +namespace { +// The s_* Class simply covers one instantiation of the object +class s_ZoneFinder : public PyObject { +public: + s_ZoneFinder() : cppobj(ZoneFinderPtr()) {}; + ZoneFinderPtr cppobj; +}; + +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer ZoneFinderContainer; + +// General creation and destruction +int +ZoneFinder_init(s_ZoneFinder* self, PyObject* args) { + // can't be called directly + PyErr_SetString(PyExc_TypeError, + "ZoneFinder cannot be constructed directly"); + + return (-1); +} + +void +ZoneFinder_destroy(s_ZoneFinder* const self) { + // cppobj is a shared ptr, but to make sure things are not destroyed in + // the wrong order, we reset it here. + self->cppobj.reset(); + Py_TYPE(self)->tp_free(self); +} + +PyObject* +ZoneFinder_getClass(PyObject* po_self, PyObject*) { + s_ZoneFinder* self = static_cast(po_self); + try { + return (createRRClassObject(self->cppobj->getClass())); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } +} + +PyObject* +ZoneFinder_getOrigin(PyObject* po_self, PyObject*) { + s_ZoneFinder* self = static_cast(po_self); + try { + return (createNameObject(self->cppobj->getOrigin())); + } catch (const std::exception& exc) { + PyErr_SetString(getDataSourceException("Error"), exc.what()); + return (NULL); + } catch (...) { + PyErr_SetString(getDataSourceException("Error"), + "Unexpected exception"); + return (NULL); + } +} + +PyObject* +ZoneFinder_find(PyObject* po_self, PyObject* args) { + s_ZoneFinder* const self = static_cast(po_self); + return (isc_datasrc_internal::ZoneFinder_helper(self->cppobj.get(), args)); +} + // This list contains the actual set of functions we have in // python. Each entry has // 1. Python method name @@ -167,6 +180,7 @@ PyMethodDef ZoneFinder_methods[] = { namespace isc { namespace datasrc { namespace python { + PyTypeObject zonefinder_type = { PyVarObject_HEAD_INIT(NULL, 0) "datasrc.ZoneFinder", diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc index 1797bb4ffe..b482ea69e4 100644 --- a/src/lib/python/isc/datasrc/iterator_python.cc +++ b/src/lib/python/isc/datasrc/iterator_python.cc @@ -63,8 +63,6 @@ ZoneIterator_init(s_ZoneIterator* self, PyObject* args) { return (-1); } -// This is a template of typical code logic of python object destructor. -// In many cases you can use it without modification, but check that carefully. void ZoneIterator_destroy(s_ZoneIterator* const self) { // cppobj is a shared ptr, but to make sure things are not destroyed in diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc index 5c1cd44eac..a9dc581088 100644 --- a/src/lib/python/isc/datasrc/updater_python.cc +++ b/src/lib/python/isc/datasrc/updater_python.cc @@ -45,6 +45,11 @@ using namespace isc::dns::python; using namespace isc::datasrc; using namespace isc::datasrc::python; +namespace isc_datasrc_internal { +// See finder_python.cc +PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args); +} + namespace { // The s_* Class simply covers one instantiation of the object class s_ZoneUpdater : public PyObject { @@ -71,8 +76,6 @@ ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args) { return (-1); } -// This is a template of typical code logic of python object destructor. -// In many cases you can use it without modification, but check that carefully. void ZoneUpdater_destroy(s_ZoneUpdater* const self) { // cppobj is a shared ptr, but to make sure things are not destroyed in @@ -81,8 +84,6 @@ ZoneUpdater_destroy(s_ZoneUpdater* const self) { Py_TYPE(self)->tp_free(self); } -// These are the functions we export -// PyObject* ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) { s_ZoneUpdater* const self = static_cast(po_self); @@ -138,8 +139,6 @@ ZoneUpdater_commit(PyObject* po_self, PyObject*) { } } -// These are the functions we export -// PyObject* ZoneUpdater_getClass(PyObject* po_self, PyObject*) { s_ZoneUpdater* self = static_cast(po_self); @@ -172,6 +171,13 @@ ZoneUpdater_getOrigin(PyObject* po_self, PyObject*) { PyObject* ZoneUpdater_find(PyObject* po_self, PyObject* args) { + s_ZoneUpdater* const self = static_cast(po_self); + return (isc_datasrc_internal::ZoneFinder_helper(&self->cppobj->getFinder(), + args)); +} + +PyObject* +AZoneUpdater_find(PyObject* po_self, PyObject* args) { s_ZoneUpdater* const self = static_cast(po_self); PyObject *name; PyObject *rrtype; From f1fef139dbc592aa4c7071d47e38e14487ab72e7 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 17:10:05 +0200 Subject: [PATCH 781/974] [1179] small comment addition --- src/lib/python/isc/datasrc/finder_python.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc index 42b90347e0..598d3001af 100644 --- a/src/lib/python/isc/datasrc/finder_python.cc +++ b/src/lib/python/isc/datasrc/finder_python.cc @@ -48,7 +48,8 @@ using namespace isc::datasrc::python; namespace isc_datasrc_internal { // This is the shared code for the find() call in the finder and the updater // Is is intentionally not available through any header, nor at our standard -// namespace. +// namespace, as it is not supposed to be called anywhere but from finder and +// updater PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) { if (finder == NULL) { PyErr_SetString(getDataSourceException("Error"), From 6c5f8867a45f40411594372bca09c04ddf5c0002 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 19:34:03 +0200 Subject: [PATCH 782/974] [1179] update docstrings --- src/lib/python/isc/datasrc/client_inc.cc | 71 ++++++++-------------- src/lib/python/isc/datasrc/finder_inc.cc | 27 +++----- src/lib/python/isc/datasrc/iterator_inc.cc | 14 ++--- src/lib/python/isc/datasrc/updater_inc.cc | 34 +++++------ 4 files changed, 59 insertions(+), 87 deletions(-) diff --git a/src/lib/python/isc/datasrc/client_inc.cc b/src/lib/python/isc/datasrc/client_inc.cc index 3b72308272..1eba4885b4 100644 --- a/src/lib/python/isc/datasrc/client_inc.cc +++ b/src/lib/python/isc/datasrc/client_inc.cc @@ -3,13 +3,13 @@ namespace { const char* const DataSourceClient_doc = "\ The base class of data source clients.\n\ \n\ -This is an abstract base class that defines the common interface for\n\ -various types of data source clients. A data source client is a top\n\ -level access point to a data source, allowing various operations on\n\ -the data source such as lookups, traversing or updates. The client\n\ -class itself has limited focus and delegates the responsibility for\n\ -these specific operations to other classes; in general methods of this\n\ -class act as factories of these other classes.\n\ +This is the python wrapper for the abstract base class that defines\n\ +the common interface for various types of data source clients. A data\n\ +source client is a top level access point to a data source, allowing \n\ +various operations on the data source such as lookups, traversing or \n\ +updates. The client class itself has limited focus and delegates \n\ +the responsibility for these specific operations to other (c++) classes;\n\ +in general methods of this class act as factories of these other classes.\n\ \n\ - InMemoryClient: A client of a conceptual data source that stores all\n\ necessary data in memory for faster lookups\n\ @@ -49,49 +49,30 @@ server). In order to avoid a surprising disruption with a naive copy\n\ it's prohibited explicitly. For the expected usage of the client\n\ classes the restriction should be acceptable.\n\ \n\ -TodoThis class is still not complete. It will need more factory\n\ +Todo: This class is still not complete. It will need more factory\n\ methods, e.g. for (re)loading a zone.\n\ -\n\ -DataSourceClient()\n\ -\n\ - Default constructor.\n\ -\n\ - This is intentionally defined as protected as this base class\n\ - should never be instantiated directly.\n\ -\n\ - The constructor of a concrete derived class may throw an\n\ - exception. This interface does not specify which exceptions can\n\ - happen (at least at this moment), and the caller should expect any\n\ - type of exception and react accordingly.\n\ -\n\ "; const char* const DataSourceClient_findZone_doc = "\ -find_zone(name) -> FindResult\n\ +find_zone(name) -> (code, ZoneFinder)\n\ \n\ Returns a ZoneFinder for a zone that best matches the given name.\n\ \n\ -- code: The result code of the operation.result.SUCCESS: A zone that\n\ - gives an exact match is foundresult.PARTIALMATCH: A zone whose\n\ - origin is a super domain of name is found (but there is no exact\n\ - match)result.NOTFOUND: For all other cases.\n\ -- result.SUCCESS: A zone that gives an exact match is found\n\ -- result.PARTIALMATCH: A zone whose origin is a super domain of name\n\ +code: The result code of the operation (integer).\n\ +- DataSourceClient.SUCCESS: A zone that gives an exact match is found\n\ +- DataSourceClient.PARTIALMATCH: A zone whose origin is a super domain of name\n\ is found (but there is no exact match)\n\ -- result.NOTFOUND: For all other cases.\n\ -- zone_finder: Pointer to a ZoneFinder object for the found zone if\n\ - one is found; otherwise NULL.\n\ +- DataSourceClient.NOTFOUND: For all other cases.\n\ +ZoneFinder: ZoneFinder object for the found zone if one is found;\n\ +otherwise None.\n\ \n\ -A specific derived version of this method may throw an exception. This\n\ -interface does not specify which exceptions can happen (at least at\n\ -this moment), and the caller should expect any type of exception and\n\ -react accordingly.\n\ +Any internal error will be raised as an isc.datasrc.Error exception\n\ \n\ Parameters:\n\ name A domain name for which the search is performed.\n\ \n\ -Return Value(s): A FindResult object enclosing the search result (see\n\ -above).\n\ +Return Value(s): A tuple containing a result value and a ZoneFinder object or\n\ +None\n\ "; const char* const DataSourceClient_getIterator_doc = "\ @@ -103,9 +84,9 @@ This allows for traversing the whole zone. The returned object can\n\ provide the RRsets one by one.\n\ \n\ This throws isc.datasrc.Error when the zone does not exist in the\n\ -datasource.\n\ +datasource, or when an internal error occurs.\n\ \n\ -The default implementation throws isc.NotImplemented. This allows for\n\ +The default implementation throws isc.datasrc.NotImplemented. This allows for\n\ easy and fast deployment of minimal custom data sources, where the\n\ user/implementator doesn't have to care about anything else but the\n\ actual queries. Also, in some cases, it isn't possible to traverse the\n\ @@ -115,8 +96,8 @@ It is not fixed if a concrete implementation of this method can throw\n\ anything else.\n\ \n\ Parameters:\n\ - name The name of zone apex to be traversed. It doesn't do\n\ - nearest match as find_zone.\n\ + isc.dns.Name The name of zone apex to be traversed. It doesn't do\n\ + nearest match as find_zone.\n\ \n\ Return Value(s): Pointer to the iterator.\n\ "; @@ -152,7 +133,7 @@ case as handling multiple incoming AXFR streams concurrently, but this\n\ interface does not even prohibit an attempt of getting more than one\n\ updater for the same zone, as long as the underlying data source\n\ allows such an operation (and any conflict resolution is left to the\n\ -specific derived class implementation).\n\ +specific implementation).\n\ \n\ If replace is true, any existing RRs of the zone will be deleted on\n\ successful completion of updates (after commit() on the updater); if\n\ @@ -160,13 +141,13 @@ it's false, the existing RRs will be intact unless explicitly deleted\n\ by delete_rrset() on the updater.\n\ \n\ A data source can be \"read only\" or can prohibit partial updates. In\n\ -such cases this method will result in an isc.NotImplemented exception\n\ +such cases this method will result in an isc.datasrc.NotImplemented exception\n\ unconditionally or when replace is false).\n\ \n\ Exceptions:\n\ - NotImplemented The underlying data source does not support updates.\n\ + isc.datasrc. NotImplemented The underlying data source does not support\n\ + updates.\n\ isc.datasrc.Error Internal error in the underlying data source.\n\ - std.bad_alloc Resource allocation failure.\n\ \n\ Parameters:\n\ name The zone name to be updated\n\ diff --git a/src/lib/python/isc/datasrc/finder_inc.cc b/src/lib/python/isc/datasrc/finder_inc.cc index d6ffde92ef..2b47d021d2 100644 --- a/src/lib/python/isc/datasrc/finder_inc.cc +++ b/src/lib/python/isc/datasrc/finder_inc.cc @@ -2,10 +2,10 @@ namespace { const char* const ZoneFinder_doc = "\ The base class to search a zone for RRsets.\n\ \n\ -The ZoneFinder class is an abstract base class for representing an\n\ +The ZoneFinder class is a wrapper for the c++ base class for representing an\n\ object that performs DNS lookups in a specific zone accessible via a\n\ data source. In general, different types of data sources (in-memory,\n\ -database-based, etc) define their own derived classes of ZoneFinder,\n\ +database-based, etc) define their own derived c++ classes of ZoneFinder,\n\ implementing ways to retrieve the required data through the common\n\ interfaces declared in the base class. Each concrete ZoneFinder object\n\ is therefore (conceptually) associated with a specific zone of one\n\ @@ -26,13 +26,6 @@ results for the same name and type can be different if other threads\n\ or programs make updates to the zone between the lookups. We should\n\ revisit this point as we gain more experiences.\n\ \n\ -ZoneFinder()\n\ -\n\ - The default constructor.\n\ -\n\ - This is intentionally defined as protected as this base class\n\ - should never be instantiated (except as part of a derived class).\n\ -\n\ "; const char* const ZoneFinder_getOrigin_doc = "\ @@ -50,7 +43,7 @@ Return the RR class of the zone.\n\ "; const char* const ZoneFinder_find_doc = "\ -find(name, type, target=NULL, options=FIND_DEFAULT) -> FindResult\n\ +find(name, type, target=NULL, options=FIND_DEFAULT) -> (code, FindResult)\n\ \n\ Search the zone for a given pair of domain name and RR type.\n\ \n\ @@ -69,7 +62,7 @@ Search the zone for a given pair of domain name and RR type.\n\ and the code of SUCCESS will be returned.\n\ - If the search name matches a delegation point of DNAME, it returns\n\ the code of DNAME and that DNAME RR.\n\ -- If the target isn't NULL, all RRsets under the domain are inserted\n\ +- If the target is a list, all RRsets under the domain are inserted\n\ there and SUCCESS (or NXDOMAIN, in case of empty domain) is returned\n\ instead of normall processing. This is intended to handle ANY query.\n\ : this behavior is controversial as we discussed in\n\ @@ -77,6 +70,7 @@ Search the zone for a given pair of domain name and RR type.\n\ We should revisit the interface before we heavily rely on it. The\n\ options parameter specifies customized behavior of the search. Their\n\ semantics is as follows:\n\ + (This feature is disable at this time)\n\ - GLUE_OK Allow search under a zone cut. By default the search will\n\ stop once it encounters a zone cut. If this option is specified it\n\ remembers information about the highest zone cut and continues the\n\ @@ -86,11 +80,8 @@ Search the zone for a given pair of domain name and RR type.\n\ the search has encountered a zone cut, DELEGATION with the\n\ information of the highest zone cut will be returned.\n\ \n\ -A derived version of this method may involve internal resource\n\ -allocation, especially for constructing the resulting RRset, and may\n\ -throw an exception if it fails. It throws DuplicateRRset exception if\n\ -there are duplicate rrsets under the same domain. It should not throw\n\ -other types of exceptions.\n\ +This method raises an isc.datasrc.Error exception if there is an internal\n\ +error in the datasource.\n\ \n\ Parameters:\n\ name The domain name to be searched for.\n\ @@ -99,7 +90,7 @@ Parameters:\n\ into it.\n\ options The search options.\n\ \n\ -Return Value(s): A FindResult object enclosing the search result (see\n\ -above).\n\ +Return Value(s): A tuple of a result code an a FindResult object enclosing\n\ +the search result (see above).\n\ "; } // unnamed namespace diff --git a/src/lib/python/isc/datasrc/iterator_inc.cc b/src/lib/python/isc/datasrc/iterator_inc.cc index 3ef1caf343..b1d9d2550e 100644 --- a/src/lib/python/isc/datasrc/iterator_inc.cc +++ b/src/lib/python/isc/datasrc/iterator_inc.cc @@ -3,9 +3,9 @@ namespace { const char* const ZoneIterator_doc = "\ Read-only iterator to a zone.\n\ \n\ -You can get an instance of (descendand of) ZoneIterator from\n\ +You can get an instance of the ZoneIterator from\n\ DataSourceClient.get_iterator() method. The actual concrete\n\ -implementation will be different depending on the actual data source\n\ +c++ implementation will be different depending on the actual data source\n\ used. This is the abstract interface.\n\ \n\ There's no way to start iterating from the beginning again or return.\n\ @@ -14,21 +14,21 @@ The ZoneIterator is a python iterator, and can be iterated over directly.\n\ "; const char* const ZoneIterator_getNextRRset_doc = "\ -get_next_rrset() -> isc.dns.ConstRRset\n\ +get_next_rrset() -> isc.dns.RRset\n\ \n\ Get next RRset from the zone.\n\ \n\ -This returns the next RRset in the zone as a shared pointer. The\n\ -shared pointer is used to allow both accessing in-memory data and\n\ -automatic memory management.\n\ +This returns the next RRset in the zone.\n\ \n\ Any special order is not guaranteed.\n\ \n\ While this can potentially throw anything (including standard\n\ allocation errors), it should be rare.\n\ \n\ -Pointer to the next RRset or NULL pointer when the iteration gets to\n\ +Pointer to the next RRset or None pointer when the iteration gets to\n\ the end of the zone.\n\ \n\ +Raises an isc.datasrc.Error exception if it is called again after returning\n\ +None\n\ "; } // unnamed namespace diff --git a/src/lib/python/isc/datasrc/updater_inc.cc b/src/lib/python/isc/datasrc/updater_inc.cc index dc313d2810..32715ecd65 100644 --- a/src/lib/python/isc/datasrc/updater_inc.cc +++ b/src/lib/python/isc/datasrc/updater_inc.cc @@ -14,14 +14,14 @@ some form of \"begin transaction\" statement for the database.\n\ Updates (adding or deleting RRs) are made via add_rrset() and\n\ delete_rrset() methods. Until the commit() method is called the\n\ changes are local to the updater object. For example, they won't be\n\ -visible via a ZoneFinder object except the one returned by the\n\ -updater's own find() method. The commit() completes the\n\ -transaction and makes the changes visible to others.\n\ +visible via a ZoneFinder object, but only by the updater's own find()\n\ +method. The commit() completes the transaction and makes the changes\n\ +visible to others.\n\ \n\ This class does not provide an explicit \"rollback\" interface. If\n\ something wrong or unexpected happens during the updates and the\n\ caller wants to cancel the intermediate updates, the caller should\n\ -simply destruct the updater object without calling commit(). The\n\ +simply destroy the updater object without calling commit(). The\n\ destructor is supposed to perform the \"rollback\" operation,\n\ depending on the internal details of the derived class.\n\ \n\ @@ -32,10 +32,10 @@ It may be revisited as we gain more experiences.\n\ "; const char* const ZoneUpdater_addRRset_doc = "\ -add_rrset(rrset) -> void\n\ +add_rrset(rrset) -> No return value\n\ \n\ Add an RRset to a zone via the updater.\n\ -\n\ +It performs a few basic checks:\n\ - Whether the RR class is identical to that for the zone to be updated\n\ - Whether the RRset is not empty, i.e., it has at least one RDATA\n\ - Whether the RRset is not associated with an RRSIG, i.e., whether\n\ @@ -76,7 +76,7 @@ This method must not be called once commit() is performed. If it calls\n\ after commit() the implementation must throw a isc.datasrc.Error\n\ exception.\n\ \n\ -TodoAs noted above we may have to revisit the design details as we\n\ +Todo As noted above we may have to revisit the design details as we\n\ gain experiences:\n\ \n\ - we may want to check (and maybe reject) if there is already a\n\ @@ -92,8 +92,7 @@ gain experiences:\n\ \n\ Exceptions:\n\ isc.datasrc.Error Called after commit(), RRset is invalid (see above),\n\ - internal data source error\n\ - std.bad_alloc Resource allocation failure\n\ + internal data source error, or wrapper error\n\ \n\ Parameters:\n\ rrset The RRset to be added\n\ @@ -101,7 +100,7 @@ Parameters:\n\ "; const char* const ZoneUpdater_deleteRRset_doc = "\ -delete_rrset(rrset) -> void\n\ +delete_rrset(rrset) -> No return value\n\ \n\ Delete an RRset from a zone via the updater.\n\ \n\ @@ -120,20 +119,21 @@ on the initial implementation decisions.\n\ Ignoring the TTL may not look sensible, but it's based on the\n\ observation that it will result in more intuitive result, especially\n\ when the underlying data source is a general purpose database. See\n\ -also DatabaseAccessor.delete_record_in_zone() on this point. It also\n\ -matches the dynamic update protocol (RFC2136), where TTLs are ignored\n\ -when deleting RRs.\n\ +also the c++ documentation of DatabaseAccessor::DeleteRecordInZone()\n\ +on this point. It also matches the dynamic update protocol (RFC2136),\n\ +where TTLs are ignored when deleting RRs.\n\ \n\ +This method performs a limited level of validation on the specified\n\ +RRset:\n\ - Whether the RR class is identical to that for the zone to be updated\n\ - Whether the RRset is not empty, i.e., it has at least one RDATA\n\ -- Whether the RRset is not associated with an RRSIG, i.e., whether\n\ - get_rrsig() on the RRset returns a NULL pointer.\n\ +- Whether the RRset is not associated with an RRSIG\n\ \n\ This method must not be called once commit() is performed. If it calls\n\ after commit() the implementation must throw a isc.datasrc.Error\n\ exception.\n\ \n\ -TodoAs noted above we may have to revisit the design details as we\n\ +Todo: As noted above we may have to revisit the design details as we\n\ gain experiences:\n\ \n\ - we may want to check (and maybe reject) if some or all of the RRs\n\ @@ -175,7 +175,7 @@ must result in a isc.datasrc.Error exception.\n\ \n\ Exceptions:\n\ isc.datasrc.Error Duplicate call of the method, internal data source\n\ - error\n\ + error, or wrapper error\n\\n\ \n\ "; } // unnamed namespace From 70bba1b3f811261fcef30694568245e83cd64bc5 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 23 Sep 2011 11:17:03 -0700 Subject: [PATCH 783/974] [1177] constify --- src/lib/datasrc/sqlite3_accessor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 22e1f30e18..69d564951d 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -688,7 +688,7 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname) } std::string result; - int rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]); + const int rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]); if (rc == SQLITE_ROW) { // We found it result = convertToPlainChar(sqlite3_column_text(dbparameters_-> From 31e010330189f489c624b7cdb812ef3f33f8e280 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 23 Sep 2011 14:34:30 -0700 Subject: [PATCH 784/974] [1144] some suggested (mostly) editorial cleanups: - style guideline adjustment (position of opening braces, indentation, etc) - avoid 'using namespace' before including a header file and (as a result of it) in ds_like.h - move standard header files from dlv/ds implementation files to ds_link.h as much as possible - trivial documentation updates --- src/lib/dns/rdata/generic/detail/ds_like.h | 50 ++++++++++++++------- src/lib/dns/rdata/generic/dlv_32769.cc | 11 ++--- src/lib/dns/rdata/generic/dlv_32769.h | 8 ++-- src/lib/dns/rdata/generic/ds_43.cc | 11 ++--- src/lib/dns/rdata/generic/ds_43.h | 28 ++++++++++-- src/lib/dns/tests/rdata_ds_like_unittest.cc | 11 +++-- 6 files changed, 73 insertions(+), 46 deletions(-) diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h index 40157e63f6..da3ade4338 100644 --- a/src/lib/dns/rdata/generic/detail/ds_like.h +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -17,19 +17,36 @@ #include +#include +#include #include #include +#include + +#include + +#include +#include +#include +#include + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + /// \brief \c rdata::DSLikeImpl class represents the DS-like RDATA for DS /// and DLV types. /// /// This class implements the basic interfaces inherited by the DS and DLV /// classes from the abstract \c rdata::Rdata class, and provides trivial /// accessors to DS-like RDATA. -templateclass DSLikeImpl { +template class DSLikeImpl { // Common sequence of toWire() operations used for the two versions of // toWire(). - template + template void toWireCommon(Output& output) const { output.writeUint16(tag_); @@ -45,10 +62,9 @@ public: /// /// \c InvalidRdataText is thrown if the method cannot process the /// parameter data for any of the number of reasons. - DSLikeImpl(const string& ds_str) - { - istringstream iss(ds_str); - stringbuf digestbuf; + DSLikeImpl(const std::string& ds_str) { + std::istringstream iss(ds_str); + std::stringbuf digestbuf; uint32_t tag, algorithm, digest_type; iss >> tag >> algorithm >> digest_type >> &digestbuf; @@ -90,24 +106,19 @@ public: isc_throw(InvalidRdataLength, RRType(typeCode) << " too short"); } - uint16_t tag = buffer.readUint16(); - uint16_t algorithm = buffer.readUint8(); - uint16_t digest_type = buffer.readUint8(); + tag_ = buffer.readUint16(); + algorithm_ = buffer.readUint8(); + digest_type_ = buffer.readUint8(); rdata_len -= 4; digest_.resize(rdata_len); buffer.readData(&digest_[0], rdata_len); - - tag_ = tag; - algorithm_ = algorithm; - digest_type_ = digest_type; } /// \brief The copy constructor. /// /// Trivial for now, we could've used the default one. - DSLikeImpl(const DSLikeImpl& source) - { + DSLikeImpl(const DSLikeImpl& source) { digest_ = source.digest_; tag_ = source.tag_; algorithm_ = source.algorithm_; @@ -117,7 +128,7 @@ public: /// \brief Convert the DS-like data to a string. /// /// \return A \c string object that represents the DS-like data. - string + std::string toText() const { using namespace boost; return (lexical_cast(static_cast(tag_)) + @@ -189,9 +200,14 @@ private: uint16_t tag_; uint8_t algorithm_; uint8_t digest_type_; - vector digest_; + std::vector digest_; }; +} +} +} +} +} #endif // __DS_LIKE_H // Local Variables: diff --git a/src/lib/dns/rdata/generic/dlv_32769.cc b/src/lib/dns/rdata/generic/dlv_32769.cc index 84a0d1603b..4e2d780397 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.cc +++ b/src/lib/dns/rdata/generic/dlv_32769.cc @@ -12,33 +12,28 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include #include -#include -#include - -#include #include #include #include -#include #include #include #include #include +#include + using namespace std; using namespace isc::util; using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE -#include - /// \brief Constructor from string. /// /// A copy of the implementation object is allocated and constructed. diff --git a/src/lib/dns/rdata/generic/dlv_32769.h b/src/lib/dns/rdata/generic/dlv_32769.h index cdc22ec46f..30128fb241 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.h +++ b/src/lib/dns/rdata/generic/dlv_32769.h @@ -30,9 +30,11 @@ // BEGIN_RDATA_NAMESPACE -template class DSLikeImpl; +namespace detail { +template class DSLikeImpl; +} -/// \brief \c rdata::DLV class represents the DLV RDATA as defined %in +/// \brief \c rdata::generic::DLV class represents the DLV RDATA as defined in /// RFC4431. /// /// This class implements the basic interfaces inherited from the abstract @@ -62,7 +64,7 @@ public: /// This method never throws an exception. uint16_t getTag() const; private: - typedef DSLikeImpl DLVImpl; + typedef detail::DSLikeImpl DLVImpl; DLVImpl* impl_; }; diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc index 0883616f69..5535d9d279 100644 --- a/src/lib/dns/rdata/generic/ds_43.cc +++ b/src/lib/dns/rdata/generic/ds_43.cc @@ -12,33 +12,28 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include #include -#include -#include - -#include #include #include #include -#include #include #include +#include + #include #include using namespace std; using namespace isc::util; using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE -#include - DS::DS(const string& ds_str) : impl_(new DSImpl(ds_str)) {} diff --git a/src/lib/dns/rdata/generic/ds_43.h b/src/lib/dns/rdata/generic/ds_43.h index ef441b6ef6..52f2f0b3cd 100644 --- a/src/lib/dns/rdata/generic/ds_43.h +++ b/src/lib/dns/rdata/generic/ds_43.h @@ -30,21 +30,41 @@ // BEGIN_RDATA_NAMESPACE -template class DSLikeImpl; +namespace detail { +template class DSLikeImpl; +} +/// \brief \c rdata::generic::DS class represents the DS RDATA as defined in +/// RFC3658. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DS RDATA. class DS : public Rdata { public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. DS& operator=(const DS& source); + + /// \brief The destructor. ~DS(); + /// \brief Return the value of the Tag field. /// - /// Specialized methods - /// + /// This method never throws an exception. uint16_t getTag() const; private: - typedef DSLikeImpl DSImpl; + typedef detail::DSLikeImpl DSImpl; DSImpl* impl_; }; diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc index 62937df23e..354dfe7522 100644 --- a/src/lib/dns/tests/rdata_ds_like_unittest.cc +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -33,8 +33,9 @@ using namespace isc::dns; using namespace isc::util; using namespace isc::dns::rdata; +namespace { // hacks to make templates work -template +template class RRTYPE : public RRType { public: RRTYPE(); @@ -43,7 +44,6 @@ public: template<> RRTYPE::RRTYPE() : RRType(RRType::DS()) {} template<> RRTYPE::RRTYPE() : RRType(RRType::DLV()) {} -namespace { template class Rdata_DS_LIKE_Test : public RdataTest { protected: @@ -51,7 +51,7 @@ protected: }; string ds_like_txt("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" - "5F0EB5C777586DE18DA6B5"); + "5F0EB5C777586DE18DA6B5"); template DS_LIKE const Rdata_DS_LIKE_Test::rdata_ds_like(ds_like_txt); @@ -103,7 +103,7 @@ TYPED_TEST(Rdata_DS_LIKE_Test, toWireRenderer) { vector data; UnitTestUtil::readWireData("rdata_ds_fromWire", data); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, - static_cast + static_cast (Rdata_DS_LIKE_Test::obuffer.getData()) + 2, Rdata_DS_LIKE_Test::obuffer.getLength() - 2, &data[2], data.size() - 2); @@ -116,8 +116,7 @@ TYPED_TEST(Rdata_DS_LIKE_Test, toWireBuffer) { TYPED_TEST(Rdata_DS_LIKE_Test, compare) { // trivial case: self equivalence - EXPECT_EQ(0, - TypeParam(ds_like_txt).compare(TypeParam(ds_like_txt))); + EXPECT_EQ(0, TypeParam(ds_like_txt).compare(TypeParam(ds_like_txt))); // TODO: need more tests } From b6dd72042939ca62d9ceeb80385eedc7c5f0560d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 23 Sep 2011 14:55:49 -0700 Subject: [PATCH 785/974] [1144] added simple assignment tests mainly only for covering the code. --- src/lib/dns/tests/rdata_ds_like_unittest.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc index 354dfe7522..daf0dd48c8 100644 --- a/src/lib/dns/tests/rdata_ds_like_unittest.cc +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -91,6 +91,23 @@ TYPED_TEST(Rdata_DS_LIKE_Test, createFromWire_DS_LIKE) { "rdata_ds_fromWire"))); } +TYPED_TEST(Rdata_DS_LIKE_Test, assignment_DS_LIKE) { + TypeParam copy((string(ds_like_txt))); + copy = this->rdata_ds_like; + EXPECT_EQ(0, copy.compare(this->rdata_ds_like)); + + // Check if the copied data is valid even after the original is deleted + TypeParam* copy2 = new TypeParam(this->rdata_ds_like); + TypeParam copy3((string(ds_like_txt))); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(this->rdata_ds_like)); + + // Self assignment + copy = copy; + EXPECT_EQ(0, copy.compare(this->rdata_ds_like)); +} + TYPED_TEST(Rdata_DS_LIKE_Test, getTag_DS_LIKE) { EXPECT_EQ(12892, Rdata_DS_LIKE_Test::rdata_ds_like.getTag()); } From 8cc8f4c008f640b7f13f8f1160261275ec14475b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 23 Sep 2011 17:40:31 -0700 Subject: [PATCH 786/974] [1165] added new configuration data: "zone_config". While the name is generic, the intent is to use it for transfer ACL per zone. The notion of per-zone configuration should eventually be implemented in a more generic way (not specific to xfrout). --- src/bin/xfrout/tests/xfrout_test.py.in | 75 ++++++++++++++++++++++++-- src/bin/xfrout/xfrout.py.in | 45 ++++++++++++++-- 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index 62c7708620..fbc3567102 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -636,17 +636,17 @@ class TestUnixSockServer(unittest.TestCase): socket.AI_NUMERICHOST)[0][4]) self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context)) - def check_loaded_ACL(self): + def check_loaded_ACL(self, acl): context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1", 1234, 0, socket.SOCK_DGRAM, socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0][4]) - self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context)) + self.assertEqual(isc.acl.acl.ACCEPT, acl.execute(context)) context = isc.acl.dns.RequestContext(socket.getaddrinfo("192.0.2.1", 1234, 0, socket.SOCK_DGRAM, socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0][4]) - self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context)) + self.assertEqual(isc.acl.acl.REJECT, acl.execute(context)) def test_update_config_data(self): self.check_default_ACL() @@ -673,12 +673,77 @@ class TestUnixSockServer(unittest.TestCase): # Load the ACL self.unix.update_config_data({'query_acl': [{'from': '127.0.0.1', 'action': 'ACCEPT'}]}) - self.check_loaded_ACL() + self.check_loaded_ACL(self.unix._acl) # Pass a wrong data there and check it does not replace the old one self.assertRaises(isc.acl.acl.LoaderError, self.unix.update_config_data, {'query_acl': ['Something bad']}) - self.check_loaded_ACL() + self.check_loaded_ACL(self.unix._acl) + + def test_zone_config_data(self): + # By default, there's no specific zone config + self.assertEqual({}, self.unix._zone_config) + + # Adding config for a specific zone. The config is empty unless + # explicitly specified. + self.unix.update_config_data({'zone_config': + [{'origin': 'example.com', + 'class': 'IN'}]}) + self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')]) + + # zone class can be omitted + self.unix.update_config_data({'zone_config': + [{'origin': 'example.com'}]}) + self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')]) + + # zone class, name are stored in the "normalized" form. class + # strings are upper cased, names are down cased. + self.unix.update_config_data({'zone_config': + [{'origin': 'EXAMPLE.com'}]}) + self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')]) + + # invalid zone class, name will result in exceptions + self.assertRaises(EmptyLabel, + self.unix.update_config_data, + {'zone_config': [{'origin': 'bad..example'}]}) + self.assertRaises(InvalidRRClass, + self.unix.update_config_data, + {'zone_config': [{'origin': 'example.com', + 'class': 'badclass'}]}) + + # Configuring a couple of more zones + self.unix.update_config_data({'zone_config': + [{'origin': 'example.com'}, + {'origin': 'example.com', + 'class': 'CH'}, + {'origin': 'example.org'}]}) + self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')]) + self.assertEqual({}, self.unix._zone_config[('CH', 'example.com.')]) + self.assertEqual({}, self.unix._zone_config[('IN', 'example.org.')]) + + # Duplicate data: should be rejected with an exception + self.assertRaises(ValueError, + self.unix.update_config_data, + {'zone_config': [{'origin': 'example.com'}, + {'origin': 'example.org'}, + {'origin': 'example.com'}]}) + + def test_zone_config_data_with_acl(self): + # Similar to the previous test, but with transfer_acl config + self.unix.update_config_data({'zone_config': + [{'origin': 'example.com', + 'transfer_acl': + [{'from': '127.0.0.1', + 'action': 'ACCEPT'}]}]}) + acl = self.unix._zone_config[('IN', 'example.com.')]['transfer_acl'] + self.check_loaded_ACL(acl) + + # invalid ACL syntax will be rejected with exception + self.assertRaises(isc.acl.acl.LoaderError, + self.unix.update_config_data, + {'zone_config': [{'origin': 'example.com', + 'transfer_acl': + [{'action': 'BADACTION'}]}]}) def test_get_db_file(self): self.assertEqual(self.unix.get_db_file(), "initdb.file") diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 144a1b8055..eb3989c9cd 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -86,6 +86,9 @@ TSIG_SIGN_EVERY_NTH = 96 XFROUT_MAX_MESSAGE_SIZE = 65535 +# In practice, RR class is almost always fixed, so if and when we allow +# it to be configured, it's convenient to make it optional. +DEFAULT_RRCLASS = RRClass('IN') def get_rrset_len(rrset): """Returns the wire length of the given RRset""" @@ -401,10 +404,11 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): def _common_init(self): self._lock = threading.Lock() self._transfers_counter = 0 - # This default value will probably get overwritten by the (same) - # default value from the spec file. This is here just to make - # sure and to make the default value in tests consistent. + # These default values will probably get overwritten by the (same) + # default value from the spec file. These are here just to make + # sure and to make the default values in tests consistent. self._acl = REQUEST_LOADER.load('[{"action": "ACCEPT"}]') + self._zone_config = {} def _receive_query_message(self, sock): ''' receive request message from sock''' @@ -551,16 +555,49 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): pass def update_config_data(self, new_config): - '''Apply the new config setting of xfrout module. ''' + '''Apply the new config setting of xfrout module. + + Note: this method does not provide strong exception guarantee; + if an exception is raised in the middle of parsing and building the + given config data, the incomplete set of new configuration will + remain. This should be fixed. + ''' logger.info(XFROUT_NEW_CONFIG) if 'query_acl' in new_config: self._acl = REQUEST_LOADER.load(new_config['query_acl']) + if 'zone_config' in new_config: + self._zone_config = \ + self.__create_zone_config(new_config.get('zone_config')) self._lock.acquire() self._max_transfers_out = new_config.get('transfers_out') self.set_tsig_key_ring(new_config.get('tsig_key_ring')) self._lock.release() logger.info(XFROUT_NEW_CONFIG_DONE) + def __create_zone_config(self, zone_config_list): + new_config = {} + for zconf in zone_config_list: + # convert the class, origin (name) pair. First build pydnspp + # object to reject invalid input. + if 'class' in zconf: + zclass = RRClass(zconf['class']) + else: + zclass = DEFAULT_RRCLASS + zorigin = Name(zconf['origin'], True) + config_key = (zclass.to_text(), zorigin.to_text()) + + # reject duplicate config + if config_key in new_config: + raise ValueError('Duplicaet zone_config for ' + + str(zorigin) + '/' + str(zclass)) + + # create a new config entry, build any given (and known) config + new_config[config_key] = {} + if 'transfer_acl' in zconf: + new_config[config_key]['transfer_acl'] = \ + REQUEST_LOADER.load(zconf['transfer_acl']) + return new_config + def set_tsig_key_ring(self, key_list): """Set the tsig_key_ring , given a TSIG key string list representation. """ From 383b6b2891226228ddf3cfd4c3dd8b17ea186b8a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 23 Sep 2011 20:39:35 -0700 Subject: [PATCH 787/974] [1165] a small cleanup: use constant instead of dynamically creating default class --- src/bin/xfrout/xfrout.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index eb3989c9cd..704721f7a0 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -88,7 +88,7 @@ XFROUT_MAX_MESSAGE_SIZE = 65535 # In practice, RR class is almost always fixed, so if and when we allow # it to be configured, it's convenient to make it optional. -DEFAULT_RRCLASS = RRClass('IN') +DEFAULT_RRCLASS = RRClass.IN() def get_rrset_len(rrset): """Returns the wire length of the given RRset""" From 57f7044d690d38cff90487b5883883a674d2589f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 23 Sep 2011 20:40:18 -0700 Subject: [PATCH 788/974] [1165] refactor the tests for later use: separate ACL related tests into a single function and provide capability of specifying the ACL context. --- src/bin/xfrout/tests/xfrout_test.py.in | 50 ++++++++++++++++---------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index fbc3567102..d29fae6f78 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -101,20 +101,24 @@ class TestXfroutSession(unittest.TestCase): def message_has_tsig(self, msg): return msg.get_tsig_record() is not None - def create_request_data_with_tsig(self): + def create_request_data(self, with_tsig=False): msg = Message(Message.RENDER) query_id = 0x1035 msg.set_qid(query_id) msg.set_opcode(Opcode.QUERY()) msg.set_rcode(Rcode.NOERROR()) - query_question = Question(Name("example.com."), RRClass.IN(), RRType.AXFR()) + query_question = Question(Name("example.com"), RRClass.IN(), + RRType.AXFR()) msg.add_question(query_question) renderer = MessageRenderer() - tsig_ctx = MockTSIGContext(TSIG_KEY) - msg.to_wire(renderer, tsig_ctx) - reply_data = renderer.get_data() - return reply_data + if with_tsig: + tsig_ctx = MockTSIGContext(TSIG_KEY) + msg.to_wire(renderer, tsig_ctx) + else: + msg.to_wire(renderer) + request_data = renderer.get_data() + return request_data def setUp(self): self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM) @@ -123,7 +127,7 @@ class TestXfroutSession(unittest.TestCase): # When not testing ACLs, simply accept isc.acl.dns.REQUEST_LOADER.load( [{"action": "ACCEPT"}])) - self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01') + self.mdata = self.create_request_data(False) self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200') def test_parse_query_message(self): @@ -131,7 +135,7 @@ class TestXfroutSession(unittest.TestCase): self.assertEqual(get_rcode.to_text(), "NOERROR") # tsig signed query message - request_data = self.create_request_data_with_tsig() + request_data = self.create_request_data(True) # BADKEY [rcode, msg] = self.xfrsess._parse_query_message(request_data) self.assertEqual(rcode.to_text(), "NOTAUTH") @@ -143,8 +147,9 @@ class TestXfroutSession(unittest.TestCase): self.assertEqual(rcode.to_text(), "NOERROR") self.assertTrue(self.xfrsess._tsig_ctx is not None) + def check_transfer_acl(self, acl_setter): # ACL checks, put some ACL inside - self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + acl_setter(isc.acl.dns.REQUEST_LOADER.load([ { "from": "127.0.0.1", "action": "ACCEPT" @@ -153,7 +158,7 @@ class TestXfroutSession(unittest.TestCase): "from": "192.0.2.1", "action": "DROP" } - ]) + ])) # Localhost (the default in this test) is accepted rcode, msg = self.xfrsess._parse_query_message(self.mdata) self.assertEqual(rcode.to_text(), "NOERROR") @@ -165,6 +170,10 @@ class TestXfroutSession(unittest.TestCase): self.xfrsess._remote = ('192.0.2.2', 12345) rcode, msg = self.xfrsess._parse_query_message(self.mdata) self.assertEqual(rcode.to_text(), "REFUSED") + + # TSIG signed request + request_data = self.create_request_data(True) + # If the TSIG check fails, it should not check ACL # (If it checked ACL as well, it would just drop the request) self.xfrsess._remote = ('192.0.2.1', 12345) @@ -174,36 +183,36 @@ class TestXfroutSession(unittest.TestCase): self.assertTrue(self.xfrsess._tsig_ctx is not None) # ACL using TSIG: successful case - self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + acl_setter(isc.acl.dns.REQUEST_LOADER.load([ {"key": "example.com", "action": "ACCEPT"}, {"action": "REJECT"} - ]) + ])) self.assertEqual(TSIGKeyRing.SUCCESS, self.xfrsess._tsig_key_ring.add(TSIG_KEY)) [rcode, msg] = self.xfrsess._parse_query_message(request_data) self.assertEqual(rcode.to_text(), "NOERROR") # ACL using TSIG: key name doesn't match; should be rejected - self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + acl_setter(isc.acl.dns.REQUEST_LOADER.load([ {"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"} - ]) + ])) [rcode, msg] = self.xfrsess._parse_query_message(request_data) self.assertEqual(rcode.to_text(), "REFUSED") # ACL using TSIG: no TSIG; should be rejected - self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + acl_setter(isc.acl.dns.REQUEST_LOADER.load([ {"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"} - ]) + ])) [rcode, msg] = self.xfrsess._parse_query_message(self.mdata) self.assertEqual(rcode.to_text(), "REFUSED") # # ACL using IP + TSIG: both should match # - self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + acl_setter(isc.acl.dns.REQUEST_LOADER.load([ {"ALL": [{"key": "example.com"}, {"from": "192.0.2.1"}], "action": "ACCEPT"}, {"action": "REJECT"} - ]) + ])) # both matches self.xfrsess._remote = ('192.0.2.1', 12345) [rcode, msg] = self.xfrsess._parse_query_message(request_data) @@ -221,6 +230,11 @@ class TestXfroutSession(unittest.TestCase): [rcode, msg] = self.xfrsess._parse_query_message(self.mdata) self.assertEqual(rcode.to_text(), "REFUSED") + def test_transfer_acl(self): + def acl_setter(acl): + self.xfrsess._acl = acl + self.check_transfer_acl(acl_setter) + def test_get_query_zone_name(self): msg = self.getmsg() self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.") From e602f86dae29c62619b0ea8bf2ca69e1ce1b8295 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 24 Sep 2011 12:09:40 -0700 Subject: [PATCH 789/974] [1165] added _get_transfer_acl to XfroutSession class to retrieve the best matching ACL for the given zone. --- src/bin/xfrout/tests/xfrout_test.py.in | 28 ++++++++++++++++++++++++++ src/bin/xfrout/xfrout.py.in | 19 +++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index d29fae6f78..ed645a6042 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -235,6 +235,34 @@ class TestXfroutSession(unittest.TestCase): self.xfrsess._acl = acl self.check_transfer_acl(acl_setter) + def test_get_transfer_acl(self): + # set the default ACL. If there's no specific zone ACL, this one + # should be used. + self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + {"from": "127.0.0.1", "action": "ACCEPT"}]) + acl = self.xfrsess._get_transfer_acl(Name('example.com'), RRClass.IN()) + self.assertEqual(acl, self.xfrsess._acl) + + # install a per zone config with transfer ACL for example.com. Then + # that ACL will be used for example.com; for others the default ACL + # will still be used. + com_acl = isc.acl.dns.REQUEST_LOADER.load([ + {"from": "127.0.0.1", "action": "REJECT"}]) + self.xfrsess._zone_config[('example.com.', 'IN')] = {} + self.xfrsess._zone_config[('example.com.', 'IN')]['transfer_acl'] = \ + com_acl + self.assertEqual(com_acl, + self.xfrsess._get_transfer_acl(Name('example.com'), + RRClass.IN())) + self.assertEqual(self.xfrsess._acl, + self.xfrsess._get_transfer_acl(Name('example.org'), + RRClass.IN())) + + # Name matching should be case insensitive. + self.assertEqual(com_acl, + self.xfrsess._get_transfer_acl(Name('EXAMPLE.COM'), + RRClass.IN())) + def test_get_query_zone_name(self): msg = self.getmsg() self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.") diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 704721f7a0..5191498d03 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -108,6 +108,7 @@ class XfroutSession(): self._tsig_len = 0 self._remote = remote self._acl = acl + self._zone_config = {} self.handle() def create_tsig_ctx(self, tsig_record, tsig_key_ring): @@ -171,6 +172,24 @@ class XfroutSession(): return rcode, msg + def _get_transfer_acl(self, zone_name, zone_class): + '''Return the ACL that should be applied for a given zone. + + The zone is identified by a tuple of name and RR class. + If a per zone configuration for the zone exists and contains + transfer_acl, that ACL will be used; otherwise, the default + ACL will be used. + + ''' + # Internally zone names are managed in lower cased label characters, + # so we first need to convert the name. + zone_name_lower = Name(zone_name.to_text(), True) + config_key = (zone_name_lower.to_text(), zone_class.to_text()) + if config_key in self._zone_config and \ + 'transfer_acl' in self._zone_config[config_key]: + return self._zone_config[config_key]['transfer_acl'] + return self._acl + def _get_query_zone_name(self, msg): question = msg.get_question()[0] return question.get_name().to_text() From 219818389cc848dc2d67aff732b9790968851b51 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 24 Sep 2011 15:40:56 -0700 Subject: [PATCH 790/974] [1165] make sure the ACL returned by _get_transfer_alc() so that per zone ACL will be used when configured. An unrelated change was piggy-backed: the ACL check was moved outside of the try-except block. it doesn't make sense to return FORMERR when an exception is raised in the ACL check. Also some minor style fixes were made (folding some long lines) --- src/bin/xfrout/tests/xfrout_test.py.in | 27 +++++++++- src/bin/xfrout/xfrout.py.in | 72 ++++++++++++++------------ 2 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index ed645a6042..d6af2c987e 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -126,7 +126,8 @@ class TestXfroutSession(unittest.TestCase): TSIGKeyRing(), ('127.0.0.1', 12345), # When not testing ACLs, simply accept isc.acl.dns.REQUEST_LOADER.load( - [{"action": "ACCEPT"}])) + [{"action": "ACCEPT"}]), + {}) self.mdata = self.create_request_data(False) self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200') @@ -231,10 +232,34 @@ class TestXfroutSession(unittest.TestCase): self.assertEqual(rcode.to_text(), "REFUSED") def test_transfer_acl(self): + # ACL checks only with the default ACL def acl_setter(acl): self.xfrsess._acl = acl self.check_transfer_acl(acl_setter) + def test_transfer_zoneacl(self): + # ACL check with a per zone ACL + default ACL. The per zone ACL + # should match the queryied zone, so it should be used. + def acl_setter(acl): + zone_key = ('example.com.', 'IN') + self.xfrsess._zone_config[zone_key] = {} + self.xfrsess._zone_config[zone_key]['transfer_acl'] = acl + self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ + {"from": "127.0.0.1", "action": "DROP"}]) + self.check_transfer_acl(acl_setter) + + def test_transfer_zoneacl_nomatch(self): + # similar to the previous one, but the per zone doesn't match the + # query. The default should be used. + def acl_setter(acl): + zone_key = ('example.org.', 'IN') + self.xfrsess._zone_config[zone_key] = {} + self.xfrsess._zone_config[zone_key]['transfer_acl'] = \ + isc.acl.dns.REQUEST_LOADER.load([ + {"from": "127.0.0.1", "action": "DROP"}]) + self.xfrsess._acl = acl + self.check_transfer_acl(acl_setter) + def test_get_transfer_acl(self): # set the default ACL. If there's no specific zone ACL, this one # should be used. diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 5191498d03..44422c03fb 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -99,7 +99,7 @@ def get_rrset_len(rrset): class XfroutSession(): def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote, - acl): + default_acl, zone_config): self._sock_fd = sock_fd self._request_data = request_data self._server = server @@ -107,8 +107,8 @@ class XfroutSession(): self._tsig_ctx = None self._tsig_len = 0 self._remote = remote - self._acl = acl - self._zone_config = {} + self._acl = default_acl + self._zone_config = zone_config self.handle() def create_tsig_ctx(self, tsig_record, tsig_key_ring): @@ -144,32 +144,30 @@ class XfroutSession(): try: msg = Message(Message.PARSE) Message.from_wire(msg, mdata) - - # TSIG related checks - rcode = self._check_request_tsig(msg, mdata) - - if rcode == Rcode.NOERROR(): - # ACL checks - acl_result = self._acl.execute( - isc.acl.dns.RequestContext(self._remote, - msg.get_tsig_record())) - if acl_result == DROP: - logger.info(XFROUT_QUERY_DROPPED, - self._get_query_zone_name(msg), - self._get_query_zone_class(msg), - self._remote[0], self._remote[1]) - return None, None - elif acl_result == REJECT: - logger.info(XFROUT_QUERY_REJECTED, - self._get_query_zone_name(msg), - self._get_query_zone_class(msg), - self._remote[0], self._remote[1]) - return Rcode.REFUSED(), msg - - except Exception as err: + except Exception as err: # Exception is too broad logger.error(XFROUT_PARSE_QUERY_ERROR, err) return Rcode.FORMERR(), None + # TSIG related checks + rcode = self._check_request_tsig(msg, mdata) + + if rcode == Rcode.NOERROR(): + # ACL checks + zone_name = msg.get_question()[0].get_name() + zone_class = msg.get_question()[0].get_class() + acl = self._get_transfer_acl(zone_name, zone_class) + acl_result = acl.execute( + isc.acl.dns.RequestContext(self._remote, + msg.get_tsig_record())) + if acl_result == DROP: + logger.info(XFROUT_QUERY_DROPPED, zone_name, zone_class, + self._remote[0], self._remote[1]) + return None, None + elif acl_result == REJECT: + logger.info(XFROUT_QUERY_REJECTED, zone_name, zone_class, + self._remote[0], self._remote[1]) + return Rcode.REFUSED(), msg + return rcode, msg def _get_transfer_acl(self, zone_name, zone_class): @@ -406,10 +404,12 @@ class XfroutSession(): self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len, count_since_last_tsig_sign) -class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): +class UnixSockServer(socketserver_mixin.NoPollMixIn, + ThreadingUnixStreamServer): '''The unix domain socket server which accept xfr query sent from auth server.''' - def __init__(self, sock_file, handle_class, shutdown_event, config_data, cc): + def __init__(self, sock_file, handle_class, shutdown_event, config_data, + cc): self._remove_unused_sock_file(sock_file) self._sock_file = sock_file socketserver_mixin.NoPollMixIn.__init__(self) @@ -505,7 +505,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): if not request_data: return - t = threading.Thread(target = self.finish_request, + t = threading.Thread(target=self.finish_request, args = (sock_fd, request_data)) if self.daemon_threads: t.daemon = True @@ -529,10 +529,14 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer): return sock.getpeername() def finish_request(self, sock_fd, request_data): - '''Finish one request by instantiating RequestHandlerClass.''' + '''Finish one request by instantiating RequestHandlerClass. + + This method creates a XfroutSession object. + ''' self.RequestHandlerClass(sock_fd, request_data, self, self.tsig_key_ring, - self._guess_remote(sock_fd), self._acl) + self._guess_remote(sock_fd), self._acl, + self._zone_config) def _remove_unused_sock_file(self, sock_file): '''Try to remove the socket file. If the file is being used @@ -673,8 +677,10 @@ class XfroutServer: def _start_xfr_query_listener(self): '''Start a new thread to accept xfr query. ''' - self._unix_socket_server = UnixSockServer(self._listen_sock_file, XfroutSession, - self._shutdown_event, self._config_data, + self._unix_socket_server = UnixSockServer(self._listen_sock_file, + XfroutSession, + self._shutdown_event, + self._config_data, self._cc) listener = threading.Thread(target=self._unix_socket_server.serve_forever) listener.start() From 27f447c8b054b17d96abfba431568c1ffe017f0a Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 26 Sep 2011 12:38:53 +0200 Subject: [PATCH 791/974] [1177] Reuse common code --- src/lib/datasrc/database.cc | 79 ++++++++++++++++--------------------- src/lib/datasrc/database.h | 17 ++++++++ 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 591099cfed..10da5ffe4b 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -351,6 +351,36 @@ FINAL_TYPES() { } +RRsetPtr +DatabaseClient::Finder::findNSECCover(const Name& name) { + try { + // Which one should contain the NSEC record? + const Name coverName(findPreviousName(name)); + // Get the record and copy it out + FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(), true); + const FoundIterator + nci(found.second.find(RRType::NSEC())); + if (nci != found.second.end()) { + return (nci->second); + } else { + // The previous doesn't contain NSEC. + // Badly signed zone or a bug? + isc_throw(DataSourceError, "No NSEC in " + + coverName.toText() + ", but it was " + "returned as previous - " + "accessor error? Badly signed zone?"); + } + } + catch (const isc::NotImplemented&) { + // Well, they want DNSSEC, but there is no available. + // So we don't provide anything. + LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED). + arg(accessor_->getDBName()).arg(name); + } + // We didn't find it, return nothing + return (RRsetPtr()); +} + ZoneFinder::FindResult DatabaseClient::Finder::find(const isc::dns::Name& name, const isc::dns::RRType& type, @@ -381,9 +411,6 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, // This is how many labels we remove to get origin size_t remove_labels(current_label_count - origin_label_count); - // Type shortcut, used a lot here - typedef std::map::const_iterator FoundIterator; - // Now go trough all superdomains from origin down for (int i(remove_labels); i > 0; --i) { Name superdomain(name.split(i)); @@ -563,24 +590,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, arg(accessor_->getDBName()).arg(wildcard). arg(name); if (dnssec_data) { - // Which one should contain the NSEC record? - const Name - coverName(findPreviousName(Name(wildcard))); - // Get the record and copy it out - found = getRRsets(coverName.toText(), NSEC_TYPES(), - true); - const FoundIterator - nci(found.second.find(RRType::NSEC())); - if (nci != found.second.end()) { + result_rrset = findNSECCover(Name(wildcard)); + if (result_rrset) { result_status = WILDCARD_NXRRSET; - result_rrset = nci->second; - } else { - // The previous doesn't contain NSEC. - // Badly signed zone or a bug? - isc_throw(DataSourceError, "No NSEC in " + - coverName.toText() + ", but it was " - "returned as previous - " - "accessor error? Badly signed zone?"); } } break; @@ -608,30 +620,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name, if (result_status == SUCCESS) { // Should we look for NSEC covering the name? if (get_cover) { - try { - // Which one should contain the NSEC record? - const Name coverName(findPreviousName(name)); - // Get the record and copy it out - found = getRRsets(coverName.toText(), NSEC_TYPES(), true); - const FoundIterator - nci(found.second.find(RRType::NSEC())); - if (nci != found.second.end()) { - result_status = NXDOMAIN; - result_rrset = nci->second; - } else { - // The previous doesn't contain NSEC. - // Badly signed zone or a bug? - isc_throw(DataSourceError, "No NSEC in " + - coverName.toText() + ", but it was " - "returned as previous - " - "accessor error? Badly signed zone?"); - } - } - catch (const isc::NotImplemented&) { - // Well, they want DNSSEC, but there is no available. - // So we don't provide anything. - LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED). - arg(accessor_->getDBName()).arg(name); + result_rrset = findNSECCover(name); + if (result_rrset) { + result_status = NXDOMAIN; } } // Something is not here and we didn't decide yet what diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index 5e66f336fa..8295779a2c 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -691,6 +691,23 @@ public: * \param name The domain to check. */ bool hasSubdomains(const std::string& name); + + /** + * \brief Get the NSEC covering a name. + * + * This one calls findPreviousName on the given name and extracts an NSEC + * record on the result. It handles various error cases. The method exists + * to share code present at more than one location. + */ + dns::RRsetPtr findNSECCover(const dns::Name& name); + + /** + * \brief Convenience type shortcut. + * + * To find stuff in the result of getRRsets. + */ + typedef std::map::const_iterator + FoundIterator; }; /** From 17d9827aa40e363650d1698fddba9204f27b5171 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 26 Sep 2011 12:52:15 +0200 Subject: [PATCH 792/974] [1177] Bugfix: don't check for NS-alone in apex --- src/lib/datasrc/database.cc | 3 ++- src/lib/datasrc/tests/database_unittest.cc | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 10da5ffe4b..9a2265d4bf 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -357,7 +357,8 @@ DatabaseClient::Finder::findNSECCover(const Name& name) { // Which one should contain the NSEC record? const Name coverName(findPreviousName(name)); // Get the record and copy it out - FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(), true); + FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(), + coverName != getOrigin()); const FoundIterator nci(found.second.find(RRType::NSEC())); if (nci != found.second.end()) { diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 69151cecb0..fe2c60a10a 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -156,6 +156,9 @@ const char* const TEST_RECORDS[][5] = { // doesn't break anything {"example.org.", "NS", "3600", "", "ns.example.com."}, {"example.org.", "A", "3600", "", "192.0.2.1"}, + {"example.org.", "NSEC", "3600", "", "acnamesig1.example.org. NS A NSEC RRSIG"}, + {"example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 " + "20000201000000 12345 example.org. FAKEFAKEFAKE"}, {"example.org.", "RRSIG", "3600", "", "NS 5 3 3600 20000101000000 " "20000201000000 12345 example.org. FAKEFAKEFAKE"}, @@ -558,6 +561,8 @@ public: } else if (id == 42) { if (rname == "org.example.nonterminal.") { return ("l.example.org."); + } else if (rname == "org.example.aa.") { + return ("example.org."); } else if (rname == "org.example.www2." || rname == "org.example.www1.") { return ("www.example.org."); @@ -1673,6 +1678,15 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) { this->rrttl_, ZoneFinder::NXDOMAIN, this->expected_rdatas_, this->expected_sig_rdatas_, Name("www.example.org."), ZoneFinder::FIND_DNSSEC); + this->expected_rdatas_.clear(); + this->expected_rdatas_.push_back("acnamesig1.example.org. NS A NSEC RRSIG"); + // This tests it works correctly in apex (there was a bug, where a check + // for NS-alone was there and it would throw). + doFindTest(*finder, isc::dns::Name("aa.example.org."), + isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), + this->rrttl_, ZoneFinder::NXDOMAIN, + this->expected_rdatas_, this->expected_sig_rdatas_, + Name("example.org."), ZoneFinder::FIND_DNSSEC); // Check that if the DB doesn't support it, the exception from there // is not propagated and it only does not include the NSEC From 5cf1b7ab58c42675c1396fbbd5b1aaf037eb8d19 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 26 Sep 2011 13:04:18 +0200 Subject: [PATCH 793/974] [1177] Don't throw on missing NSEC Because it can't be distinguished from unsigned zone for now. Should be temporary solution for now. --- src/lib/datasrc/database.cc | 7 +++++++ src/lib/datasrc/tests/database_unittest.cc | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 9a2265d4bf..a8c6bae857 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -366,10 +366,17 @@ DatabaseClient::Finder::findNSECCover(const Name& name) { } else { // The previous doesn't contain NSEC. // Badly signed zone or a bug? + + // FIXME: Currently, if the zone is not signed, we could get + // here. In that case we can't really throw, but for now, we can't + // recognize it. So we don't throw at all, enable it once + // we have a is_signed flag or something. +#if 0 isc_throw(DataSourceError, "No NSEC in " + coverName.toText() + ", but it was " "returned as previous - " "accessor error? Badly signed zone?"); +#endif } } catch (const isc::NotImplemented&) { diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index fe2c60a10a..f554d1be53 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -2368,9 +2368,19 @@ TYPED_TEST(DatabaseClientTest, invalidRdata) { TEST_F(MockDatabaseClientTest, missingNSEC) { shared_ptr finder(this->getFinder()); + /* + * FIXME: For now, we can't really distinguish this bogus input + * from not-signed zone so we can't throw. But once we can, + * enable the original test. + */ +#if 0 EXPECT_THROW(finder->find(Name("badnsec2.example.org."), RRType::A(), NULL, ZoneFinder::FIND_DNSSEC), DataSourceError); +#endif + doFindTest(*finder, Name("badnsec2.example.org."), RRType::A(), + RRType::A(), this->rrttl_, ZoneFinder::NXDOMAIN, + this->expected_rdatas_, this->expected_sig_rdatas_); } TEST_F(MockDatabaseClientTest, badName) { From b8e90124c19177e0b6b33bd624e244860e2424b3 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 26 Sep 2011 13:12:52 +0200 Subject: [PATCH 794/974] [1177] Tests for names before origin --- src/lib/datasrc/tests/database_unittest.cc | 7 +++++++ src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index f554d1be53..94ae022807 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -2353,6 +2353,13 @@ TYPED_TEST(DatabaseClientTest, previous) { EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")), isc::NotImplemented); + } else { + // No need to test this on mock one, because we test only that + // the exception gets through + + // A name before the origin + EXPECT_THROW(finder->findPreviousName(Name("example.com")), + isc::NotImplemented); } } diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 87708c7bcd..3974977553 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -375,6 +375,11 @@ TEST_F(SQLite3AccessorTest, findPrevious) { // be skipped. EXPECT_EQ("example.com.", accessor->findPreviousName(1, "com.example.cname-ext.")); + // Throw when we are before the origin + EXPECT_THROW(accessor->findPreviousName(1, "com.example."), + isc::NotImplemented); + EXPECT_THROW(accessor->findPreviousName(1, "a.example."), + isc::NotImplemented); } TEST_F(SQLite3AccessorTest, findPreviousNoData) { From d36ded7d95a695f0412f6ccdb59bf55fc600e9d3 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 26 Sep 2011 13:18:49 +0200 Subject: [PATCH 795/974] [1177] More details about returned NSEC in doc --- src/lib/datasrc/zone.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 89e4003b6c..6b74b5a197 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -62,6 +62,17 @@ public: /// we need to add one proving there's no exact match and this is /// actually the best wildcard we have). Data sources that don't /// support DNSSEC don't need to distinguish them. + /// + /// In case of NXRRSET related results, the returned NSEC record + /// belongs to the domain which would provide the result if it + /// contained the correct type (in case of NXRRSET, it is the queried + /// domain, in case of WILDCARD_NXRRSET, it is the wildcard domain + /// that matched the query name). In case of empty nonterminal cases, + /// an NSEC is provided for the interval where the empty nonterminal + /// lives, which is the one ending in the subdomain of the empty + /// nonterminal. + /// + /// In case of NXDOMAIN, the returned NSEC covers the queried domain. enum Result { SUCCESS, ///< An exact match is found. DELEGATION, ///< The search encounters a zone cut. From 688867daa34ade5075443c77535f80e1d2d76743 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 26 Sep 2011 11:21:45 -0700 Subject: [PATCH 796/974] [1177] more constify --- src/lib/datasrc/database.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index a8c6bae857..e476297885 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -357,8 +357,8 @@ DatabaseClient::Finder::findNSECCover(const Name& name) { // Which one should contain the NSEC record? const Name coverName(findPreviousName(name)); // Get the record and copy it out - FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(), - coverName != getOrigin()); + const FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(), + coverName != getOrigin()); const FoundIterator nci(found.second.find(RRType::NSEC())); if (nci != found.second.end()) { From c5d5522f83888a8b442aa7ff17738f3f688749fe Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 26 Sep 2011 15:23:03 -0700 Subject: [PATCH 797/974] [1165] add configuration items for zone_config. query_acl was renamed to transfer_acl (for consistency) and is now used as the default transfer ACL. related tests were added. also removed DEFAULT_RRCLASS, and retrieved the default RR class from the spec. --- src/bin/xfrout/tests/Makefile.am | 3 ++ src/bin/xfrout/tests/xfrout_test.py.in | 19 +++++++----- src/bin/xfrout/xfrout.py.in | 27 ++++++++--------- src/bin/xfrout/xfrout.spec.pre.in | 41 +++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index 255478a5ee..7e58cc98b6 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -10,6 +10,8 @@ LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/. endif # test using command-line arguments, so use check-local target instead of TESTS +# We set B10_FROM_BUILD below, so that the test can refer to the in-source +# spec file. check-local: if ENABLE_PYTHON_COVERAGE touch $(abs_top_srcdir)/.coverage @@ -19,6 +21,7 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ chmod +x $(abs_builddir)/$$pytest ; \ + B10_FROM_BUILD=$(abs_top_builddir) \ $(LIBRARY_PATH_PLACEHOLDER) \ PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/xfrout:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \ diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index d6af2c987e..bbe49e9b03 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -20,6 +20,7 @@ import unittest import os from isc.testutils.tsigctx_mock import MockTSIGContext from isc.cc.session import * +import isc.config from pydnspp import * from xfrout import * import xfrout @@ -241,7 +242,7 @@ class TestXfroutSession(unittest.TestCase): # ACL check with a per zone ACL + default ACL. The per zone ACL # should match the queryied zone, so it should be used. def acl_setter(acl): - zone_key = ('example.com.', 'IN') + zone_key = ('IN', 'example.com.') self.xfrsess._zone_config[zone_key] = {} self.xfrsess._zone_config[zone_key]['transfer_acl'] = acl self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ @@ -252,7 +253,7 @@ class TestXfroutSession(unittest.TestCase): # similar to the previous one, but the per zone doesn't match the # query. The default should be used. def acl_setter(acl): - zone_key = ('example.org.', 'IN') + zone_key = ('IN', 'example.org.') self.xfrsess._zone_config[zone_key] = {} self.xfrsess._zone_config[zone_key]['transfer_acl'] = \ isc.acl.dns.REQUEST_LOADER.load([ @@ -273,8 +274,8 @@ class TestXfroutSession(unittest.TestCase): # will still be used. com_acl = isc.acl.dns.REQUEST_LOADER.load([ {"from": "127.0.0.1", "action": "REJECT"}]) - self.xfrsess._zone_config[('example.com.', 'IN')] = {} - self.xfrsess._zone_config[('example.com.', 'IN')]['transfer_acl'] = \ + self.xfrsess._zone_config[('IN', 'example.com.')] = {} + self.xfrsess._zone_config[('IN', 'example.com.')]['transfer_acl'] = \ com_acl self.assertEqual(com_acl, self.xfrsess._get_transfer_acl(Name('example.com'), @@ -639,9 +640,11 @@ class TestXfroutSession(unittest.TestCase): # and it should not have sent anything else self.assertEqual(0, len(self.sock.sendqueue)) -class MyCCSession(): +class MyCCSession(isc.config.ConfigData): def __init__(self): - pass + module_spec = isc.config.module_spec_from_file( + xfrout.SPECFILE_LOCATION) + ConfigData.__init__(self, module_spec) def get_remote_config_value(self, module_name, identifier): if module_name == "Auth" and identifier == "database_file": @@ -738,13 +741,13 @@ class TestUnixSockServer(unittest.TestCase): self.assertEqual(self.unix.tsig_key_ring.size(), 0) # Load the ACL - self.unix.update_config_data({'query_acl': [{'from': '127.0.0.1', + self.unix.update_config_data({'transfer_acl': [{'from': '127.0.0.1', 'action': 'ACCEPT'}]}) self.check_loaded_ACL(self.unix._acl) # Pass a wrong data there and check it does not replace the old one self.assertRaises(isc.acl.acl.LoaderError, self.unix.update_config_data, - {'query_acl': ['Something bad']}) + {'transfer_acl': ['Something bad']}) self.check_loaded_ACL(self.unix._acl) def test_zone_config_data(self): diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 44422c03fb..31ec374337 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -86,10 +86,6 @@ TSIG_SIGN_EVERY_NTH = 96 XFROUT_MAX_MESSAGE_SIZE = 65535 -# In practice, RR class is almost always fixed, so if and when we allow -# it to be configured, it's convenient to make it optional. -DEFAULT_RRCLASS = RRClass.IN() - def get_rrset_len(rrset): """Returns the wire length of the given RRset""" bytes = bytearray() @@ -182,7 +178,7 @@ class XfroutSession(): # Internally zone names are managed in lower cased label characters, # so we first need to convert the name. zone_name_lower = Name(zone_name.to_text(), True) - config_key = (zone_name_lower.to_text(), zone_class.to_text()) + config_key = (zone_class.to_text(), zone_name_lower.to_text()) if config_key in self._zone_config and \ 'transfer_acl' in self._zone_config[config_key]: return self._zone_config[config_key]['transfer_acl'] @@ -417,8 +413,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, self._shutdown_event = shutdown_event self._write_sock, self._read_sock = socket.socketpair() self._common_init() - self.update_config_data(config_data) self._cc = cc + self.update_config_data(config_data) def _common_init(self): self._lock = threading.Lock() @@ -586,11 +582,11 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, remain. This should be fixed. ''' logger.info(XFROUT_NEW_CONFIG) - if 'query_acl' in new_config: - self._acl = REQUEST_LOADER.load(new_config['query_acl']) - if 'zone_config' in new_config: - self._zone_config = \ - self.__create_zone_config(new_config.get('zone_config')) + if 'transfer_acl' in new_config: + self._acl = REQUEST_LOADER.load(new_config['transfer_acl']) + zone_config = new_config.get('zone_config') + if zone_config is not None: + self._zone_config = self.__create_zone_config(zone_config) self._lock.acquire() self._max_transfers_out = new_config.get('transfers_out') self.set_tsig_key_ring(new_config.get('tsig_key_ring')) @@ -602,10 +598,11 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, for zconf in zone_config_list: # convert the class, origin (name) pair. First build pydnspp # object to reject invalid input. - if 'class' in zconf: - zclass = RRClass(zconf['class']) - else: - zclass = DEFAULT_RRCLASS + zclass_str = zconf.get('class') + if zclass_str is None: + #zclass_str = 'IN' # temporary + zclass_str = self._cc.get_default_value('zone_config/class') + zclass = RRClass(zclass_str) zorigin = Name(zconf['origin'], True) config_key = (zclass.to_text(), zorigin.to_text()) diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in index 8ecbb0b92b..b51312beb6 100644 --- a/src/bin/xfrout/xfrout.spec.pre.in +++ b/src/bin/xfrout/xfrout.spec.pre.in @@ -51,7 +51,7 @@ } }, { - "item_name": "query_acl", + "item_name": "transfer_acl", "item_type": "list", "item_optional": false, "item_default": [{"action": "ACCEPT"}], @@ -61,6 +61,45 @@ "item_type": "any", "item_optional": true } + }, + { + "item_name": "zone_config", + "item_type": "list", + "item_optional": true, + "item_default": [], + "list_item_spec": + { + "item_name": "zone_config_element", + "item_type": "map", + "item_optional": true, + "item_default": { "origin": "" }, + "map_item_spec": [ + { + "item_name": "origin", + "item_type": "string", + "item_optional": false, + "item_default": "" + }, + { + "item_name": "class", + "item_type": "string", + "item_optional": false, + "item_default": "IN" + }, + { + "item_name": "transfer_acl", + "item_type": "list", + "item_optional": true, + "item_default": [{"action": "ACCEPT"}], + "list_item_spec": + { + "item_name": "acl_element", + "item_type": "any", + "item_optional": true + } + } + ] + } } ], "commands": [ From 1e32824c93dac7e406d1b35449b42700bf854679 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 26 Sep 2011 16:17:13 -0700 Subject: [PATCH 798/974] [1165] catch some exceptions that could be raised due to configuration error and log messages for them. this is necessary to gracefully die due to broken config data in b10-config.db at startup time. --- src/bin/xfrout/tests/xfrout_test.py.in | 6 ++--- src/bin/xfrout/xfrout.py.in | 37 +++++++++++++++++++++----- src/bin/xfrout/xfrout_messages.mes | 11 ++++++++ 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index bbe49e9b03..6d314885ae 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -745,7 +745,7 @@ class TestUnixSockServer(unittest.TestCase): 'action': 'ACCEPT'}]}) self.check_loaded_ACL(self.unix._acl) # Pass a wrong data there and check it does not replace the old one - self.assertRaises(isc.acl.acl.LoaderError, + self.assertRaises(XfroutConfigError, self.unix.update_config_data, {'transfer_acl': ['Something bad']}) self.check_loaded_ACL(self.unix._acl) @@ -792,7 +792,7 @@ class TestUnixSockServer(unittest.TestCase): self.assertEqual({}, self.unix._zone_config[('IN', 'example.org.')]) # Duplicate data: should be rejected with an exception - self.assertRaises(ValueError, + self.assertRaises(XfroutConfigError, self.unix.update_config_data, {'zone_config': [{'origin': 'example.com'}, {'origin': 'example.org'}, @@ -809,7 +809,7 @@ class TestUnixSockServer(unittest.TestCase): self.check_loaded_ACL(acl) # invalid ACL syntax will be rejected with exception - self.assertRaises(isc.acl.acl.LoaderError, + self.assertRaises(XfroutConfigError, self.unix.update_config_data, {'zone_config': [{'origin': 'example.com', 'transfer_acl': diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 31ec374337..e64f6961a3 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -48,11 +48,23 @@ except ImportError as e: # must keep running, so we warn about it and move forward. log.error(XFROUT_IMPORT, str(e)) -from isc.acl.acl import ACCEPT, REJECT, DROP +from isc.acl.acl import ACCEPT, REJECT, DROP, LoaderError from isc.acl.dns import REQUEST_LOADER isc.util.process.rename() +class XfroutConfigError(Exception): + """An exception indicating an error in updating xfrout configuration. + + This exception is raised when the xfrout process encouters an error in + handling configuration updates. Not all syntax error can be caught + at the module-CC layer, so xfrout needs to (explicitly or implicitly) + validate the given configuration data itself. When it finds an error + it raises this exception (either directly or by converting an exception + from other modules) as a unified error in configuration. + """ + pass + def init_paths(): global SPECFILE_PATH global AUTH_SPECFILE_PATH @@ -583,7 +595,11 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ''' logger.info(XFROUT_NEW_CONFIG) if 'transfer_acl' in new_config: - self._acl = REQUEST_LOADER.load(new_config['transfer_acl']) + try: + self._acl = REQUEST_LOADER.load(new_config['transfer_acl']) + except LoaderError as e: + raise XfroutConfigError('Failed to parse transfer_acl: ' + + str(e)) zone_config = new_config.get('zone_config') if zone_config is not None: self._zone_config = self.__create_zone_config(zone_config) @@ -608,14 +624,19 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, # reject duplicate config if config_key in new_config: - raise ValueError('Duplicaet zone_config for ' + - str(zorigin) + '/' + str(zclass)) + raise XfroutConfigError('Duplicaet zone_config for ' + + str(zorigin) + '/' + str(zclass)) # create a new config entry, build any given (and known) config new_config[config_key] = {} if 'transfer_acl' in zconf: - new_config[config_key]['transfer_acl'] = \ - REQUEST_LOADER.load(zconf['transfer_acl']) + try: + new_config[config_key]['transfer_acl'] = \ + REQUEST_LOADER.load(zconf['transfer_acl']) + except LoaderError as e: + raise XfroutConfigError('Failed to parse transfer_acl ' + + 'for ' + zorigin.to_text() + '/' + + zclass_str + ': ' + str(e)) return new_config def set_tsig_key_ring(self, key_list): @@ -785,6 +806,10 @@ if '__main__' == __name__: logger.INFO(XFROUT_STOPPED_BY_KEYBOARD) except SessionError as e: logger.error(XFROUT_CC_SESSION_ERROR, str(e)) + except ModuleCCSessionError as e: + logger.error(XFROUT_MODULECC_SESSION_ERROR, str(e)) + except XfroutConfigError as e: + logger.error(XFROUT_CONFIG_ERROR, str(e)) except SessionTimeout as e: logger.error(XFROUT_CC_SESSION_TIMEOUT_ERROR) diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes index 121b2adf4b..c2d3c7fa6b 100644 --- a/src/bin/xfrout/xfrout_messages.mes +++ b/src/bin/xfrout/xfrout_messages.mes @@ -47,6 +47,17 @@ a valid TSIG key. There was a problem reading from the command and control channel. The most likely cause is that the msgq daemon is not running. +% XFROUT_MODULECC_SESSION_ERROR error from module config/command module: %1 +There was a problem in the lower level module handling configuration and +control commands. This could happen for various reasons, but the most likely +cause is that the configuration database contains a syntax error and xfrout +fails to start at initialization. Details error message from the module +will also be displayed. + +% XFROUT_CONFIG_ERROR error found in configuration data: %1 +The xfrout process encoutered an error in installing configuration at +startup time. Error details are included in the log message. + % XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response There was a problem reading a response from another module over the command and control channel. The most likely cause is that the From ed8d686171f140fd12164d2d34f65b4ab3c97645 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 26 Sep 2011 23:06:18 -0700 Subject: [PATCH 799/974] [1165] a small (not really related to the task though) refactoring: simply use the spec default values in initialization of UnixSockServer. this way we can avoid redundant initialization. --- src/bin/xfrout/tests/xfrout_test.py.in | 4 ++-- src/bin/xfrout/xfrout.py.in | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index 6d314885ae..85979a012e 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -656,9 +656,9 @@ class MyCCSession(isc.config.ConfigData): class MyUnixSockServer(UnixSockServer): def __init__(self): self._shutdown_event = threading.Event() - self._max_transfers_out = 10 - self._cc = MyCCSession() self._common_init() + self._cc = MyCCSession() + self.update_config_data(self._cc.get_full_config()) class TestUnixSockServer(unittest.TestCase): def setUp(self): diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index e64f6961a3..e36e807f44 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -91,7 +91,6 @@ init_paths() SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec" AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec" -MAX_TRANSFERS_OUT = 10 VERBOSE_MODE = False # tsig sign every N axfr packets. TSIG_SIGN_EVERY_NTH = 96 @@ -429,13 +428,9 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, self.update_config_data(config_data) def _common_init(self): + '''Initialization shared with the mock server class used for tests''' self._lock = threading.Lock() self._transfers_counter = 0 - # These default values will probably get overwritten by the (same) - # default value from the spec file. These are here just to make - # sure and to make the default values in tests consistent. - self._acl = REQUEST_LOADER.load('[{"action": "ACCEPT"}]') - self._zone_config = {} def _receive_query_message(self, sock): ''' receive request message from sock''' From 40cd22fc64c7755efe60cd42cb12851cf3de55a4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 26 Sep 2011 23:29:01 -0700 Subject: [PATCH 800/974] [1165] recover the strong exception guarantee in update_config_data() (I first thought it could be deferred, but since the exception can happen just due to incorrect config input, it should be guaranteed from the beginning.) Also made sure that both the default ACL and zone config are changed atomically under the protection of lock. --- src/bin/xfrout/xfrout.py.in | 47 +++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index e36e807f44..d3d385f995 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -431,6 +431,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, '''Initialization shared with the mock server class used for tests''' self._lock = threading.Lock() self._transfers_counter = 0 + self._zone_config = {} + self._acl = None # this will be initialized in update_config_data() def _receive_query_message(self, sock): ''' receive request message from sock''' @@ -536,10 +538,13 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, This method creates a XfroutSession object. ''' + self._lock.acquire() + acl = self._acl + zone_config = self._zone_config + self._lock.release() self.RequestHandlerClass(sock_fd, request_data, self, self.tsig_key_ring, - self._guess_remote(sock_fd), self._acl, - self._zone_config) + self._guess_remote(sock_fd), acl, zone_config) def _remove_unused_sock_file(self, sock_file): '''Try to remove the socket file. If the file is being used @@ -583,24 +588,30 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, def update_config_data(self, new_config): '''Apply the new config setting of xfrout module. - Note: this method does not provide strong exception guarantee; - if an exception is raised in the middle of parsing and building the - given config data, the incomplete set of new configuration will - remain. This should be fixed. ''' - logger.info(XFROUT_NEW_CONFIG) - if 'transfer_acl' in new_config: - try: - self._acl = REQUEST_LOADER.load(new_config['transfer_acl']) - except LoaderError as e: - raise XfroutConfigError('Failed to parse transfer_acl: ' + - str(e)) - zone_config = new_config.get('zone_config') - if zone_config is not None: - self._zone_config = self.__create_zone_config(zone_config) self._lock.acquire() - self._max_transfers_out = new_config.get('transfers_out') - self.set_tsig_key_ring(new_config.get('tsig_key_ring')) + try: + logger.info(XFROUT_NEW_CONFIG) + new_acl = self._acl + if 'transfer_acl' in new_config: + try: + new_acl = REQUEST_LOADER.load(new_config['transfer_acl']) + except LoaderError as e: + raise XfroutConfigError('Failed to parse transfer_acl: ' + + str(e)) + + new_zone_config = self._zone_config + zconfig_data = new_config.get('zone_config') + if zconfig_data is not None: + new_zone_config = self.__create_zone_config(zconfig_data) + + self._acl = new_acl + self._zone_config = new_zone_config + self._max_transfers_out = new_config.get('transfers_out') + self.set_tsig_key_ring(new_config.get('tsig_key_ring')) + except Exception as e: + self._lock.release() + raise e self._lock.release() logger.info(XFROUT_NEW_CONFIG_DONE) From 7751d0ac43f1b7186a53ba5dd5cf2eeca6f7dc46 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 17:33:08 +0200 Subject: [PATCH 801/974] [1206] initial hardcoded no checking factory function --- src/lib/datasrc/Makefile.am | 1 + src/lib/datasrc/factory.cc | 45 +++++++++++++++++++++++++++++++ src/lib/datasrc/factory.h | 40 +++++++++++++++++++++++++++ src/lib/datasrc/tests/Makefile.am | 1 + 4 files changed, 87 insertions(+) create mode 100644 src/lib/datasrc/factory.cc create mode 100644 src/lib/datasrc/factory.h diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index 8f8a5ce212..f588744256 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -24,6 +24,7 @@ libdatasrc_la_SOURCES += logger.h logger.cc libdatasrc_la_SOURCES += client.h iterator.h libdatasrc_la_SOURCES += database.h database.cc libdatasrc_la_SOURCES += sqlite3_accessor.h sqlite3_accessor.cc +libdatasrc_la_SOURCES += factory.h factory.cc nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc new file mode 100644 index 0000000000..a75ac76b31 --- /dev/null +++ b/src/lib/datasrc/factory.cc @@ -0,0 +1,45 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include "factory.h" + +#include "data_source.h" +#include "database.h" +#include "sqlite3_accessor.h" +#include "memory_datasrc.h" + +namespace isc { +namespace datasrc { + +boost::shared_ptr +createDataSourceClient(const std::string& type, + const isc::dns::RRClass& rrclass, + isc::data::ConstElementPtr config) { + // For now, mapping hardcoded + // config is assumed to be ok + if (type == "sqlite3") { + boost::shared_ptr sqlite3_accessor( + new SQLite3Accessor(config->get("dbfile")->stringValue(), rrclass)); + return boost::shared_ptr( + new DatabaseClient(rrclass, sqlite3_accessor)); + } else if (type == "memory") { + return boost::shared_ptr(new InMemoryClient()); + } else { + isc_throw(DataSourceError, "Unknown datasource type: " << type); + } +} + +} // end namespace datasrc +} // end namespace isc + diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h new file mode 100644 index 0000000000..18dea4b2d8 --- /dev/null +++ b/src/lib/datasrc/factory.h @@ -0,0 +1,40 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#ifndef __DATA_SOURCE_FACTORY_H +#define __DATA_SOURCE_FACTORY_H 1 + +//#include +//#include + +//#include + +#include + +#include + +namespace isc { +namespace datasrc { + +boost::shared_ptr +createDataSourceClient(const std::string& type, + const isc::dns::RRClass& rrclass, + isc::data::ConstElementPtr config); + +} +} +#endif // DATA_SOURCE_FACTORY_H +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index 48cbc7697f..36e678821f 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -35,6 +35,7 @@ run_unittests_SOURCES += logger_unittest.cc run_unittests_SOURCES += database_unittest.cc run_unittests_SOURCES += client_unittest.cc run_unittests_SOURCES += sqlite3_accessor_unittest.cc +run_unittests_SOURCES += factory_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) From 8f9f4ece764df4607f695f3f7eb4c421e8ac4c9d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 21 Sep 2011 21:16:03 +0200 Subject: [PATCH 802/974] [1206] some functionality for config --- src/lib/datasrc/factory.cc | 80 ++++++++++++++++++++++++++++++++++---- src/lib/datasrc/factory.h | 30 +++++++++++++- 2 files changed, 101 insertions(+), 9 deletions(-) diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index a75ac76b31..48adc2f480 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -19,22 +19,88 @@ #include "sqlite3_accessor.h" #include "memory_datasrc.h" +using namespace isc::data; +using namespace isc::datasrc; + +namespace { +// This initial implementation hard codes specific details. These functions +// should be moved to their corresponding backend libs if we plan on making +// them dynamically loadable + +void +addError(ElementPtr errors, const std::string& error) { + if (errors != ElementPtr() && errors->getType() == Element::list) { + errors->add(Element::create(error)); + } +} + +bool +sqlite3CheckConfig(ConstElementPtr config, ElementPtr errors) { + bool result = true; + if (config->getType() != Element::map) { + addError(errors, "Base config for SQlite3 backend must be a map"); + result = false; + if (!config->contains("file")) { + addError(errors, + "Config for SQlite3 backend does not contain a 'file' value"); + result = false; + } else if (config->get("file")->getType() != Element::string) { + addError(errors, "file value in SQLite3 backend is not a string"); + result = false; + } else if (config->get("file")->stringValue() == "") { + addError(errors, "file value in SQLite3 backend is empty"); + result = false; + } + + if (!config->contains("class")) { + addError(errors, "Config for SQlite3 backend does not contain a 'class' value"); + result = false; + } else if (config->get("class")->getType() != Element::string) { + addError(errors, "class value in SQLite3 backend is not a string"); + result = false; + } else { + try { + isc::dns::RRClass rrclass(config->get("class")->stringValue()); + } catch (const isc::dns::InvalidRRClass& ivrc) { + addError(errors, ivrc.what()); + result = false; + } catch (const isc::dns::IncompleteRRClass& icrc) { + addError(errors, icrc.what()); + result = false; + } + } + } + + return (result); +} + +DataSourceClient * +sqlite3CreateInstance(isc::data::ConstElementPtr config) { + ElementPtr errors; + if (!sqlite3CheckConfig(config, errors)) { + isc_throw(DataSourceConfigError, errors->str()); + } + isc::dns::RRClass rrclass(config->get("class")->stringValue()); + std::string dbfile = config->get("file")->stringValue(); + boost::shared_ptr sqlite3_accessor( + new SQLite3Accessor(dbfile, rrclass)); + return (new DatabaseClient(rrclass, sqlite3_accessor)); +} + +} // end anonymous namespace + namespace isc { namespace datasrc { -boost::shared_ptr +DataSourceClient * createDataSourceClient(const std::string& type, - const isc::dns::RRClass& rrclass, isc::data::ConstElementPtr config) { // For now, mapping hardcoded // config is assumed to be ok if (type == "sqlite3") { - boost::shared_ptr sqlite3_accessor( - new SQLite3Accessor(config->get("dbfile")->stringValue(), rrclass)); - return boost::shared_ptr( - new DatabaseClient(rrclass, sqlite3_accessor)); + return (sqlite3CreateInstance(config)); } else if (type == "memory") { - return boost::shared_ptr(new InMemoryClient()); + return (new InMemoryClient()); } else { isc_throw(DataSourceError, "Unknown datasource type: " << type); } diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index 18dea4b2d8..7afb15c9f4 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -21,15 +21,41 @@ //#include #include +#include #include namespace isc { namespace datasrc { -boost::shared_ptr +/// \brief Raised if the given config contains bad data +/// +/// Depending on the datasource type, the configuration may differ (for +/// instance, the sqlite3 datasource needs a database file). +class DataSourceConfigError : public isc::Exception { +public: + DataSourceConfigError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// \brief Create a datasource instance +/// +/// This function is a fixed generator for datasource instances of all types. +/// +/// Currently, the different types are hardcoded in the implementation of this +/// function. However, we plan on making it more flexible, possibly through +/// th +/// +/// \note This function returns a raw pointer. The caller is expected to +/// delete this pointer again. We don't return any specific smart +/// pointer for flexibility. However, we highly advice that the +/// return value of this function is directly put into a shared or +/// scoped pointer. +/// +/// \exception DataSourceConfigError if the given configuration values are +/// bad for the given datasource type +DataSourceClient* createDataSourceClient(const std::string& type, - const isc::dns::RRClass& rrclass, isc::data::ConstElementPtr config); } From f0ef6c88066961a038ea1b80face4feaa9a2d17d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 22 Sep 2011 14:39:23 +0200 Subject: [PATCH 803/974] [1206] additional checks and tests --- src/lib/datasrc/factory.cc | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index 48adc2f480..2233466e7a 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -37,14 +37,16 @@ addError(ElementPtr errors, const std::string& error) { bool sqlite3CheckConfig(ConstElementPtr config, ElementPtr errors) { bool result = true; - if (config->getType() != Element::map) { + if (!config || config->getType() != Element::map) { addError(errors, "Base config for SQlite3 backend must be a map"); result = false; + } else { if (!config->contains("file")) { addError(errors, "Config for SQlite3 backend does not contain a 'file' value"); result = false; - } else if (config->get("file")->getType() != Element::string) { + } else if (!config->get("file") || + config->get("file")->getType() != Element::string) { addError(errors, "file value in SQLite3 backend is not a string"); result = false; } else if (config->get("file")->stringValue() == "") { @@ -55,7 +57,8 @@ sqlite3CheckConfig(ConstElementPtr config, ElementPtr errors) { if (!config->contains("class")) { addError(errors, "Config for SQlite3 backend does not contain a 'class' value"); result = false; - } else if (config->get("class")->getType() != Element::string) { + } else if (!config->get("class") || + config->get("class")->getType() != Element::string) { addError(errors, "class value in SQLite3 backend is not a string"); result = false; } else { @@ -76,7 +79,7 @@ sqlite3CheckConfig(ConstElementPtr config, ElementPtr errors) { DataSourceClient * sqlite3CreateInstance(isc::data::ConstElementPtr config) { - ElementPtr errors; + ElementPtr errors(Element::createList()); if (!sqlite3CheckConfig(config, errors)) { isc_throw(DataSourceConfigError, errors->str()); } @@ -87,6 +90,21 @@ sqlite3CreateInstance(isc::data::ConstElementPtr config) { return (new DatabaseClient(rrclass, sqlite3_accessor)); } +bool +memoryCheckConfig(ConstElementPtr, ElementPtr) { + // current inmem has no options (yet) + return true; +} + +DataSourceClient * +memoryCreateInstance(isc::data::ConstElementPtr config) { + ElementPtr errors(Element::createList()); + if (!memoryCheckConfig(config, errors)) { + isc_throw(DataSourceConfigError, errors->str()); + } + return (new InMemoryClient()); +} + } // end anonymous namespace namespace isc { @@ -100,7 +118,7 @@ createDataSourceClient(const std::string& type, if (type == "sqlite3") { return (sqlite3CreateInstance(config)); } else if (type == "memory") { - return (new InMemoryClient()); + return (memoryCreateInstance(config)); } else { isc_throw(DataSourceError, "Unknown datasource type: " << type); } From 07b6398dbd11037eb553fc6fcf56dc8051e71150 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 22 Sep 2011 15:15:12 +0200 Subject: [PATCH 804/974] [1206] initial experiment with dlopened sqlite3 ds --- src/lib/datasrc/Makefile.am | 7 ++- src/lib/datasrc/factory.cc | 16 ++++++- src/lib/datasrc/factory.h | 2 + src/lib/datasrc/sqlite3_accessor.cc | 71 +++++++++++++++++++++++++++++ src/lib/datasrc/sqlite3_accessor.h | 6 +++ src/lib/datasrc/tests/Makefile.am | 3 ++ 6 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index f588744256..204f093ed0 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -9,7 +9,7 @@ AM_CXXFLAGS = $(B10_CXXFLAGS) CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc -lib_LTLIBRARIES = libdatasrc.la +lib_LTLIBRARIES = libdatasrc.la sqlite3_ds.la libdatasrc_la_SOURCES = data_source.h data_source.cc libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc @@ -23,10 +23,13 @@ libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc libdatasrc_la_SOURCES += client.h iterator.h libdatasrc_la_SOURCES += database.h database.cc -libdatasrc_la_SOURCES += sqlite3_accessor.h sqlite3_accessor.cc +#libdatasrc_la_SOURCES += sqlite3_accessor.h sqlite3_accessor.cc libdatasrc_la_SOURCES += factory.h factory.cc nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc +sqlite3_ds_la_SOURCES = sqlite3_accessor.h sqlite3_accessor.cc +sqlite3_ds_la_LDFLAGS = -module + libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la libdatasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la libdatasrc_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index 2233466e7a..27d35a3648 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -19,6 +19,8 @@ #include "sqlite3_accessor.h" #include "memory_datasrc.h" +#include + using namespace isc::data; using namespace isc::datasrc; @@ -116,7 +118,19 @@ createDataSourceClient(const std::string& type, // For now, mapping hardcoded // config is assumed to be ok if (type == "sqlite3") { - return (sqlite3CreateInstance(config)); + void *ds_lib = dlopen("sqlite3_ds.so", RTLD_LAZY); + if (ds_lib == NULL) { + isc_throw(DataSourceError, "Unable to load " << type << + ": " << dlerror()); + } + dlerror(); + ds_creator* ds_create = (ds_creator*)dlsym(ds_lib, "createInstance"); + const char* dlsym_error = dlerror(); + if (dlsym_error != NULL) { + isc_throw(DataSourceError, "Error in library " << type << + ": " << dlsym_error); + } + return (ds_create(config)); } else if (type == "memory") { return (memoryCreateInstance(config)); } else { diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index 7afb15c9f4..5f6d2cb346 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -58,6 +58,8 @@ DataSourceClient* createDataSourceClient(const std::string& type, isc::data::ConstElementPtr config); +typedef DataSourceClient* ds_creator(isc::data::ConstElementPtr config); +typedef void ds_destructor(); } } #endif // DATA_SOURCE_FACTORY_H diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 956f447363..e4c14623e3 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -22,9 +22,11 @@ #include #include #include +#include #include using namespace std; +using namespace isc::data; #define SQLITE_SCHEMA_VERSION 1 @@ -658,5 +660,74 @@ SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) { *dbparameters_, DEL_RECORD, params, "delete record from zone"); } +namespace { +void +addError(ElementPtr errors, const std::string& error) { + if (errors != ElementPtr() && errors->getType() == Element::list) { + errors->add(Element::create(error)); + } +} +} // end anonymous namespace + +bool +checkConfig(ConstElementPtr config, ElementPtr errors) { + bool result = true; + if (!config || config->getType() != Element::map) { + addError(errors, "Base config for SQlite3 backend must be a map"); + result = false; + } else { + if (!config->contains("file")) { + addError(errors, + "Config for SQlite3 backend does not contain a 'file' value"); + result = false; + } else if (!config->get("file") || + config->get("file")->getType() != Element::string) { + addError(errors, "file value in SQLite3 backend is not a string"); + result = false; + } else if (config->get("file")->stringValue() == "") { + addError(errors, "file value in SQLite3 backend is empty"); + result = false; + } + + if (!config->contains("class")) { + addError(errors, "Config for SQlite3 backend does not contain a 'class' value"); + result = false; + } else if (!config->get("class") || + config->get("class")->getType() != Element::string) { + addError(errors, "class value in SQLite3 backend is not a string"); + result = false; + } else { + try { + isc::dns::RRClass rrclass(config->get("class")->stringValue()); + } catch (const isc::dns::InvalidRRClass& ivrc) { + addError(errors, ivrc.what()); + result = false; + } catch (const isc::dns::IncompleteRRClass& icrc) { + addError(errors, icrc.what()); + result = false; + } + } + } + + return (result); +} + +DataSourceClient * +createInstance(isc::data::ConstElementPtr config) { + ElementPtr errors(Element::createList()); + if (!checkConfig(config, errors)) { + isc_throw(DataSourceConfigError, errors->str()); + } + isc::dns::RRClass rrclass(config->get("class")->stringValue()); + std::string dbfile = config->get("file")->stringValue(); + boost::shared_ptr sqlite3_accessor( + new SQLite3Accessor(dbfile, rrclass)); + return (new DatabaseClient(rrclass, sqlite3_accessor)); +} + +void destroyInstance(DataSourceClient* instance) { + delete instance; +} + } } diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index fae249b402..04c5377b7e 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -24,6 +24,8 @@ #include #include +#include + namespace isc { namespace dns { class RRClass; @@ -187,6 +189,10 @@ private: const std::string database_name_; }; +extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr config); + +extern "C" void destroyInstance(DataSourceClient* instance); + } } diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index 36e678821f..c53f94cf82 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -36,6 +36,9 @@ run_unittests_SOURCES += database_unittest.cc run_unittests_SOURCES += client_unittest.cc run_unittests_SOURCES += sqlite3_accessor_unittest.cc run_unittests_SOURCES += factory_unittest.cc +# for the dlopened types we have tests for, we also need to include the +# sources +run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) From 8b4f53f245ab45bf07be9b1108fca951133b836a Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 22 Sep 2011 15:36:50 +0200 Subject: [PATCH 805/974] [1206] remove unused initial code --- src/lib/datasrc/factory.cc | 67 -------------------------------------- 1 file changed, 67 deletions(-) diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index 27d35a3648..7fb0d81dd4 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -25,73 +25,6 @@ using namespace isc::data; using namespace isc::datasrc; namespace { -// This initial implementation hard codes specific details. These functions -// should be moved to their corresponding backend libs if we plan on making -// them dynamically loadable - -void -addError(ElementPtr errors, const std::string& error) { - if (errors != ElementPtr() && errors->getType() == Element::list) { - errors->add(Element::create(error)); - } -} - -bool -sqlite3CheckConfig(ConstElementPtr config, ElementPtr errors) { - bool result = true; - if (!config || config->getType() != Element::map) { - addError(errors, "Base config for SQlite3 backend must be a map"); - result = false; - } else { - if (!config->contains("file")) { - addError(errors, - "Config for SQlite3 backend does not contain a 'file' value"); - result = false; - } else if (!config->get("file") || - config->get("file")->getType() != Element::string) { - addError(errors, "file value in SQLite3 backend is not a string"); - result = false; - } else if (config->get("file")->stringValue() == "") { - addError(errors, "file value in SQLite3 backend is empty"); - result = false; - } - - if (!config->contains("class")) { - addError(errors, "Config for SQlite3 backend does not contain a 'class' value"); - result = false; - } else if (!config->get("class") || - config->get("class")->getType() != Element::string) { - addError(errors, "class value in SQLite3 backend is not a string"); - result = false; - } else { - try { - isc::dns::RRClass rrclass(config->get("class")->stringValue()); - } catch (const isc::dns::InvalidRRClass& ivrc) { - addError(errors, ivrc.what()); - result = false; - } catch (const isc::dns::IncompleteRRClass& icrc) { - addError(errors, icrc.what()); - result = false; - } - } - } - - return (result); -} - -DataSourceClient * -sqlite3CreateInstance(isc::data::ConstElementPtr config) { - ElementPtr errors(Element::createList()); - if (!sqlite3CheckConfig(config, errors)) { - isc_throw(DataSourceConfigError, errors->str()); - } - isc::dns::RRClass rrclass(config->get("class")->stringValue()); - std::string dbfile = config->get("file")->stringValue(); - boost::shared_ptr sqlite3_accessor( - new SQLite3Accessor(dbfile, rrclass)); - return (new DatabaseClient(rrclass, sqlite3_accessor)); -} - bool memoryCheckConfig(ConstElementPtr, ElementPtr) { // current inmem has no options (yet) From 87a3c86e7e132a1ee80bf29b418ad4b61cefc7d8 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 22 Sep 2011 15:39:22 +0200 Subject: [PATCH 806/974] [1206] forgot to add file --- src/lib/datasrc/tests/factory_unittest.cc | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/lib/datasrc/tests/factory_unittest.cc diff --git a/src/lib/datasrc/tests/factory_unittest.cc b/src/lib/datasrc/tests/factory_unittest.cc new file mode 100644 index 0000000000..f9573b95a6 --- /dev/null +++ b/src/lib/datasrc/tests/factory_unittest.cc @@ -0,0 +1,86 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC 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. + +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace isc::datasrc; +using namespace isc::data; + +namespace { + +// The default implementation is NotImplemented +TEST(FactoryTest, memoryClient) { + boost::scoped_ptr client( + createDataSourceClient("memory", ElementPtr())); + + ASSERT_TRUE(client.get() != NULL); +} + +TEST(FactoryTest, badType) { + ASSERT_THROW(createDataSourceClient("foo", ElementPtr()), DataSourceError); +} + +TEST(FactoryTest, sqlite3ClientBadConfig) { + ElementPtr config; + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config = Element::create("asdf"); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config = Element::createMap(); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config->set("class", ElementPtr()); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create(1)); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create("FOO")); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create("IN")); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config->set("file", ElementPtr()); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config->set("file", Element::create(1)); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + DataSourceConfigError); + + config->set("file", Element::create("/foo/bar/doesnotexist")); + ASSERT_THROW(createDataSourceClient("sqlite3", config), + SQLite3Error); +} + +} // end anonymous namespace + From a8b5aabeb7b56702a85344434d7822a034ff140c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 22 Sep 2011 16:53:23 +0200 Subject: [PATCH 807/974] [1206] use a container for dlopened ds so we can clean it up --- src/lib/datasrc/Makefile.am | 7 +- src/lib/datasrc/factory.cc | 94 ++++++++++++++--------- src/lib/datasrc/factory.h | 17 +++- src/lib/datasrc/memory_datasrc.cc | 25 ++++++ src/lib/datasrc/memory_datasrc.h | 10 ++- src/lib/datasrc/sqlite3_accessor.cc | 4 +- src/lib/datasrc/tests/Makefile.am | 5 +- src/lib/datasrc/tests/factory_unittest.cc | 71 +++++++++++++---- 8 files changed, 172 insertions(+), 61 deletions(-) diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index 204f093ed0..7c29d38ffe 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -9,7 +9,7 @@ AM_CXXFLAGS = $(B10_CXXFLAGS) CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc -lib_LTLIBRARIES = libdatasrc.la sqlite3_ds.la +lib_LTLIBRARIES = libdatasrc.la sqlite3_ds.la memory_ds.la libdatasrc_la_SOURCES = data_source.h data_source.cc libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc @@ -17,7 +17,7 @@ libdatasrc_la_SOURCES += query.h query.cc libdatasrc_la_SOURCES += cache.h cache.cc libdatasrc_la_SOURCES += rbtree.h libdatasrc_la_SOURCES += zonetable.h zonetable.cc -libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc +#libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc libdatasrc_la_SOURCES += zone.h libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc @@ -30,6 +30,9 @@ nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc sqlite3_ds_la_SOURCES = sqlite3_accessor.h sqlite3_accessor.cc sqlite3_ds_la_LDFLAGS = -module +memory_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc +memory_ds_la_LDFLAGS = -module + libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la libdatasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la libdatasrc_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index 7fb0d81dd4..4165a7385c 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -24,51 +24,71 @@ using namespace isc::data; using namespace isc::datasrc; -namespace { -bool -memoryCheckConfig(ConstElementPtr, ElementPtr) { - // current inmem has no options (yet) - return true; -} - -DataSourceClient * -memoryCreateInstance(isc::data::ConstElementPtr config) { - ElementPtr errors(Element::createList()); - if (!memoryCheckConfig(config, errors)) { - isc_throw(DataSourceConfigError, errors->str()); - } - return (new InMemoryClient()); -} - -} // end anonymous namespace - namespace isc { namespace datasrc { +DataSourceClientContainer::DataSourceClientContainer(const std::string& type, + ConstElementPtr config) +{ + // The name of the loadable module is type + _ds.so + // config is assumed to be ok + std::string dl_name = type + "_ds.so"; + + ds_lib = dlopen(dl_name.c_str(), RTLD_NOW | RTLD_LOCAL); + if (ds_lib == NULL) { + isc_throw(DataSourceError, "Unable to load " << type << + ": " << dlerror()); + } + dlerror(); + ds_creator* ds_create = (ds_creator*)dlsym(ds_lib, "createInstance"); + const char* dlsym_error = dlerror(); + if (dlsym_error != NULL) { + dlclose(ds_lib); + isc_throw(DataSourceError, "Error in library " << type << + ": " << dlsym_error); + } + try { + instance = ds_create(config); + } catch (...) { + dlclose(ds_lib); + throw; + } + + dlerror(); + destructor = (ds_destructor*)dlsym(ds_lib, "destroyInstance"); + dlsym_error = dlerror(); + if (dlsym_error != NULL) { + dlclose(ds_lib); + isc_throw(DataSourceError, "Error in library " << type << + ": " << dlsym_error); + } +} + +DataSourceClientContainer::~DataSourceClientContainer() { + destructor(instance); + dlclose(ds_lib); +} + DataSourceClient * createDataSourceClient(const std::string& type, isc::data::ConstElementPtr config) { - // For now, mapping hardcoded + // The name of the loadable module is type + _ds.so // config is assumed to be ok - if (type == "sqlite3") { - void *ds_lib = dlopen("sqlite3_ds.so", RTLD_LAZY); - if (ds_lib == NULL) { - isc_throw(DataSourceError, "Unable to load " << type << - ": " << dlerror()); - } - dlerror(); - ds_creator* ds_create = (ds_creator*)dlsym(ds_lib, "createInstance"); - const char* dlsym_error = dlerror(); - if (dlsym_error != NULL) { - isc_throw(DataSourceError, "Error in library " << type << - ": " << dlsym_error); - } - return (ds_create(config)); - } else if (type == "memory") { - return (memoryCreateInstance(config)); - } else { - isc_throw(DataSourceError, "Unknown datasource type: " << type); + std::string dl_name = type + "_ds.so"; + + void *ds_lib = dlopen(dl_name.c_str(), RTLD_LAZY); + if (ds_lib == NULL) { + isc_throw(DataSourceError, "Unable to load " << type << + ": " << dlerror()); } + dlerror(); + ds_creator* ds_create = (ds_creator*)dlsym(ds_lib, "createInstance"); + const char* dlsym_error = dlerror(); + if (dlsym_error != NULL) { + isc_throw(DataSourceError, "Error in library " << type << + ": " << dlsym_error); + } + return (ds_create(config)); } } // end namespace datasrc diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index 5f6d2cb346..e5754e5e48 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -28,6 +28,21 @@ namespace isc { namespace datasrc { +typedef DataSourceClient* ds_creator(isc::data::ConstElementPtr config); +typedef void ds_destructor(DataSourceClient* instance); + +class DataSourceClientContainer { +public: + DataSourceClientContainer(const std::string& type, + isc::data::ConstElementPtr config); + ~DataSourceClientContainer(); +private: + DataSourceClient* instance; + ds_destructor* destructor; + void *ds_lib; +}; + + /// \brief Raised if the given config contains bad data /// /// Depending on the datasource type, the configuration may differ (for @@ -58,8 +73,6 @@ DataSourceClient* createDataSourceClient(const std::string& type, isc::data::ConstElementPtr config); -typedef DataSourceClient* ds_creator(isc::data::ConstElementPtr config); -typedef void ds_destructor(); } } #endif // DATA_SOURCE_FACTORY_H diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 630b1c005e..2dada97536 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -29,9 +29,13 @@ #include #include #include +#include + +#include using namespace std; using namespace isc::dns; +using namespace isc::data; namespace isc { namespace datasrc { @@ -799,5 +803,26 @@ ZoneUpdaterPtr InMemoryClient::getUpdater(const isc::dns::Name&, bool) const { isc_throw(isc::NotImplemented, "Update attempt on in memory data source"); } + +bool +checkConfig(ConstElementPtr, ElementPtr) { + // current inmem has no options (yet) + return true; +} + +DataSourceClient * +createInstance(isc::data::ConstElementPtr config) { + ElementPtr errors(Element::createList()); + if (!checkConfig(config, errors)) { + isc_throw(DataSourceConfigError, errors->str()); + } + return (new InMemoryClient()); +} + +void destroyInstance(DataSourceClient* instance) { + delete instance; +} + + } // end of namespace datasrc } // end of namespace dns diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index c569548f63..6f15b83c60 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -22,6 +22,8 @@ #include #include +#include + namespace isc { namespace dns { class Name; @@ -213,7 +215,7 @@ private: /// while it wouldn't be safe to delete unnecessary zones inside the dedicated /// backend. /// -/// The findZone() method takes a domain name and returns the best matching +/// The findZone() method takes a domain name and returns the best matching /// \c InMemoryZoneFinder in the form of (Boost) shared pointer, so that it can /// provide the general interface for all data sources. class InMemoryClient : public DataSourceClient { @@ -283,6 +285,12 @@ private: class InMemoryClientImpl; InMemoryClientImpl* impl_; }; + +extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr config); + +extern "C" void destroyInstance(DataSourceClient* instance); + + } } #endif // __DATA_SOURCE_MEMORY_H diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index e4c14623e3..f9ce905286 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -729,5 +729,5 @@ void destroyInstance(DataSourceClient* instance) { delete instance; } -} -} +} // end of namespace datasrc +} // end of namespace isc diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index c53f94cf82..10e1add99f 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -29,8 +29,8 @@ run_unittests_SOURCES += query_unittest.cc run_unittests_SOURCES += cache_unittest.cc run_unittests_SOURCES += test_datasrc.h test_datasrc.cc run_unittests_SOURCES += rbtree_unittest.cc -run_unittests_SOURCES += zonetable_unittest.cc -run_unittests_SOURCES += memory_datasrc_unittest.cc +#run_unittests_SOURCES += zonetable_unittest.cc +#run_unittests_SOURCES += memory_datasrc_unittest.cc run_unittests_SOURCES += logger_unittest.cc run_unittests_SOURCES += database_unittest.cc run_unittests_SOURCES += client_unittest.cc @@ -39,6 +39,7 @@ run_unittests_SOURCES += factory_unittest.cc # for the dlopened types we have tests for, we also need to include the # sources run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc +#run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/datasrc/tests/factory_unittest.cc b/src/lib/datasrc/tests/factory_unittest.cc index f9573b95a6..9e35636122 100644 --- a/src/lib/datasrc/tests/factory_unittest.cc +++ b/src/lib/datasrc/tests/factory_unittest.cc @@ -30,55 +30,96 @@ namespace { // The default implementation is NotImplemented TEST(FactoryTest, memoryClient) { - boost::scoped_ptr client( - createDataSourceClient("memory", ElementPtr())); - - ASSERT_TRUE(client.get() != NULL); + DataSourceClientContainer client("memory", ElementPtr()); + DataSourceClientContainer client2("memory", ElementPtr()); } TEST(FactoryTest, badType) { - ASSERT_THROW(createDataSourceClient("foo", ElementPtr()), DataSourceError); + ASSERT_THROW(DataSourceClientContainer("foo", ElementPtr()), DataSourceError); } TEST(FactoryTest, sqlite3ClientBadConfig) { ElementPtr config; - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config = Element::create("asdf"); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config = Element::createMap(); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config->set("class", ElementPtr()); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config->set("class", Element::create(1)); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config->set("class", Element::create("FOO")); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config->set("class", Element::create("IN")); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config->set("file", ElementPtr()); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config->set("file", Element::create(1)); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); config->set("file", Element::create("/foo/bar/doesnotexist")); - ASSERT_THROW(createDataSourceClient("sqlite3", config), + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + SQLite3Error); +} + + +TEST(FactoryTest, sqlite3ClientBadConfig3) { + ElementPtr config; + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config = Element::create("asdf"); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config = Element::createMap(); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("class", ElementPtr()); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create(1)); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create("FOO")); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create("IN")); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("file", ElementPtr()); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("file", Element::create(1)); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("file", Element::create("/foo/bar/doesnotexist")); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), SQLite3Error); } From 6b27a7ba1c0343725e3d2e9ea7d97426a8f73f0d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 11:37:00 +0200 Subject: [PATCH 808/974] [1206] some makefile workarounds --- src/bin/auth/Makefile.am | 6 ++++++ src/bin/auth/benchmarks/Makefile.am | 6 ++++++ src/bin/auth/tests/Makefile.am | 7 +++++++ src/lib/datasrc/Makefile.am | 1 - src/lib/datasrc/factory.cc | 22 ---------------------- src/lib/datasrc/memory_datasrc.cc | 5 ++++- src/lib/datasrc/tests/factory_unittest.cc | 2 -- 7 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am index e3128b5113..4d8ec833bd 100644 --- a/src/bin/auth/Makefile.am +++ b/src/bin/auth/Makefile.am @@ -50,6 +50,12 @@ b10_auth_SOURCES += command.cc command.h b10_auth_SOURCES += common.h common.cc b10_auth_SOURCES += statistics.cc statistics.h b10_auth_SOURCES += main.cc +# This is a temporary workaround for #1206, where the InMemoryClient has been +# moved to an ldopened library. We could add that library to LDADD, but that +# is nonportable. When #1207 is done this becomes moot anyway, and the +# specific workaround is not needed anymore, so we can then remove this +# line again. +b10_auth_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc EXTRA_DIST += auth_messages.mes diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am index d51495bcb4..53c019fbaa 100644 --- a/src/bin/auth/benchmarks/Makefile.am +++ b/src/bin/auth/benchmarks/Makefile.am @@ -13,6 +13,12 @@ query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc query_bench_SOURCES += ../auth_config.h ../auth_config.cc query_bench_SOURCES += ../statistics.h ../statistics.cc query_bench_SOURCES += ../auth_log.h ../auth_log.cc +# This is a temporary workaround for #1206, where the InMemoryClient has been +# moved to an ldopened library. We could add that library to LDADD, but that +# is nonportable. When #1207 is done this becomes moot anyway, and the +# specific workaround is not needed anymore, so we can then remove this +# line again. +query_bench_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am index 5cd2f5a6cd..d27386e62e 100644 --- a/src/bin/auth/tests/Makefile.am +++ b/src/bin/auth/tests/Makefile.am @@ -37,6 +37,13 @@ run_unittests_SOURCES += query_unittest.cc run_unittests_SOURCES += change_user_unittest.cc run_unittests_SOURCES += statistics_unittest.cc run_unittests_SOURCES += run_unittests.cc +# This is a temporary workaround for #1206, where the InMemoryClient has been +# moved to an ldopened library. We could add that library to LDADD, but that +# is nonportable. When #1207 is done this becomes moot anyway, and the +# specific workaround is not needed anymore, so we can then remove this +# line again. +run_unittests_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc + nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index 7c29d38ffe..3a01f0d5f9 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -17,7 +17,6 @@ libdatasrc_la_SOURCES += query.h query.cc libdatasrc_la_SOURCES += cache.h cache.cc libdatasrc_la_SOURCES += rbtree.h libdatasrc_la_SOURCES += zonetable.h zonetable.cc -#libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc libdatasrc_la_SOURCES += zone.h libdatasrc_la_SOURCES += result.h libdatasrc_la_SOURCES += logger.h logger.cc diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index 4165a7385c..cd9052fae1 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -69,28 +69,6 @@ DataSourceClientContainer::~DataSourceClientContainer() { dlclose(ds_lib); } -DataSourceClient * -createDataSourceClient(const std::string& type, - isc::data::ConstElementPtr config) { - // The name of the loadable module is type + _ds.so - // config is assumed to be ok - std::string dl_name = type + "_ds.so"; - - void *ds_lib = dlopen(dl_name.c_str(), RTLD_LAZY); - if (ds_lib == NULL) { - isc_throw(DataSourceError, "Unable to load " << type << - ": " << dlerror()); - } - dlerror(); - ds_creator* ds_create = (ds_creator*)dlsym(ds_lib, "createInstance"); - const char* dlsym_error = dlerror(); - if (dlsym_error != NULL) { - isc_throw(DataSourceError, "Error in library " << type << - ": " << dlsym_error); - } - return (ds_create(config)); -} - } // end namespace datasrc } // end namespace isc diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 2dada97536..dcb730ffe5 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -804,6 +804,9 @@ InMemoryClient::getUpdater(const isc::dns::Name&, bool) const { isc_throw(isc::NotImplemented, "Update attempt on in memory data source"); } +// due to a c++ symbol lookup oddity, we need to explicitely put this into +// an anonymous namespace. Once we remove memory.cc from the general datasource +// lib, we can export this bool checkConfig(ConstElementPtr, ElementPtr) { // current inmem has no options (yet) @@ -825,4 +828,4 @@ void destroyInstance(DataSourceClient* instance) { } // end of namespace datasrc -} // end of namespace dns +} // end of namespace isc diff --git a/src/lib/datasrc/tests/factory_unittest.cc b/src/lib/datasrc/tests/factory_unittest.cc index 9e35636122..2a35ea4ba2 100644 --- a/src/lib/datasrc/tests/factory_unittest.cc +++ b/src/lib/datasrc/tests/factory_unittest.cc @@ -31,7 +31,6 @@ namespace { // The default implementation is NotImplemented TEST(FactoryTest, memoryClient) { DataSourceClientContainer client("memory", ElementPtr()); - DataSourceClientContainer client2("memory", ElementPtr()); } TEST(FactoryTest, badType) { @@ -122,6 +121,5 @@ TEST(FactoryTest, sqlite3ClientBadConfig3) { ASSERT_THROW(DataSourceClientContainer("sqlite3", config), SQLite3Error); } - } // end anonymous namespace From 0372723794501908ae94be9330dcd8577d951f68 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 11:57:16 +0200 Subject: [PATCH 809/974] [1206] use RAII for ldopened lib itself too --- src/lib/datasrc/factory.cc | 72 ++++++++++++----------- src/lib/datasrc/factory.h | 16 ++++- src/lib/datasrc/tests/factory_unittest.cc | 3 + 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index cd9052fae1..1825dae3cb 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -27,46 +27,50 @@ using namespace isc::datasrc; namespace isc { namespace datasrc { -DataSourceClientContainer::DataSourceClientContainer(const std::string& type, - ConstElementPtr config) -{ - // The name of the loadable module is type + _ds.so - // config is assumed to be ok - std::string dl_name = type + "_ds.so"; - ds_lib = dlopen(dl_name.c_str(), RTLD_NOW | RTLD_LOCAL); - if (ds_lib == NULL) { - isc_throw(DataSourceError, "Unable to load " << type << +DLHolder::DLHolder(const std::string& name) : ds_name_(name) { + ds_lib_ = dlopen(ds_name_.c_str(), RTLD_NOW | RTLD_LOCAL); + if (ds_lib_ == NULL) { + isc_throw(DataSourceError, "Unable to load " << ds_name_ << ": " << dlerror()); } - dlerror(); - ds_creator* ds_create = (ds_creator*)dlsym(ds_lib, "createInstance"); - const char* dlsym_error = dlerror(); - if (dlsym_error != NULL) { - dlclose(ds_lib); - isc_throw(DataSourceError, "Error in library " << type << - ": " << dlsym_error); - } - try { - instance = ds_create(config); - } catch (...) { - dlclose(ds_lib); - throw; - } - - dlerror(); - destructor = (ds_destructor*)dlsym(ds_lib, "destroyInstance"); - dlsym_error = dlerror(); - if (dlsym_error != NULL) { - dlclose(ds_lib); - isc_throw(DataSourceError, "Error in library " << type << - ": " << dlsym_error); - } +} + +DLHolder::~DLHolder() { + dlclose(ds_lib_); +} + +void* +DLHolder::getSym(const char* name) { + // Since dlsym can return NULL on success, we check for errors by + // first clearing any existing errors with dlerror(), then calling dlsym, + // and finally checking for errors with dlerror() + dlerror(); + + void *sym = dlsym(ds_lib_, name); + + const char* dlsym_error = dlerror(); + if (dlsym_error != NULL) { + dlclose(ds_lib_); + isc_throw(DataSourceError, "Error in library " << ds_name_ << + ": " << dlsym_error); + } + + return (sym); +} + +DataSourceClientContainer::DataSourceClientContainer(const std::string& type, + ConstElementPtr config) +: ds_lib_(type + "_ds.so") +{ + ds_creator* ds_create = (ds_creator*)ds_lib_.getSym("createInstance"); + destructor_ = (ds_destructor*)ds_lib_.getSym("destroyInstance"); + + instance_ = ds_create(config); } DataSourceClientContainer::~DataSourceClientContainer() { - destructor(instance); - dlclose(ds_lib); + destructor_(instance_); } } // end namespace datasrc diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index e5754e5e48..3f47ff4ed1 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -31,15 +31,25 @@ namespace datasrc { typedef DataSourceClient* ds_creator(isc::data::ConstElementPtr config); typedef void ds_destructor(DataSourceClient* instance); +class DLHolder { +public: + DLHolder(const std::string& name); + ~DLHolder(); + void* getSym(const char* name); +private: + const std::string ds_name_; + void *ds_lib_; +}; + class DataSourceClientContainer { public: DataSourceClientContainer(const std::string& type, isc::data::ConstElementPtr config); ~DataSourceClientContainer(); private: - DataSourceClient* instance; - ds_destructor* destructor; - void *ds_lib; + DataSourceClient* instance_; + ds_destructor* destructor_; + DLHolder ds_lib_; }; diff --git a/src/lib/datasrc/tests/factory_unittest.cc b/src/lib/datasrc/tests/factory_unittest.cc index 2a35ea4ba2..509bf11a04 100644 --- a/src/lib/datasrc/tests/factory_unittest.cc +++ b/src/lib/datasrc/tests/factory_unittest.cc @@ -120,6 +120,9 @@ TEST(FactoryTest, sqlite3ClientBadConfig3) { config->set("file", Element::create("/foo/bar/doesnotexist")); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), SQLite3Error); + + config->set("file", Element::create("/tmp/some_file.sqlite3")); + DataSourceClientContainer dsc("sqlite3", config); } } // end anonymous namespace From 90b3952ff515f8746ffc6b227695836921bc046d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 23 Sep 2011 12:07:13 +0200 Subject: [PATCH 810/974] [1206] add getInstance() call --- src/lib/datasrc/factory.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index 3f47ff4ed1..f3a5d641a7 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -46,6 +46,8 @@ public: DataSourceClientContainer(const std::string& type, isc::data::ConstElementPtr config); ~DataSourceClientContainer(); + DataSourceClient& getInstance() { return *instance_; } + private: DataSourceClient* instance_; ds_destructor* destructor_; From 3ff9c6c215faa2e1419d4cb67906a1f7772b355a Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 26 Sep 2011 12:28:32 +0200 Subject: [PATCH 811/974] [1206] check for config as specified in current auth --- src/lib/datasrc/memory_datasrc.cc | 121 +++++++++++++++++++++- src/lib/datasrc/sqlite3_accessor.cc | 52 ++++------ src/lib/datasrc/tests/factory_unittest.cc | 86 ++++++++++----- 3 files changed, 200 insertions(+), 59 deletions(-) diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index dcb730ffe5..13ce348712 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -804,12 +805,122 @@ InMemoryClient::getUpdater(const isc::dns::Name&, bool) const { isc_throw(isc::NotImplemented, "Update attempt on in memory data source"); } -// due to a c++ symbol lookup oddity, we need to explicitely put this into -// an anonymous namespace. Once we remove memory.cc from the general datasource -// lib, we can export this + +namespace { +// convencience function to add an error message to a list of those +// (TODO: move functions like these to some util lib?) +void +addError(ElementPtr errors, const std::string& error) { + if (errors != ElementPtr() && errors->getType() == Element::list) { + errors->add(Element::create(error)); + } +} + +/// Check if the given element exists in the map, and if it is a string bool -checkConfig(ConstElementPtr, ElementPtr) { - // current inmem has no options (yet) +checkConfigElementString(ConstElementPtr config, const std::string& name, + ElementPtr errors) +{ + if (!config->contains(name)) { + addError(errors, + "Config for memory backend does not contain a '" + "type" + "' value"); + return false; + } else if (!config->get(name) || + config->get(name)->getType() != Element::string) { + addError(errors, "value of " + name + + " in memory backend config is not a string"); + return false; + } else { + return true; + } +} + +bool +checkZoneConfig(ConstElementPtr config, ElementPtr errors) { + bool result = true; + if (!config || config->getType() != Element::map) { + addError(errors, "Elements in memory backend's zone list must be maps"); + result = false; + } else { + if (!checkConfigElementString(config, "origin", errors)) { + result = false; + } + if (!checkConfigElementString(config, "file", errors)) { + result = false; + } + // we could add some existence/readabilty/parsability checks here + // if we want + } + return result; +} + +} // end anonymous namespace + +bool +checkConfig(ConstElementPtr config, ElementPtr errors) { + /* Specific configuration is under discussion, right now this accepts + * the 'old' configuration, see [TODO] + * So for memory datasource, we get a structure like this: + * { "type": string ("memory"), + * "class": string ("IN"/"CH"/etc), + * "zones": list + * } + * Zones list is a list of maps: + * { "origin": string, + * "file": string + * } + * + * At this moment we cannot be completely sure of the contents of the + * structure, so we have to do some more extensive tests than should + * strictly be necessary (e.g. existence and type of elements) + */ + bool result = true; + + if (!config || config->getType() != Element::map) { + addError(errors, "Base config for memory backend must be a map"); + result = false; + } else { + if (!checkConfigElementString(config, "type", errors)) { + result = false; + } else { + if (config->get("type")->stringValue() != "memory") { + addError(errors, + "Config for memory backend is not of type \"memory\""); + result = false; + } + } + if (!checkConfigElementString(config, "class", errors)) { + result = false; + } else { + try { + RRClass rrc(config->get("class")->stringValue()); + } catch (const isc::Exception& rrce) { + addError(errors, + "Error parsing class config for memory backend: " + + std::string(rrce.what())); + result = false; + } + } + if (!config->contains("zones")) { + addError(errors, "No 'zones' element in memory backend config"); + result = false; + } else if (!config->get("zones") || + config->get("zones")->getType() != Element::list) { + addError(errors, "'zones' element in memory backend config is not a list"); + result = false; + } else { + BOOST_FOREACH(ConstElementPtr zone_config, + config->get("zones")->listValue()) { + if (!checkZoneConfig(zone_config, errors)) { + result = false; + } + } + } + } + + return (result); return true; } diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index f9ce905286..863da1028f 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -30,6 +30,8 @@ using namespace isc::data; #define SQLITE_SCHEMA_VERSION 1 +#define CONFIG_ITEM_DATABASE_FILE "database_file" + namespace isc { namespace datasrc { @@ -671,42 +673,33 @@ addError(ElementPtr errors, const std::string& error) { bool checkConfig(ConstElementPtr config, ElementPtr errors) { + /* Specific configuration is under discussion, right now this accepts + * the 'old' configuration, see [TODO] + */ bool result = true; + if (!config || config->getType() != Element::map) { addError(errors, "Base config for SQlite3 backend must be a map"); result = false; } else { - if (!config->contains("file")) { + if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) { addError(errors, - "Config for SQlite3 backend does not contain a 'file' value"); + "Config for SQlite3 backend does not contain a '" + CONFIG_ITEM_DATABASE_FILE + "' value"); result = false; - } else if (!config->get("file") || - config->get("file")->getType() != Element::string) { - addError(errors, "file value in SQLite3 backend is not a string"); + } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) || + config->get(CONFIG_ITEM_DATABASE_FILE)->getType() != + Element::string) { + addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE + " in SQLite3 backend is not a string"); result = false; - } else if (config->get("file")->stringValue() == "") { - addError(errors, "file value in SQLite3 backend is empty"); + } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() == + "") { + addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE + " in SQLite3 backend is empty"); result = false; } - - if (!config->contains("class")) { - addError(errors, "Config for SQlite3 backend does not contain a 'class' value"); - result = false; - } else if (!config->get("class") || - config->get("class")->getType() != Element::string) { - addError(errors, "class value in SQLite3 backend is not a string"); - result = false; - } else { - try { - isc::dns::RRClass rrclass(config->get("class")->stringValue()); - } catch (const isc::dns::InvalidRRClass& ivrc) { - addError(errors, ivrc.what()); - result = false; - } catch (const isc::dns::IncompleteRRClass& icrc) { - addError(errors, icrc.what()); - result = false; - } - } } return (result); @@ -718,11 +711,10 @@ createInstance(isc::data::ConstElementPtr config) { if (!checkConfig(config, errors)) { isc_throw(DataSourceConfigError, errors->str()); } - isc::dns::RRClass rrclass(config->get("class")->stringValue()); - std::string dbfile = config->get("file")->stringValue(); + std::string dbfile = config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue(); boost::shared_ptr sqlite3_accessor( - new SQLite3Accessor(dbfile, rrclass)); - return (new DatabaseClient(rrclass, sqlite3_accessor)); + new SQLite3Accessor(dbfile, isc::dns::RRClass::IN())); + return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor)); } void destroyInstance(DataSourceClient* instance) { diff --git a/src/lib/datasrc/tests/factory_unittest.cc b/src/lib/datasrc/tests/factory_unittest.cc index 509bf11a04..ebdcfeed2f 100644 --- a/src/lib/datasrc/tests/factory_unittest.cc +++ b/src/lib/datasrc/tests/factory_unittest.cc @@ -30,7 +30,60 @@ namespace { // The default implementation is NotImplemented TEST(FactoryTest, memoryClient) { - DataSourceClientContainer client("memory", ElementPtr()); + ElementPtr config; + ASSERT_THROW(DataSourceClientContainer client("memory", config), + DataSourceConfigError); + + config = Element::create("asdf"); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config = Element::createMap(); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("type", ElementPtr()); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("type", Element::create(1)); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("type", Element::create("FOO")); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("type", Element::create("memory")); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("class", ElementPtr()); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("class", Element::create(1)); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("class", Element::create("FOO")); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("class", Element::create("IN")); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("zones", ElementPtr()); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("zones", Element::create(1)); + ASSERT_THROW(DataSourceClientContainer("memory", config), + DataSourceConfigError); + + config->set("zones", Element::createList()); + DataSourceClientContainer("memory", config); } TEST(FactoryTest, badType) { @@ -66,15 +119,15 @@ TEST(FactoryTest, sqlite3ClientBadConfig) { ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); - config->set("file", ElementPtr()); + config->set("database_file", ElementPtr()); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); - config->set("file", Element::create(1)); + config->set("database_file", Element::create(1)); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); - config->set("file", Element::create("/foo/bar/doesnotexist")); + config->set("database_file", Element::create("/foo/bar/doesnotexist")); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), SQLite3Error); } @@ -93,35 +146,20 @@ TEST(FactoryTest, sqlite3ClientBadConfig3) { ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); - config->set("class", ElementPtr()); + config->set("database_file", ElementPtr()); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); - config->set("class", Element::create(1)); + config->set("database_file", Element::create(1)); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceConfigError); - config->set("class", Element::create("FOO")); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("class", Element::create("IN")); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("file", ElementPtr()); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("file", Element::create(1)); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("file", Element::create("/foo/bar/doesnotexist")); + config->set("database_file", Element::create("/foo/bar/doesnotexist")); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), SQLite3Error); - config->set("file", Element::create("/tmp/some_file.sqlite3")); + // TODO remove this one (now config isn't bad anymore) or find better filename + config->set("database_file", Element::create("/tmp/some_file.sqlite3")); DataSourceClientContainer dsc("sqlite3", config); } } // end anonymous namespace From f7a92e4b0336f3c64eb429947657952178b7d76f Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 26 Sep 2011 16:42:48 +0200 Subject: [PATCH 812/974] [1206] update to docs and tests --- src/lib/datasrc/factory.cc | 12 +- src/lib/datasrc/factory.h | 107 +++++++++++++-- src/lib/datasrc/tests/factory_unittest.cc | 159 ++++++++++++---------- 3 files changed, 183 insertions(+), 95 deletions(-) diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index 1825dae3cb..f2f9d6a5b5 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -27,12 +27,10 @@ using namespace isc::datasrc; namespace isc { namespace datasrc { - -DLHolder::DLHolder(const std::string& name) : ds_name_(name) { - ds_lib_ = dlopen(ds_name_.c_str(), RTLD_NOW | RTLD_LOCAL); +DLHolder::DLHolder(const std::string& name) { + ds_lib_ = dlopen(name.c_str(), RTLD_NOW | RTLD_LOCAL); if (ds_lib_ == NULL) { - isc_throw(DataSourceError, "Unable to load " << ds_name_ << - ": " << dlerror()); + isc_throw(DataSourceLibraryError, dlerror()); } } @@ -51,9 +49,7 @@ DLHolder::getSym(const char* name) { const char* dlsym_error = dlerror(); if (dlsym_error != NULL) { - dlclose(ds_lib_); - isc_throw(DataSourceError, "Error in library " << ds_name_ << - ": " << dlsym_error); + isc_throw(DataSourceLibraryError, dlsym_error); } return (sym); diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index f3a5d641a7..66374d5049 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -20,6 +20,7 @@ //#include +#include #include #include @@ -28,24 +29,117 @@ namespace isc { namespace datasrc { + +/// \brief Raised if there is an error loading the datasource implementation +/// library, or if that library misses a needed symbol +class DataSourceLibraryError : public DataSourceError { +public: + DataSourceLibraryError(const char* file, size_t line, const char* what) : + DataSourceError(file, line, what) {} +}; + +/// \brief Raised if the given config contains bad data +/// +/// Depending on the datasource type, the configuration may differ (for +/// instance, the sqlite3 datasource needs a database file). +class DataSourceConfigError : public DataSourceError { +public: + DataSourceConfigError(const char* file, size_t line, const char* what) : + DataSourceError(file, line, what) {} +}; + typedef DataSourceClient* ds_creator(isc::data::ConstElementPtr config); typedef void ds_destructor(DataSourceClient* instance); +/// \brief Container class for dynamically loaded libraries +/// +/// This class is used to dlopen() a library, provides access to dlsym(), +/// and cleans up the dlopened library when the instance of this class is +/// destroyed. +/// +/// Its main function is to provide RAII-style access to dlopen'ed libraries. +/// +/// \note Currently it is Datasource-backend specific. If we have need for this +/// in other places than for dynamically loading datasources, then, apart +/// from moving it to another location, we also need to make the +/// exceptions raised more general. class DLHolder { public: + /// \brief Constructor + /// + /// \param name The name of the library (.so) file. This file must be in + /// the library path. + /// + /// \exception DataSourceLibraryError If the library cannot be found or + /// cannot be loaded. DLHolder(const std::string& name); + + /// \brief Destructor + /// + /// Cleans up the library by calling dlclose() ~DLHolder(); + + /// \brief Retrieve a symbol + /// + /// This retrieves a symbol from the loaded library. + /// + /// \exception DataSourceLibraryError if the symbol cannot be found, or if + /// another error (as reported by dlerror() occurs. + /// + /// \param name The name of the symbol to retrieve + /// \return A pointer to the symbol void* getSym(const char* name); private: - const std::string ds_name_; + /// Pointer to the dynamically loaded library structure void *ds_lib_; }; + +/// \brief Container for a specific instance of a dynamically loaded +/// DataSourceClient implementation +/// +/// Given a datasource type and a type-specific set of configuration data, +/// the corresponding dynamic library is loaded (if it hadn't been already), +/// and an instance is created. This instance is stored within this structure, +/// and can be accessed through getInstance(). +/// +/// The 'type' is actually the name of the library, minus the '_ds.so' postfix +/// Datasource implementation libraries therefore have a fixed name, both for +/// easy recognition and to reduce potential mistakes. +/// For example, the sqlite3 implementation has the type 'sqlite3', and the +/// derived filename 'sqlite3_ds.so' +/// +/// There are of course some demands to an implementation, not all of which +/// can be verified compile-time. It must provide a creator and destructor +/// functions. The creator function must return an instance of a subclass of +/// DataSourceClient. +/// class DataSourceClientContainer { public: + /// \brief Constructor + /// + /// \exception DataSourceLibraryError if there is an error loading the + /// backend library + /// \exception DataSourceConfigError if the given config is not correct + /// for the given type + /// + /// \param type The type of the datasource client. Based on the value of + /// type, a specific backend library is used, by appending the + /// string '_ds.so' to the given type, and loading that as the + /// implementation library + /// \param config Type-specific configuration data, see the documentation + /// of the datasource backend type for information on what + /// configuration data to pass. DataSourceClientContainer(const std::string& type, isc::data::ConstElementPtr config); + + /// \brief Destructor ~DataSourceClientContainer(); + + /// \brief Accessor to the instance + /// + /// \return Reference to the DataSourceClient instance contained in this + /// container. DataSourceClient& getInstance() { return *instance_; } private: @@ -54,17 +148,6 @@ private: DLHolder ds_lib_; }; - -/// \brief Raised if the given config contains bad data -/// -/// Depending on the datasource type, the configuration may differ (for -/// instance, the sqlite3 datasource needs a database file). -class DataSourceConfigError : public isc::Exception { -public: - DataSourceConfigError(const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) {} -}; - /// \brief Create a datasource instance /// /// This function is a fixed generator for datasource instances of all types. diff --git a/src/lib/datasrc/tests/factory_unittest.cc b/src/lib/datasrc/tests/factory_unittest.cc index ebdcfeed2f..94d11189ed 100644 --- a/src/lib/datasrc/tests/factory_unittest.cc +++ b/src/lib/datasrc/tests/factory_unittest.cc @@ -26,10 +26,78 @@ using namespace isc::datasrc; using namespace isc::data; +std::string SQLITE_DBFILE_EXAMPLE_ORG = TEST_DATA_DIR "/example.org.sqlite3"; + namespace { -// The default implementation is NotImplemented +TEST(FactoryTest, sqlite3ClientBadConfig) { + // We start out by building the configuration data bit by bit, + // testing each form of 'bad config', until we have a good one. + // Then we do some very basic operation on the client (detailed + // tests are left to the implementation-specific backends) + ElementPtr config; + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config = Element::create("asdf"); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config = Element::createMap(); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("class", ElementPtr()); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create(1)); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create("FOO")); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("class", Element::create("IN")); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("database_file", ElementPtr()); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("database_file", Element::create(1)); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + DataSourceConfigError); + + config->set("database_file", Element::create("/foo/bar/doesnotexist")); + ASSERT_THROW(DataSourceClientContainer("sqlite3", config), + SQLite3Error); + + config->set("database_file", Element::create(SQLITE_DBFILE_EXAMPLE_ORG)); + DataSourceClientContainer dsc("sqlite3", config); + + DataSourceClient::FindResult result1( + dsc.getInstance().findZone(isc::dns::Name("example.org."))); + ASSERT_EQ(result::SUCCESS, result1.code); + + DataSourceClient::FindResult result2( + dsc.getInstance().findZone(isc::dns::Name("no.such.zone."))); + ASSERT_EQ(result::NOTFOUND, result2.code); + + ZoneIteratorPtr iterator(dsc.getInstance().getIterator( + isc::dns::Name("example.org."))); + + ZoneUpdaterPtr updater(dsc.getInstance().getUpdater( + isc::dns::Name("example.org."), false)); +} + TEST(FactoryTest, memoryClient) { + // We start out by building the configuration data bit by bit, + // testing each form of 'bad config', until we have a good one. + // Then we do some very basic operation on the client (detailed + // tests are left to the implementation-specific backends) ElementPtr config; ASSERT_THROW(DataSourceClientContainer client("memory", config), DataSourceConfigError); @@ -83,84 +151,25 @@ TEST(FactoryTest, memoryClient) { DataSourceConfigError); config->set("zones", Element::createList()); - DataSourceClientContainer("memory", config); + DataSourceClientContainer dsc("memory", config); + + // Once it is able to load some zones, we should add a few tests + // here to see that it does. + DataSourceClient::FindResult result( + dsc.getInstance().findZone(isc::dns::Name("no.such.zone."))); + ASSERT_EQ(result::NOTFOUND, result.code); + + ASSERT_THROW(dsc.getInstance().getIterator(isc::dns::Name("example.org.")), + DataSourceError); + + ASSERT_THROW(dsc.getInstance().getUpdater(isc::dns::Name("no.such.zone."), + false), isc::NotImplemented); } TEST(FactoryTest, badType) { - ASSERT_THROW(DataSourceClientContainer("foo", ElementPtr()), DataSourceError); + ASSERT_THROW(DataSourceClientContainer("foo", ElementPtr()), + DataSourceError); } -TEST(FactoryTest, sqlite3ClientBadConfig) { - ElementPtr config; - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config = Element::create("asdf"); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config = Element::createMap(); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("class", ElementPtr()); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("class", Element::create(1)); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("class", Element::create("FOO")); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("class", Element::create("IN")); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("database_file", ElementPtr()); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("database_file", Element::create(1)); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("database_file", Element::create("/foo/bar/doesnotexist")); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - SQLite3Error); -} - - -TEST(FactoryTest, sqlite3ClientBadConfig3) { - ElementPtr config; - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config = Element::create("asdf"); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config = Element::createMap(); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("database_file", ElementPtr()); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("database_file", Element::create(1)); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - DataSourceConfigError); - - config->set("database_file", Element::create("/foo/bar/doesnotexist")); - ASSERT_THROW(DataSourceClientContainer("sqlite3", config), - SQLite3Error); - - // TODO remove this one (now config isn't bad anymore) or find better filename - config->set("database_file", Element::create("/tmp/some_file.sqlite3")); - DataSourceClientContainer dsc("sqlite3", config); -} } // end anonymous namespace From 98cb905a5852321204499985efb42c5a76b9da6e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 26 Sep 2011 17:09:16 +0200 Subject: [PATCH 813/974] [1206] remove original now unused function --- src/lib/datasrc/factory.h | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index 66374d5049..fec060ce62 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -148,28 +148,8 @@ private: DLHolder ds_lib_; }; -/// \brief Create a datasource instance -/// -/// This function is a fixed generator for datasource instances of all types. -/// -/// Currently, the different types are hardcoded in the implementation of this -/// function. However, we plan on making it more flexible, possibly through -/// th -/// -/// \note This function returns a raw pointer. The caller is expected to -/// delete this pointer again. We don't return any specific smart -/// pointer for flexibility. However, we highly advice that the -/// return value of this function is directly put into a shared or -/// scoped pointer. -/// -/// \exception DataSourceConfigError if the given configuration values are -/// bad for the given datasource type -DataSourceClient* -createDataSourceClient(const std::string& type, - isc::data::ConstElementPtr config); - -} -} +} // end namespace datasrc +} // end namespace isc #endif // DATA_SOURCE_FACTORY_H // Local Variables: // mode: c++ From 58b843554162e6599ba895c8325985f74adef734 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 27 Sep 2011 10:52:18 +0200 Subject: [PATCH 814/974] [1206] update documentation, and change DLHolder name --- src/lib/datasrc/client.h | 44 ++++++++++++++++++++++++++++++++++++++ src/lib/datasrc/factory.cc | 6 +++--- src/lib/datasrc/factory.h | 18 ++++++++++------ 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h index 6a7ae046bd..40b7a3f307 100644 --- a/src/lib/datasrc/client.h +++ b/src/lib/datasrc/client.h @@ -22,6 +22,47 @@ #include +/// \file +/// Datasource clients +/// +/// The data source client API is specified in client.h, and provides the +/// functionality to query and modify data in the data sources. There are +/// multiple datasource implementations, and by subclassing DataSourceClient or +/// DatabaseClient, more can be added. +/// +/// All datasources are implemented as loadable modules, with a name of the +/// form "_ds.so". This has been chosen intentionally, to minimize +/// confusion and potential mistakes. +/// +/// In order to use a datasource client backend, the class +/// DataSourceClientContainer is provided in factory.h; this will load the +/// library, set up the instance, and clean everything up once it is destroyed. +/// +/// Access to the actual instance is provided with the getInstance() method +/// in DataSourceClientContainer +/// +/// \note Depending on actual usage, we might consider making the container +/// a transparent abstraction layer, so it can be used as a DataSourceClient +/// directly. This has some other implications though so for now the only access +/// provided is through getInstance()). +/// +/// For datasource backends, we use a dynamically loaded library system (with +/// dlopen()). This library must contain the following things; +/// - A subclass of DataSourceClient or DatabaseClient (which itself is a +/// subclass of DataSourceClient) +/// - A creator function for an instance of that subclass, of the form: +/// \code +/// extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr cfg); +/// \endcode +/// - A destructor for said instance, of the form: +/// \code +/// extern "C" void destroyInstance(isc::data::DataSourceClient* instance); +/// \endcode +/// +/// See the documentation for the \link DataSourceClient \endlink class for +/// more information on implementing subclasses of it. +/// + namespace isc { namespace datasrc { @@ -39,6 +80,9 @@ typedef boost::shared_ptr ZoneIteratorPtr; /// operations to other classes; in general methods of this class act as /// factories of these other classes. /// +/// See \link datasrc/client.h datasrc/client.h \endlink for more information +/// on adding datasource implementations. +/// /// The following derived classes are currently (expected to be) provided: /// - \c InMemoryClient: A client of a conceptual data source that stores /// all necessary data in memory for faster lookups diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index f2f9d6a5b5..3126a0424a 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -27,19 +27,19 @@ using namespace isc::datasrc; namespace isc { namespace datasrc { -DLHolder::DLHolder(const std::string& name) { +LibraryContainer::LibraryContainer(const std::string& name) { ds_lib_ = dlopen(name.c_str(), RTLD_NOW | RTLD_LOCAL); if (ds_lib_ == NULL) { isc_throw(DataSourceLibraryError, dlerror()); } } -DLHolder::~DLHolder() { +LibraryContainer::~LibraryContainer() { dlclose(ds_lib_); } void* -DLHolder::getSym(const char* name) { +LibraryContainer::getSym(const char* name) { // Since dlsym can return NULL on success, we check for errors by // first clearing any existing errors with dlerror(), then calling dlsym, // and finally checking for errors with dlerror() diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index fec060ce62..813b6d75ff 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -63,7 +63,7 @@ typedef void ds_destructor(DataSourceClient* instance); /// in other places than for dynamically loading datasources, then, apart /// from moving it to another location, we also need to make the /// exceptions raised more general. -class DLHolder { +class LibraryContainer { public: /// \brief Constructor /// @@ -72,12 +72,12 @@ public: /// /// \exception DataSourceLibraryError If the library cannot be found or /// cannot be loaded. - DLHolder(const std::string& name); + LibraryContainer(const std::string& name); /// \brief Destructor /// /// Cleans up the library by calling dlclose() - ~DLHolder(); + ~LibraryContainer(); /// \brief Retrieve a symbol /// @@ -101,7 +101,9 @@ private: /// Given a datasource type and a type-specific set of configuration data, /// the corresponding dynamic library is loaded (if it hadn't been already), /// and an instance is created. This instance is stored within this structure, -/// and can be accessed through getInstance(). +/// and can be accessed through getInstance(). Upon destruction of this +/// container, the stored instance of the DataSourceClient is deleted with +/// the destructor function provided by the loaded library. /// /// The 'type' is actually the name of the library, minus the '_ds.so' postfix /// Datasource implementation libraries therefore have a fixed name, both for @@ -112,8 +114,12 @@ private: /// There are of course some demands to an implementation, not all of which /// can be verified compile-time. It must provide a creator and destructor /// functions. The creator function must return an instance of a subclass of -/// DataSourceClient. +/// DataSourceClient. The prototypes of these functions are as follows: +/// \code +/// extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr cfg); /// +/// extern "C" void destroyInstance(isc::data::DataSourceClient* instance); +/// \endcode class DataSourceClientContainer { public: /// \brief Constructor @@ -145,7 +151,7 @@ public: private: DataSourceClient* instance_; ds_destructor* destructor_; - DLHolder ds_lib_; + LibraryContainer ds_lib_; }; } // end namespace datasrc From e56e0f7d1ad206f1ebc26e285d82a8e7ff6390e1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 27 Sep 2011 12:00:00 +0200 Subject: [PATCH 815/974] [1177] Examples for NSEC results. --- src/lib/datasrc/zone.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 6b74b5a197..2a00b7a358 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -70,7 +70,20 @@ public: /// that matched the query name). In case of empty nonterminal cases, /// an NSEC is provided for the interval where the empty nonterminal /// lives, which is the one ending in the subdomain of the empty - /// nonterminal. + /// nonterminal. Examples: if zone "example.com" has the following + /// record: + /// \code + /// a.b.example.com. NSEC c.example.com. + /// \endcode + /// a call to \c find() for "b.example.com." will result in NXRRSET, + /// and if the FIND_DNSSEC option is set this NSEC will be returned. + /// Likewise, if zone "example.org" has the following record, + /// \code + /// x.*.example.org. NSEC a.example.org. + /// \endcode + /// a call to \c find() for "y.example.org" will result in + /// WILDCARD_NXRRSET (*.example.org is an empty nonterminal wildcard node), + /// and if the FIND_DNSSEC option is set this NSEC will be returned. /// /// In case of NXDOMAIN, the returned NSEC covers the queried domain. enum Result { From bc03b37015ab6ea23cbec70dbd299c74fb001aba Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 27 Sep 2011 12:12:50 +0200 Subject: [PATCH 816/974] [1177] Further doxygen tweaks --- src/lib/datasrc/zone.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 2a00b7a358..36b2d4215b 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -67,10 +67,14 @@ public: /// belongs to the domain which would provide the result if it /// contained the correct type (in case of NXRRSET, it is the queried /// domain, in case of WILDCARD_NXRRSET, it is the wildcard domain - /// that matched the query name). In case of empty nonterminal cases, + /// that matched the query name). In case of an empty nonterminal, /// an NSEC is provided for the interval where the empty nonterminal - /// lives, which is the one ending in the subdomain of the empty - /// nonterminal. Examples: if zone "example.com" has the following + /// lives. The end of the interval is the subdomain causing existence + /// of the empty nonterminal (if there's sub.x.example.com, and no + /// x.example.com, then x.example.com exists implicitly - is the empty + /// nonterminal and sub.x.example.com is the subdomain causing it). + /// + /// Examples: if zone "example.com" has the following /// record: /// \code /// a.b.example.com. NSEC c.example.com. From 0f8868d1ed7d479d05e2a70de67897d133d41ef9 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 27 Sep 2011 04:16:55 -0700 Subject: [PATCH 817/974] [master] fix libpath placeholder in test (unittest failures) --- src/lib/python/isc/datasrc/tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am index 78284ab1c7..be30dfa0e2 100644 --- a/src/lib/python/isc/datasrc/tests/Makefile.am +++ b/src/lib/python/isc/datasrc/tests/Makefile.am @@ -12,7 +12,7 @@ CLEANFILES = $(abs_builddir)/rwtest.sqlite3.copied # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS From 38816f95cc01f1c7aeec1d42bde3febb308dd98f Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 27 Sep 2011 04:50:03 -0700 Subject: [PATCH 818/974] [master] update rest of placeholders too --- src/bin/bind10/tests/Makefile.am | 2 +- src/bin/bindctl/tests/Makefile.am | 2 +- src/bin/cfgmgr/plugins/tests/Makefile.am | 2 +- src/bin/cfgmgr/tests/Makefile.am | 2 +- src/bin/cmdctl/tests/Makefile.am | 2 +- src/bin/dhcp6/tests/Makefile.am | 2 +- src/bin/loadzone/tests/correct/Makefile.am | 2 +- src/bin/loadzone/tests/error/Makefile.am | 2 +- src/bin/msgq/tests/Makefile.am | 2 +- src/bin/tests/Makefile.am | 2 +- src/bin/xfrin/tests/Makefile.am | 2 +- src/bin/xfrout/tests/Makefile.am | 2 +- src/bin/zonemgr/tests/Makefile.am | 2 +- src/lib/python/isc/acl/tests/Makefile.am | 2 +- src/lib/python/isc/bind10/tests/Makefile.am | 2 +- src/lib/python/isc/cc/tests/Makefile.am | 2 +- src/lib/python/isc/config/tests/Makefile.am | 2 +- src/lib/python/isc/log/tests/Makefile.am | 2 +- src/lib/python/isc/net/tests/Makefile.am | 2 +- src/lib/python/isc/notify/tests/Makefile.am | 2 +- src/lib/python/isc/util/tests/Makefile.am | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am index d0f36ca6b2..d54ee56be1 100644 --- a/src/bin/bind10/tests/Makefile.am +++ b/src/bin/bind10/tests/Makefile.am @@ -8,7 +8,7 @@ noinst_SCRIPTS = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/bindctl/tests/Makefile.am b/src/bin/bindctl/tests/Makefile.am index 5bde145053..3d08a1720b 100644 --- a/src/bin/bindctl/tests/Makefile.am +++ b/src/bin/bindctl/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/cfgmgr/plugins/tests/Makefile.am b/src/bin/cfgmgr/plugins/tests/Makefile.am index ca8005b3ba..ffea2d7011 100644 --- a/src/bin/cfgmgr/plugins/tests/Makefile.am +++ b/src/bin/cfgmgr/plugins/tests/Makefile.am @@ -7,7 +7,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am index 41edc8edb6..a2e43ff5b0 100644 --- a/src/bin/cfgmgr/tests/Makefile.am +++ b/src/bin/cfgmgr/tests/Makefile.am @@ -8,7 +8,7 @@ EXTRA_DIST = testdata/plugins/testplugin.py # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/cmdctl/tests/Makefile.am b/src/bin/cmdctl/tests/Makefile.am index 6bb9fba8e7..89d89ea64d 100644 --- a/src/bin/cmdctl/tests/Makefile.am +++ b/src/bin/cmdctl/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 79f5968310..231a3d9c5c 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -8,7 +8,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/loadzone/tests/correct/Makefile.am b/src/bin/loadzone/tests/correct/Makefile.am index 73c8a3464a..fb882bae4b 100644 --- a/src/bin/loadzone/tests/correct/Makefile.am +++ b/src/bin/loadzone/tests/correct/Makefile.am @@ -19,7 +19,7 @@ noinst_SCRIPTS = correct_test.sh # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # TODO: maybe use TESTS? diff --git a/src/bin/loadzone/tests/error/Makefile.am b/src/bin/loadzone/tests/error/Makefile.am index 57f7857b21..03263b79d0 100644 --- a/src/bin/loadzone/tests/error/Makefile.am +++ b/src/bin/loadzone/tests/error/Makefile.am @@ -18,7 +18,7 @@ noinst_SCRIPTS = error_test.sh # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # TODO: use TESTS ? diff --git a/src/bin/msgq/tests/Makefile.am b/src/bin/msgq/tests/Makefile.am index ee9ffd87fc..50b218b915 100644 --- a/src/bin/msgq/tests/Makefile.am +++ b/src/bin/msgq/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am index 446c002b8f..0ce992d5d7 100644 --- a/src/bin/tests/Makefile.am +++ b/src/bin/tests/Makefile.am @@ -8,7 +8,7 @@ noinst_SCRIPTS = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am index 2f318083eb..3d560093ec 100644 --- a/src/bin/xfrin/tests/Makefile.am +++ b/src/bin/xfrin/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index 255478a5ee..2e22e6496c 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -6,7 +6,7 @@ noinst_SCRIPTS = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/zonemgr/tests/Makefile.am b/src/bin/zonemgr/tests/Makefile.am index 6e8c35b913..769d332b87 100644 --- a/src/bin/zonemgr/tests/Makefile.am +++ b/src/bin/zonemgr/tests/Makefile.am @@ -7,7 +7,7 @@ CLEANFILES = initdb.file # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am index 5a193b855c..e0a1895d70 100644 --- a/src/lib/python/isc/acl/tests/Makefile.am +++ b/src/lib/python/isc/acl/tests/Makefile.am @@ -7,7 +7,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/bind10/tests/Makefile.am b/src/lib/python/isc/bind10/tests/Makefile.am index 0cc12ff48a..df8ab30e21 100644 --- a/src/lib/python/isc/bind10/tests/Makefile.am +++ b/src/lib/python/isc/bind10/tests/Makefile.am @@ -9,7 +9,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/cc/tests/Makefile.am b/src/lib/python/isc/cc/tests/Makefile.am index 2dc6a58f38..4c2acc05d4 100644 --- a/src/lib/python/isc/cc/tests/Makefile.am +++ b/src/lib/python/isc/cc/tests/Makefile.am @@ -10,7 +10,7 @@ EXTRA_DIST += test_session.py # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/config/tests/Makefile.am b/src/lib/python/isc/config/tests/Makefile.am index 7b48f43ad4..6670ee7254 100644 --- a/src/lib/python/isc/config/tests/Makefile.am +++ b/src/lib/python/isc/config/tests/Makefile.am @@ -8,7 +8,7 @@ EXTRA_DIST += unittest_fakesession.py # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am index 8fa3746956..170eee6cb0 100644 --- a/src/lib/python/isc/log/tests/Makefile.am +++ b/src/lib/python/isc/log/tests/Makefile.am @@ -8,7 +8,7 @@ EXTRA_DIST = console.out check_output.sh $(PYTESTS_NOGEN) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/net/tests/Makefile.am b/src/lib/python/isc/net/tests/Makefile.am index 371df59bb5..dd949464ea 100644 --- a/src/lib/python/isc/net/tests/Makefile.am +++ b/src/lib/python/isc/net/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/notify/tests/Makefile.am b/src/lib/python/isc/notify/tests/Makefile.am index 2f4e060ff2..00c2eee95a 100644 --- a/src/lib/python/isc/notify/tests/Makefile.am +++ b/src/lib/python/isc/notify/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/util/tests/Makefile.am b/src/lib/python/isc/util/tests/Makefile.am index db44c864ff..3b882b4c02 100644 --- a/src/lib/python/isc/util/tests/Makefile.am +++ b/src/lib/python/isc/util/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS From f0ff0a2f69bcfae3e2a30a3bdeae37b475ae9106 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Tue, 27 Sep 2011 14:13:20 +0100 Subject: [PATCH 819/974] [1202] Remove include of rrtype.h from gen-rdatacode.py.in This change made as result of review. --- src/lib/dns/gen-rdatacode.py.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in index c984464f14..f3cd5df81a 100755 --- a/src/lib/dns/gen-rdatacode.py.in +++ b/src/lib/dns/gen-rdatacode.py.in @@ -41,10 +41,6 @@ heading_txt = '''/////////////// /////////////// /////////////// -''' -rrtypeh_txt = '''// Many RR implementation files require definition of RR types. -#include - ''' def import_classdef(class_txt, file): @@ -204,7 +200,6 @@ def generate_rdatadef(file, basemtime): return rdata_deffile = open(file, 'w') rdata_deffile.write(heading_txt) - rdata_deffile.write(rrtypeh_txt) rdata_deffile.write(class_definitions) rdata_deffile.close() From 7dfa14ccdb6777ccacb99fe0d716b7d63654426f Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 27 Sep 2011 06:20:55 -0700 Subject: [PATCH 820/974] [master] needed to update run_bind10.sh as well --- src/bin/bind10/run_bind10.sh.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in index 9ed0ceaaaf..50e6e29e2d 100755 --- a/src/bin/bind10/run_bind10.sh.in +++ b/src/bin/bind10/run_bind10.sh.in @@ -23,14 +23,14 @@ 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/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH export PATH -PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs: +PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs export PYTHONPATH # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@ if test $SET_ENV_LIBRARY_PATH = yes; then - @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ + @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@ export @ENV_LIBRARY_PATH@ fi From 9c95bf79406ae791e2f8c7263ff4fddb19d0eda4 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 27 Sep 2011 11:24:43 -0400 Subject: [PATCH 821/974] [1144] style/cosmetic changes in ds_43.{h,cc} and dlv_32769.{h,cc} --- src/lib/dns/rdata/generic/dlv_32769.cc | 5 +---- src/lib/dns/rdata/generic/dlv_32769.h | 2 +- src/lib/dns/rdata/generic/ds_43.cc | 5 +---- src/lib/dns/rdata/generic/ds_43.h | 2 +- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/lib/dns/rdata/generic/dlv_32769.cc b/src/lib/dns/rdata/generic/dlv_32769.cc index 4e2d780397..9887aa88bd 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.cc +++ b/src/lib/dns/rdata/generic/dlv_32769.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -21,9 +21,6 @@ #include #include -#include -#include - #include using namespace std; diff --git a/src/lib/dns/rdata/generic/dlv_32769.h b/src/lib/dns/rdata/generic/dlv_32769.h index 30128fb241..86cd98ce05 100644 --- a/src/lib/dns/rdata/generic/dlv_32769.h +++ b/src/lib/dns/rdata/generic/dlv_32769.h @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc index 5535d9d279..20b62dca83 100644 --- a/src/lib/dns/rdata/generic/ds_43.cc +++ b/src/lib/dns/rdata/generic/ds_43.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -23,9 +23,6 @@ #include -#include -#include - using namespace std; using namespace isc::util; using namespace isc::util::encode; diff --git a/src/lib/dns/rdata/generic/ds_43.h b/src/lib/dns/rdata/generic/ds_43.h index 52f2f0b3cd..2697f513be 100644 --- a/src/lib/dns/rdata/generic/ds_43.h +++ b/src/lib/dns/rdata/generic/ds_43.h @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above From 5e5743ecb40da81c4e8ad27ac8b158c9a7aaff87 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 27 Sep 2011 11:26:10 -0400 Subject: [PATCH 822/974] [1144] proposed new DLV entry in ChangeLog --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5a145584ce..f2c3edc736 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +278. [func] dvv + Implement the DLV rrtype according to RFC4431. + (Trac #1144, git TBD) + 277. [func] jerry Implement the SRV rrtype according to RFC2782. (Trac #1128, git 5fd94aa027828c50e63ae1073d9d6708e0a9c223) From 0b46c391a973bb8d3f0a1681eb0a79e8a196f0f0 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 27 Sep 2011 11:28:18 -0400 Subject: [PATCH 823/974] [1144] src/lib/dns/rdata/generic/detail/ds_like.h: bug fixed for a faling unittest case + cosmetic/style change src/lib/dns/tests/rdata_ds_like_unittest.cc: a previously failing test case enabled --- src/lib/dns/rdata/generic/detail/ds_like.h | 12 ++++++++++- src/lib/dns/tests/rdata_ds_like_unittest.cc | 22 ++++++++------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h index da3ade4338..1061ece2f1 100644 --- a/src/lib/dns/rdata/generic/detail/ds_like.h +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -64,10 +64,12 @@ public: /// parameter data for any of the number of reasons. DSLikeImpl(const std::string& ds_str) { std::istringstream iss(ds_str); + // peekc should be of iss's char_type for isspace to work + std::istringstream::char_type peekc; std::stringbuf digestbuf; uint32_t tag, algorithm, digest_type; - iss >> tag >> algorithm >> digest_type >> &digestbuf; + iss >> tag >> algorithm >> digest_type; if (iss.bad() || iss.fail()) { isc_throw(InvalidRdataText, "Invalid " << RRType(typeCode) << " text"); @@ -85,6 +87,14 @@ public: RRType(typeCode) << " digest type out of range"); } + peekc = iss.peek(); + if (!iss.good() || !isspace(peekc, iss.getloc())) { + isc_throw(InvalidRdataText, + RRType(typeCode) << " presentation format error"); + } + + iss >> &digestbuf; + tag_ = tag; algorithm_ = algorithm; digest_type_ = digest_type; diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc index daf0dd48c8..95610e1edc 100644 --- a/src/lib/dns/tests/rdata_ds_like_unittest.cc +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -62,8 +62,7 @@ typedef testing::Types Implementations; TYPED_TEST_CASE(Rdata_DS_LIKE_Test, Implementations); TYPED_TEST(Rdata_DS_LIKE_Test, toText_DS_LIKE) { - EXPECT_EQ(ds_like_txt, - Rdata_DS_LIKE_Test::rdata_ds_like.toText()); + EXPECT_EQ(ds_like_txt, this->rdata_ds_like.toText()); } TYPED_TEST(Rdata_DS_LIKE_Test, badText_DS_LIKE) { @@ -74,11 +73,6 @@ TYPED_TEST(Rdata_DS_LIKE_Test, badText_DS_LIKE) { InvalidRdataText); EXPECT_THROW(const TypeParam ds_like2("11111 5 2"), InvalidRdataText); EXPECT_THROW(const TypeParam ds_like2("GARBAGE IN"), InvalidRdataText); -} - -// this test currently fails; we must fix it, and then migrate the test to -// badText_DS_LIKE -TYPED_TEST(Rdata_DS_LIKE_Test, DISABLED_badText_DS_LIKE) { // no space between the digest type and the digest. EXPECT_THROW(const TypeParam ds_like2( "12892 5 2F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" @@ -86,7 +80,7 @@ TYPED_TEST(Rdata_DS_LIKE_Test, DISABLED_badText_DS_LIKE) { } TYPED_TEST(Rdata_DS_LIKE_Test, createFromWire_DS_LIKE) { - EXPECT_EQ(0, Rdata_DS_LIKE_Test::rdata_ds_like.compare( + EXPECT_EQ(0, this->rdata_ds_like.compare( *this->rdataFactoryFromFile(RRTYPE(), RRClass::IN(), "rdata_ds_fromWire"))); } @@ -109,26 +103,26 @@ TYPED_TEST(Rdata_DS_LIKE_Test, assignment_DS_LIKE) { } TYPED_TEST(Rdata_DS_LIKE_Test, getTag_DS_LIKE) { - EXPECT_EQ(12892, Rdata_DS_LIKE_Test::rdata_ds_like.getTag()); + EXPECT_EQ(12892, this->rdata_ds_like.getTag()); } TYPED_TEST(Rdata_DS_LIKE_Test, toWireRenderer) { Rdata_DS_LIKE_Test::renderer.skip(2); TypeParam rdata_ds_like(ds_like_txt); - rdata_ds_like.toWire(Rdata_DS_LIKE_Test::renderer); + rdata_ds_like.toWire(this->renderer); vector data; UnitTestUtil::readWireData("rdata_ds_fromWire", data); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, static_cast - (Rdata_DS_LIKE_Test::obuffer.getData()) + 2, - Rdata_DS_LIKE_Test::obuffer.getLength() - 2, + (this->obuffer.getData()) + 2, + this->obuffer.getLength() - 2, &data[2], data.size() - 2); } TYPED_TEST(Rdata_DS_LIKE_Test, toWireBuffer) { TypeParam rdata_ds_like(ds_like_txt); - rdata_ds_like.toWire(Rdata_DS_LIKE_Test::obuffer); + rdata_ds_like.toWire(this->obuffer); } TYPED_TEST(Rdata_DS_LIKE_Test, compare) { From 000164d51a974acf3846a6b0a7795f484e915161 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 27 Sep 2011 09:37:33 -0700 Subject: [PATCH 824/974] [1177] a bit more suggested editorial nits --- src/lib/datasrc/zone.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h index 36b2d4215b..c83b14b779 100644 --- a/src/lib/datasrc/zone.h +++ b/src/lib/datasrc/zone.h @@ -70,12 +70,11 @@ public: /// that matched the query name). In case of an empty nonterminal, /// an NSEC is provided for the interval where the empty nonterminal /// lives. The end of the interval is the subdomain causing existence - /// of the empty nonterminal (if there's sub.x.example.com, and no - /// x.example.com, then x.example.com exists implicitly - is the empty + /// of the empty nonterminal (if there's sub.x.example.com, and no record + /// in x.example.com, then x.example.com exists implicitly - is the empty /// nonterminal and sub.x.example.com is the subdomain causing it). /// - /// Examples: if zone "example.com" has the following - /// record: + /// Examples: if zone "example.com" has the following record: /// \code /// a.b.example.com. NSEC c.example.com. /// \endcode From 9797d47ab90761c50020f78d5a55fb2672ffd7c0 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 27 Sep 2011 09:50:43 -0700 Subject: [PATCH 825/974] [master] yet more path additions needed (also a hidden one) --- src/bin/loadzone/run_loadzone.sh.in | 2 +- src/bin/stats/tests/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/loadzone/run_loadzone.sh.in b/src/bin/loadzone/run_loadzone.sh.in index e6db99c7e7..43b79206c2 100755 --- a/src/bin/loadzone/run_loadzone.sh.in +++ b/src/bin/loadzone/run_loadzone.sh.in @@ -25,7 +25,7 @@ export PYTHONPATH # required by loadable python modules. SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@ if test $SET_ENV_LIBRARY_PATH = yes; then - @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ + @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@ export @ENV_LIBRARY_PATH@ fi diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index bb9369fba6..ee79de2f0d 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -8,7 +8,7 @@ CLEANFILES = fake_time.pyc fake_socket.pyc fake_select.pyc # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS From 05eaa177051b212669c2a7b9e2194c3e9ba47f14 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 27 Sep 2011 10:09:50 -0700 Subject: [PATCH 826/974] [master] and more systest works again now --- src/bin/bindctl/run_bindctl.sh.in | 2 +- src/bin/cmdctl/run_b10-cmdctl.sh.in | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/bin/bindctl/run_bindctl.sh.in b/src/bin/bindctl/run_bindctl.sh.in index f7d7e7828e..f4cc40c718 100755 --- a/src/bin/bindctl/run_bindctl.sh.in +++ b/src/bin/bindctl/run_bindctl.sh.in @@ -27,7 +27,7 @@ export PYTHONPATH # required by loadable python modules. SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@ if test $SET_ENV_LIBRARY_PATH = yes; then - @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ + @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@ export @ENV_LIBRARY_PATH@ fi diff --git a/src/bin/cmdctl/run_b10-cmdctl.sh.in b/src/bin/cmdctl/run_b10-cmdctl.sh.in index 6a519e1207..7e632499db 100644 --- a/src/bin/cmdctl/run_b10-cmdctl.sh.in +++ b/src/bin/cmdctl/run_b10-cmdctl.sh.in @@ -19,9 +19,17 @@ PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@} export PYTHON_EXEC CMD_CTRLD_PATH=@abs_top_builddir@/src/bin/cmdctl -PYTHONPATH=@abs_top_srcdir@/src/lib/python +PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs export PYTHONPATH +# If necessary (rare cases), explicitly specify paths to dynamic libraries +# required by loadable python modules. +SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@ +if test $SET_ENV_LIBRARY_PATH = yes; then + @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@ + export @ENV_LIBRARY_PATH@ +fi + BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket export BIND10_MSGQ_SOCKET_FILE From 5e14c4caafaa44b92134c5df01b726f435f46845 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 28 Sep 2011 09:04:20 +0200 Subject: [PATCH 827/974] [1206] forgot to commit these doc additions --- src/lib/datasrc/factory.cc | 2 ++ src/lib/datasrc/memory_datasrc.h | 19 +++++++++++++++++++ src/lib/datasrc/sqlite3_accessor.cc | 2 +- src/lib/datasrc/sqlite3_accessor.h | 8 ++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index 3126a0424a..e464c89fa1 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -19,6 +19,8 @@ #include "sqlite3_accessor.h" #include "memory_datasrc.h" +#include + #include using namespace isc::data; diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h index 6f15b83c60..4a6641d59e 100644 --- a/src/lib/datasrc/memory_datasrc.h +++ b/src/lib/datasrc/memory_datasrc.h @@ -286,8 +286,27 @@ private: InMemoryClientImpl* impl_; }; +/// \brief Creates an instance of the Memory datasource client +/// +/// Currently the configuration passed here must be a MapElement, formed as +/// follows: +/// \code +/// { "type": string ("memory"), +/// "class": string ("IN"/"CH"/etc), +/// "zones": list +/// } +/// Zones list is a list of maps: +/// { "origin": string, +/// "file": string +/// } +/// \endcode +/// (i.e. the configuration that was used prior to the datasource refactor) +/// +/// This configuration setup is currently under discussion and will change in +/// the near future. extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr config); +/// \brief Destroy the instance created by createInstance() extern "C" void destroyInstance(DataSourceClient* instance); diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 863da1028f..5ee58d4a85 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -674,7 +674,7 @@ addError(ElementPtr errors, const std::string& error) { bool checkConfig(ConstElementPtr config, ElementPtr errors) { /* Specific configuration is under discussion, right now this accepts - * the 'old' configuration, see [TODO] + * the 'old' configuration, see header file */ bool result = true; diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 04c5377b7e..6a77a63911 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -189,8 +189,16 @@ private: const std::string database_name_; }; +/// \brief Creates an instance of the SQlite3 datasource client +/// +/// Currently the configuration passed here must be a MapElement, containing +/// one item called "database_file", whose value is a string +/// +/// This configuration setup is currently under discussion and will change in +/// the near future. extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr config); +/// \brief Destroy the instance created by createInstance() extern "C" void destroyInstance(DataSourceClient* instance); } From c24c42a5e29444313efee6528f172ad66452050d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 28 Sep 2011 14:14:48 +0000 Subject: [PATCH 828/974] [master] fix for debian/freebsd unittest linker problem --- src/lib/python/isc/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/python/isc/__init__.py b/src/lib/python/isc/__init__.py index 8fcbf4256d..ba5ebb0ad0 100644 --- a/src/lib/python/isc/__init__.py +++ b/src/lib/python/isc/__init__.py @@ -1,4 +1,7 @@ -import isc.datasrc +# On some systems, it appears the dynamic linker gets +# confused if the order is not right here +# There is probably a solution for this, but for now: +# order is imporant here! import isc.cc import isc.config -#import isc.dns +import isc.datasrc From 7592596f7a9f8dce2e5e8d9311cc40c5199c66e3 Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Wed, 28 Sep 2011 10:13:59 -0500 Subject: [PATCH 829/974] [master] spelling typo in comment --- src/lib/python/isc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/__init__.py b/src/lib/python/isc/__init__.py index ba5ebb0ad0..029f110b31 100644 --- a/src/lib/python/isc/__init__.py +++ b/src/lib/python/isc/__init__.py @@ -1,7 +1,7 @@ # On some systems, it appears the dynamic linker gets # confused if the order is not right here # There is probably a solution for this, but for now: -# order is imporant here! +# order is important here! import isc.cc import isc.config import isc.datasrc From 99be45a44f97942f9327b16aff368f1650994e0e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 28 Sep 2011 11:43:29 -0400 Subject: [PATCH 830/974] [master] make sure right libsqlite3 is used by changing the order of the imports, we make sure python doesn't use the first libsqlite3 it encounters that kind of looks right (through the soon to be obsolete sqlite3_ds.py) --- src/lib/datasrc/Makefile.am | 1 + src/lib/python/isc/datasrc/Makefile.am | 2 +- src/lib/python/isc/datasrc/__init__.py | 7 +++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index 8f8a5ce212..6b71388156 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -30,6 +30,7 @@ libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la libdatasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la libdatasrc_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la libdatasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la +libdatasrc_la_LIBADD += $(SQLITE_LIBS) BUILT_SOURCES = datasrc_messages.h datasrc_messages.cc datasrc_messages.h datasrc_messages.cc: Makefile datasrc_messages.mes diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am index e5f20c18f4..d8f6b082d5 100644 --- a/src/lib/python/isc/datasrc/Makefile.am +++ b/src/lib/python/isc/datasrc/Makefile.am @@ -25,7 +25,7 @@ datasrc_la_LDFLAGS += -module datasrc_la_LIBADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/python/libpydnspp.la datasrc_la_LIBADD += $(PYTHON_LIB) -datasrc_la_LIBADD += $(SQLITE_LIBS) +#datasrc_la_LIBADD += $(SQLITE_LIBS) EXTRA_DIST = client_inc.cc EXTRA_DIST += finder_inc.cc diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py index 05911f7a53..53711dbc1b 100644 --- a/src/lib/python/isc/datasrc/__init__.py +++ b/src/lib/python/isc/datasrc/__init__.py @@ -1,5 +1,5 @@ -from isc.datasrc.master import * -from isc.datasrc.sqlite3_ds import * +import sys +import os for base in sys.path[:]: datasrc_libdir = os.path.join(base, 'isc/datasrc/.libs') @@ -7,3 +7,6 @@ for base in sys.path[:]: sys.path.insert(0, datasrc_libdir) from datasrc import * +from isc.datasrc.sqlite3_ds import * +from isc.datasrc.master import * + From 44a44c0b568dc997e7522292212e0ef02b522f3d Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 28 Sep 2011 17:28:19 +0100 Subject: [PATCH 831/974] [1165] Corrected typos and grammatical errors. --- src/bin/xfrout/xfrout.py.in | 2 +- src/bin/xfrout/xfrout_messages.mes | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index d3d385f995..8049e29e3a 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -630,7 +630,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, # reject duplicate config if config_key in new_config: - raise XfroutConfigError('Duplicaet zone_config for ' + + raise XfroutConfigError('Duplicate zone_config for ' + str(zorigin) + '/' + str(zclass)) # create a new config entry, build any given (and known) config diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes index c2d3c7fa6b..b2e432ca5c 100644 --- a/src/bin/xfrout/xfrout_messages.mes +++ b/src/bin/xfrout/xfrout_messages.mes @@ -47,16 +47,16 @@ a valid TSIG key. There was a problem reading from the command and control channel. The most likely cause is that the msgq daemon is not running. -% XFROUT_MODULECC_SESSION_ERROR error from module config/command module: %1 +% XFROUT_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1 There was a problem in the lower level module handling configuration and control commands. This could happen for various reasons, but the most likely cause is that the configuration database contains a syntax error and xfrout -fails to start at initialization. Details error message from the module +failed to start at initialization. A detailed error message from the module will also be displayed. % XFROUT_CONFIG_ERROR error found in configuration data: %1 -The xfrout process encoutered an error in installing configuration at -startup time. Error details are included in the log message. +The xfrout process encountered an error when installing the configuration at +startup time. Details of the error are included in the log message. % XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response There was a problem reading a response from another module over the From 2418922a1389bbf265b02328f7c4f594257c4026 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 28 Sep 2011 17:16:43 -0700 Subject: [PATCH 832/974] [1165] untabified --- src/bin/xfrout/xfrout.spec.pre.in | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in index b51312beb6..0891a579c8 100644 --- a/src/bin/xfrout/xfrout.spec.pre.in +++ b/src/bin/xfrout/xfrout.spec.pre.in @@ -73,32 +73,32 @@ "item_type": "map", "item_optional": true, "item_default": { "origin": "" }, - "map_item_spec": [ - { - "item_name": "origin", - "item_type": "string", + "map_item_spec": [ + { + "item_name": "origin", + "item_type": "string", "item_optional": false, - "item_default": "" - }, - { - "item_name": "class", - "item_type": "string", + "item_default": "" + }, + { + "item_name": "class", + "item_type": "string", "item_optional": false, - "item_default": "IN" - }, - { - "item_name": "transfer_acl", - "item_type": "list", + "item_default": "IN" + }, + { + "item_name": "transfer_acl", + "item_type": "list", "item_optional": true, - "item_default": [{"action": "ACCEPT"}], - "list_item_spec": - { - "item_name": "acl_element", - "item_type": "any", - "item_optional": true - } - } - ] + "item_default": [{"action": "ACCEPT"}], + "list_item_spec": + { + "item_name": "acl_element", + "item_type": "any", + "item_optional": true + } + } + ] } } ], From b1f197c6102ae31ded2e4b61103308dcdfa72a89 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 11:57:25 +0200 Subject: [PATCH 833/974] [1259] Library for the IXFR-in --- configure.ac | 2 ++ src/lib/python/isc/Makefile.am | 2 +- src/lib/python/isc/xfrin/Makefile.am | 10 +++++++++ src/lib/python/isc/xfrin/__init__.py | 0 src/lib/python/isc/xfrin/tests/Makefile.am | 24 ++++++++++++++++++++++ 5 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/lib/python/isc/xfrin/Makefile.am create mode 100644 src/lib/python/isc/xfrin/__init__.py create mode 100644 src/lib/python/isc/xfrin/tests/Makefile.am diff --git a/configure.ac b/configure.ac index cd44b4ee29..850216870a 100644 --- a/configure.ac +++ b/configure.ac @@ -861,6 +861,8 @@ AC_CONFIG_FILES([Makefile src/lib/python/isc/testutils/Makefile src/lib/python/isc/bind10/Makefile src/lib/python/isc/bind10/tests/Makefile + src/lib/python/isc/xfrin/Makefile + src/lib/python/isc/xfrin/tests/Makefile src/lib/config/Makefile src/lib/config/tests/Makefile src/lib/config/tests/testdata/Makefile diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am index f90f7b699e..a9cc0e1b73 100644 --- a/src/lib/python/isc/Makefile.am +++ b/src/lib/python/isc/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10 -SUBDIRS += log_messages +SUBDIRS += log_messages xfrin python_PYTHON = __init__.py diff --git a/src/lib/python/isc/xfrin/Makefile.am b/src/lib/python/isc/xfrin/Makefile.am new file mode 100644 index 0000000000..5e39bedd84 --- /dev/null +++ b/src/lib/python/isc/xfrin/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = . tests + +python_PYTHON = __init__.py + +pythondir = $(pyexecdir)/isc/xfrin + +CLEANDIRS = __pycache__ + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/lib/python/isc/xfrin/__init__.py b/src/lib/python/isc/xfrin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/lib/python/isc/xfrin/tests/Makefile.am b/src/lib/python/isc/xfrin/tests/Makefile.am new file mode 100644 index 0000000000..64d6409e92 --- /dev/null +++ b/src/lib/python/isc/xfrin/tests/Makefile.am @@ -0,0 +1,24 @@ +PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ +PYTESTS = +EXTRA_DIST = $(PYTESTS) + +# If necessary (rare cases), explicitly specify paths to dynamic libraries +# required by loadable python modules. +LIBRARY_PATH_PLACEHOLDER = +if SET_ENV_LIBRARY_PATH +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) +endif + +# test using command-line arguments, so use check-local target instead of TESTS +check-local: +if ENABLE_PYTHON_COVERAGE + touch $(abs_top_srcdir)/.coverage + rm -f .coverage + ${LN_S} $(abs_top_srcdir)/.coverage .coverage +endif + for pytest in $(PYTESTS) ; do \ + echo Running test: $$pytest ; \ + $(LIBRARY_PATH_PLACEHOLDER) \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \ + $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ + done From b2d2acebebc66495b98eef634ce633eb70cc2411 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 29 Sep 2011 12:23:26 +0200 Subject: [PATCH 834/974] [1260] add ixfr_disabled option option is called ixfr_disabled, is on a per-zone basis (on the same level as master address), and is accessible as ZoneInfo.ixfr_disabled --- src/bin/xfrin/tests/xfrin_test.py | 12 ++++++++++-- src/bin/xfrin/xfrin.py.in | 11 +++++++++++ src/bin/xfrin/xfrin.spec | 5 +++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py index 92bf1b01bb..05cce986a9 100644 --- a/src/bin/xfrin/tests/xfrin_test.py +++ b/src/bin/xfrin/tests/xfrin_test.py @@ -955,13 +955,20 @@ class TestXfrin(unittest.TestCase): self.assertEqual(zone_info.tsig_key.to_text(), TSIGKey(zone_config['tsig_key']).to_text()) else: self.assertIsNone(zone_info.tsig_key) + if 'ixfr_disabled' in zone_config and\ + zone_config.get('ixfr_disabled'): + self.assertTrue(zone_info.ixfr_disabled) + else: + # if not set, should default to False + self.assertFalse(zone_info.ixfr_disabled) def test_command_handler_zones(self): config1 = { 'transfers_in': 3, 'zones': [ { 'name': 'test.example.', 'master_addr': '192.0.2.1', - 'master_port': 53 + 'master_port': 53, + 'ixfr_disabled': False } ]} self.assertEqual(self.xfr.config_handler(config1)['result'][0], 0) @@ -972,7 +979,8 @@ class TestXfrin(unittest.TestCase): { 'name': 'test.example.', 'master_addr': '192.0.2.2', 'master_port': 53, - 'tsig_key': "example.com:SFuWd/q99SzF8Yzd1QbB9g==" + 'tsig_key': "example.com:SFuWd/q99SzF8Yzd1QbB9g==", + 'ixfr_disabled': True } ]} self.assertEqual(self.xfr.config_handler(config2)['result'][0], 0) diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in index 8845b426f5..a77a383315 100755 --- a/src/bin/xfrin/xfrin.py.in +++ b/src/bin/xfrin/xfrin.py.in @@ -451,6 +451,7 @@ class ZoneInfo: self.set_master_port(config_data.get('master_port')) self.set_zone_class(config_data.get('class')) self.set_tsig_key(config_data.get('tsig_key')) + self.set_ixfr_disabled(config_data.get('ixfr_disabled')) def set_name(self, name_str): """Set the name for this zone given a name string. @@ -525,6 +526,16 @@ class ZoneInfo: errmsg = "bad TSIG key string: " + tsig_key_str raise XfrinZoneInfoException(errmsg) + def set_ixfr_disabled(self, ixfr_disabled): + """Set ixfr_disabled. If set to False (the default), it will use + IXFR for incoming transfers. If set to True, it will use AXFR. + At this moment there is no automatic fallback""" + # don't care what type it is; if evaluates to true, set to True + if ixfr_disabled: + self.ixfr_disabled = True + else: + self.ixfr_disabled = False + def get_master_addr_info(self): return (self.master_addr.family, socket.SOCK_STREAM, (str(self.master_addr), self.master_port)) diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec index a3e62cefc4..bc937205d8 100644 --- a/src/bin/xfrin/xfrin.spec +++ b/src/bin/xfrin/xfrin.spec @@ -43,6 +43,11 @@ { "item_name": "tsig_key", "item_type": "string", "item_optional": true + }, + { "item_name": "ixfr_disabled", + "item_type": "boolean", + "item_optional": false, + "item_default": false } ] } From 16cc75f764b6ea509f386c261b472e282cd606ed Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 12:34:01 +0200 Subject: [PATCH 835/974] [1259] Interface of the diff --- src/lib/python/isc/xfrin/Makefile.am | 2 +- src/lib/python/isc/xfrin/diff.py | 100 +++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/lib/python/isc/xfrin/diff.py diff --git a/src/lib/python/isc/xfrin/Makefile.am b/src/lib/python/isc/xfrin/Makefile.am index 5e39bedd84..8b453482e6 100644 --- a/src/lib/python/isc/xfrin/Makefile.am +++ b/src/lib/python/isc/xfrin/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS = . tests -python_PYTHON = __init__.py +python_PYTHON = __init__.py diff.py pythondir = $(pyexecdir)/isc/xfrin diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py new file mode 100644 index 0000000000..d910938780 --- /dev/null +++ b/src/lib/python/isc/xfrin/diff.py @@ -0,0 +1,100 @@ +# 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 helps the XFR in process with accumulating parts of diff and applying +it to the datasource. +""" + +class NoSuchZone(Exception): + """ + This is raised if a diff for non-existant zone is being created. + """ + pass + +class Diff: + """ + The class represents a diff against current state of datasource on + one zone. The usual way of working with it is creating it, then putting + bunch of changes in and commiting at the end. + + If you change your mind, you can just stop using the object without + really commiting it. In that case no changes will happen in the data + sounce. + + The class works as a kind of a buffer as well, it does not direct + the changes to underlying data source right away, but keeps them for + a while. + """ + def __init__(self, datasource, zone): + """ + Initializes the diff to a ready state. It checks the zone exists + in the datasource and if not, NoSuchZone is raised. This also creates + a transaction in the data source. + + The datasource is the one containing the zone. Zone is isc.dns.Name + object representing the name of the zone (its apex). + + You can also expect isc.datasrc.Error or isc.datasrc.NotImplemented + exceptions. + """ + pass + + def add_data(self, rr): + """ + Schedules addition of an RR into the zone in this diff. + + The rr is of isc.dns.RRset type and it must contain only one RR. + If this is not the case or if the diff was already commited, this + raises the ValueError exception. + """ + pass + + def remove_data(self, rr): + """ + Schedules removal of an RR from the zone in this diff. + + The rr is of isc.dns.RRset type and it must contain only one RR. + If this is not the case or if the diff was already commited, this + raises the ValueError exception. + """ + pass + + def apply(self): + """ + Push the buffered changes inside this diff down into the data source. + This does not stop you from adding more changes later through this + diff and it does not close the datasource transaction, so the changes + will not be shown to others yet. It just means the internal memory + buffer is flushed. + + This is called from time to time automatically, but you can call it + manually if you really want to. + + This raises ValueError if the diff was already commited. + + It also can raise isc.datasrc.Error. + """ + pass + + def commit(self): + """ + Writes all the changes into the data source and makes them visible. + This closes the diff, you may not use it any more. If you try to use + it, you'll get ValueError. + + This might raise isc.datasrc.Error. + """ + pass From 66e1420d30f8e71e867a3b5b0a73ead1156d5660 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 13:11:44 +0200 Subject: [PATCH 836/974] [1259] Test for creating of the diff --- src/lib/python/isc/xfrin/tests/Makefile.am | 2 +- src/lib/python/isc/xfrin/tests/diff_tests.py | 69 ++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/lib/python/isc/xfrin/tests/diff_tests.py diff --git a/src/lib/python/isc/xfrin/tests/Makefile.am b/src/lib/python/isc/xfrin/tests/Makefile.am index 64d6409e92..e5325fccc8 100644 --- a/src/lib/python/isc/xfrin/tests/Makefile.am +++ b/src/lib/python/isc/xfrin/tests/Makefile.am @@ -1,5 +1,5 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ -PYTESTS = +PYTESTS = diff_tests.py EXTRA_DIST = $(PYTESTS) # If necessary (rare cases), explicitly specify paths to dynamic libraries diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py new file mode 100644 index 0000000000..7d34617a18 --- /dev/null +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -0,0 +1,69 @@ +# 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. + +import isc.log +import unittest +from isc.dns import Name +from isc.xfrin.diff import Diff, NoSuchZone + +class DiffTest(unittest.TestCase): + """ + Tests for the isc.xfrin.diff.Diff class. + + It also plays role of a data source and an updater, so it can manipulate + some test variables while being called. + """ + def setUp(self): + """ + This sets internal variables so we can see nothing was called yet. + """ + self.__updater_requested = False + + def get_updater(self, zone_name, replace): + """ + This one pretends this is the data source client and serves + getting an updater. + + If zone_name is 'none.example.org.', it returns None, otherwise + it returns self. + """ + # The diff should not delete the old data. + self.assertFalse(replace) + self.__updater_requested = True + # Pretend this zone doesn't exist + if zone_name == Name('none.example.org.'): + return None + else: + return self + + def test_create(self): + """ + This test the case when the diff is successfuly created. It just + tries it does not throw and gets the updater. + """ + diff = Diff(self, Name('example.org.')) + self.assertTrue(self.__updater_requested) + + def test_create_nonexist(self): + """ + Try to create a diff on a zone that doesn't exist. This should + raise a correct exception. + """ + self.assertRaises(NoSuchZone, Diff, self, Name('none.example.org.')) + self.assertTrue(self.__updater_requested) + +if __name__ == "__main__": + isc.log.init("bind10") + unittest.main() From 85455b6e2f7063b10bae9938de1b70f5d319911e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 13:14:36 +0200 Subject: [PATCH 837/974] [1259] Creation of the Diff --- src/lib/python/isc/xfrin/diff.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index d910938780..faa281ced0 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -50,7 +50,12 @@ class Diff: You can also expect isc.datasrc.Error or isc.datasrc.NotImplemented exceptions. """ - pass + self.__updater = datasource.get_updater(zone, False) + if self.__updater is None: + # The no such zone case + raise NoSuchZone("Zone " + str(zone) + + " does not exist in the data source " + + str(datasource)) def add_data(self, rr): """ From e2eca96f1876a72fc8c121c9204d49cb7e9eaeb7 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 13:23:47 +0200 Subject: [PATCH 838/974] [1259] Add the buffer into diff --- src/lib/python/isc/xfrin/diff.py | 11 +++++++++++ src/lib/python/isc/xfrin/tests/diff_tests.py | 1 + 2 files changed, 12 insertions(+) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index faa281ced0..a4d41fc04b 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -56,6 +56,7 @@ class Diff: raise NoSuchZone("Zone " + str(zone) + " does not exist in the data source " + str(datasource)) + self.__buffer = [] def add_data(self, rr): """ @@ -103,3 +104,13 @@ class Diff: This might raise isc.datasrc.Error. """ pass + + def get_buffer(self): + """ + Returns the current buffer of changes not yet passed into the data + source. It is in a form like [('add', rrset), ('remove', rrset), + ('remove', rrset), ...]. + + Probably useful only for testing and introspection purposes. + """ + return self.__buffer diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 7d34617a18..17335d982d 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -55,6 +55,7 @@ class DiffTest(unittest.TestCase): """ diff = Diff(self, Name('example.org.')) self.assertTrue(self.__updater_requested) + self.assertEqual([], diff.get_buffer()) def test_create_nonexist(self): """ From 4e458fc15b5c236e1cc44565f6af313753e87a26 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 13:40:25 +0200 Subject: [PATCH 839/974] [1259] Test for add_data --- src/lib/python/isc/xfrin/tests/diff_tests.py | 48 +++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 17335d982d..e4181c96d4 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -15,7 +15,7 @@ import isc.log import unittest -from isc.dns import Name +from isc.dns import Name, RRset, RRClass, RRType, RRTTL, Rdata from isc.xfrin.diff import Diff, NoSuchZone class DiffTest(unittest.TestCase): @@ -28,8 +28,32 @@ class DiffTest(unittest.TestCase): def setUp(self): """ This sets internal variables so we can see nothing was called yet. + + It also creates some variables used in multiple tests. """ + # Track what was called already self.__updater_requested = False + # Some common values + self.__rrclass = RRClass.IN() + self.__type = RRType.A() + self.__ttl = RRTTL(3600) + # And RRsets + # Create two valid rrsets + self.__rrset1 = RRset(Name('a.example.org.'), self.__rrclass, + self.__type, self.__ttl) + self.__rdata = Rdata(self.__type, self.__rrclass, '192.0.2.1') + self.__rrset1.add_rdata(self.__rdata) + self.__rrset2 = RRset(Name('b.example.org.'), self.__rrclass, + self.__type, self.__ttl) + self.__rrset2.add_rdata(self.__rdata) + # And two invalid + self.__rrset_empty = RRset(Name('empty.example.org.'), self.__rrclass, + self.__type, self.__ttl) + self.__rrset_multi = RRset(Name('multi.example.org.'), self.__rrclass, + self.__type, self.__ttl) + self.__rrset_multi.add_rdata(self.__rdata) + self.__rrset_multi.add_rdata(Rdata(self.__type, self.__rrclass, + '192.0.2.2')) def get_updater(self, zone_name, replace): """ @@ -65,6 +89,28 @@ class DiffTest(unittest.TestCase): self.assertRaises(NoSuchZone, Diff, self, Name('none.example.org.')) self.assertTrue(self.__updater_requested) + def test_add(self): + """ + Try to add few items into the diff and see they are stored in there. + + Also try passing an rrset that has more differnt amount of RRs than 1. + """ + diff = Diff(self, Name('example.org.')) + # Try putting there the bad data first + self.assertRaises(ValueError, diff.add_data, self.__rrset_empty) + self.assertRaises(ValueError, diff.add_data, self.__rrset_multi) + # They were not added + self.assertEqual([], diff.get_buffer()) + # Add some proper data + diff.add_data(self.__rrset1) + diff.add_data(self.__rrset2) + dlist = [('add', self.__rrset1), ('add', self.__rrset2)] + self.assertEqual(dlist, diff.get_buffer()) + # Check the data are not destroyed by raising an exception because of + # bad data + self.assertRaises(ValueError, diff.add_data, self.__rrset_empty) + self.assertEqual(dlist, diff.get_buffer()) + if __name__ == "__main__": isc.log.init("bind10") unittest.main() From f59415a8b5ee951dd298eaf8eecaa21e8955851c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 13:47:35 +0200 Subject: [PATCH 840/974] [1259] Adding data --- src/lib/python/isc/xfrin/diff.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index a4d41fc04b..1d4747e1fd 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -66,7 +66,10 @@ class Diff: If this is not the case or if the diff was already commited, this raises the ValueError exception. """ - pass + if rr.get_rdata_count() != 1: + raise ValueError('The rrset must contain exactly 1 Rdata, but ' + + 'it holds ' + str(rr.get_rdata_count())) + self.__buffer.append(('add', rr)) def remove_data(self, rr): """ From 0a22b98c05bf5032c190fbfdf9fefceac3597411 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 13:52:56 +0200 Subject: [PATCH 841/974] [1259] Test for remove_data --- src/lib/python/isc/xfrin/tests/diff_tests.py | 46 +++++++++++++------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index e4181c96d4..94985a1933 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -89,27 +89,43 @@ class DiffTest(unittest.TestCase): self.assertRaises(NoSuchZone, Diff, self, Name('none.example.org.')) self.assertTrue(self.__updater_requested) + def __data_common(self, diff, method, name): + """ + Common part of test for test_add and test_remove. + """ + # Try putting there the bad data first + self.assertRaises(ValueError, method, self.__rrset_empty) + self.assertRaises(ValueError, method, self.__rrset_multi) + # They were not added + self.assertEqual([], diff.get_buffer()) + # Add some proper data + method(self.__rrset1) + method(self.__rrset2) + dlist = [(name, self.__rrset1), (name, self.__rrset2)] + self.assertEqual(dlist, diff.get_buffer()) + # Check the data are not destroyed by raising an exception because of + # bad data + self.assertRaises(ValueError, method, self.__rrset_empty) + self.assertEqual(dlist, diff.get_buffer()) + def test_add(self): """ Try to add few items into the diff and see they are stored in there. - Also try passing an rrset that has more differnt amount of RRs than 1. + Also try passing an rrset that has differnt amount of RRs than 1. """ diff = Diff(self, Name('example.org.')) - # Try putting there the bad data first - self.assertRaises(ValueError, diff.add_data, self.__rrset_empty) - self.assertRaises(ValueError, diff.add_data, self.__rrset_multi) - # They were not added - self.assertEqual([], diff.get_buffer()) - # Add some proper data - diff.add_data(self.__rrset1) - diff.add_data(self.__rrset2) - dlist = [('add', self.__rrset1), ('add', self.__rrset2)] - self.assertEqual(dlist, diff.get_buffer()) - # Check the data are not destroyed by raising an exception because of - # bad data - self.assertRaises(ValueError, diff.add_data, self.__rrset_empty) - self.assertEqual(dlist, diff.get_buffer()) + self.__data_common(diff, diff.add_data, 'add') + + def test_remove(self): + """ + Try scheduling removal of few items into the diff and see they are + stored in there. + + Also try passing an rrset that has different amount of RRs than 1. + """ + diff = Diff(self, Name('example.org.')) + self.__data_common(diff, diff.remove_data, 'remove') if __name__ == "__main__": isc.log.init("bind10") From 638674c480d47cf957a8b4f7d61dda3320c881ff Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 14:25:16 +0200 Subject: [PATCH 842/974] [1259] Little interface update on Diff It now has the compact method, which is used internally mostly, but can be called explicitly. Also a note about exception was added. --- src/lib/python/isc/xfrin/diff.py | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 1d4747e1fd..7859274d00 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -58,6 +58,18 @@ class Diff: str(datasource)) self.__buffer = [] + def __data_common(self, rr, operation): + """ + Schedules an operation with rr. + + It does all the real work of add_data and remove_data, including + all checks. + """ + if rr.get_rdata_count() != 1: + raise ValueError('The rrset must contain exactly 1 Rdata, but ' + + 'it holds ' + str(rr.get_rdata_count())) + self.__buffer.append((operation, rr)) + def add_data(self, rr): """ Schedules addition of an RR into the zone in this diff. @@ -66,10 +78,7 @@ class Diff: If this is not the case or if the diff was already commited, this raises the ValueError exception. """ - if rr.get_rdata_count() != 1: - raise ValueError('The rrset must contain exactly 1 Rdata, but ' + - 'it holds ' + str(rr.get_rdata_count())) - self.__buffer.append(('add', rr)) + self.__data_common(rr, 'add') def remove_data(self, rr): """ @@ -79,6 +88,17 @@ class Diff: If this is not the case or if the diff was already commited, this raises the ValueError exception. """ + self.__data_common(rr, 'remove') + + def compact(self): + """ + Tries to compact the operations in buffer a little by putting some of + the operations together, forming RRsets with more than one RR. + + This is called by apply before putting the data into datasource. + + It is currently empty and needs implementing. + """ pass def apply(self): @@ -94,7 +114,8 @@ class Diff: This raises ValueError if the diff was already commited. - It also can raise isc.datasrc.Error. + It also can raise isc.datasrc.Error. If that happens, you should stop + using this object and abort the modification. """ pass @@ -114,6 +135,7 @@ class Diff: source. It is in a form like [('add', rrset), ('remove', rrset), ('remove', rrset), ...]. - Probably useful only for testing and introspection purposes. + Probably useful only for testing and introspection purposes. Don't + modify the list. """ return self.__buffer From e7f1ead205f2dc13d6fd6e2a28b121794ca281be Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 14:26:26 +0200 Subject: [PATCH 843/974] [1259] Tests for the apply method With the minimal dependencies on the other methods, except for adding and removing. --- src/lib/python/isc/xfrin/tests/diff_tests.py | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 94985a1933..959841228e 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -33,6 +33,8 @@ class DiffTest(unittest.TestCase): """ # Track what was called already self.__updater_requested = False + self.__compact_called = False + self.__data_operations = [] # Some common values self.__rrclass = RRClass.IN() self.__type = RRType.A() @@ -55,6 +57,27 @@ class DiffTest(unittest.TestCase): self.__rrset_multi.add_rdata(Rdata(self.__type, self.__rrclass, '192.0.2.2')) + def __mock_compact(self): + """ + This can be put into the diff to hook into it's compact method and see + if it gets called. + """ + self.__compact_called = True + + def add_rrset(self, rrset): + """ + This one is part of pretending to be a zone updater. It writes down + addition of an rrset was requested. + """ + self.__data_operations.append(('add', rrset)) + + def remove_rrset(self, rrset): + """ + This one is part of pretending to be a zone updater. It writes down + removal of an rrset was requested. + """ + self.__data_operations.append(('remove', rrset)) + def get_updater(self, zone_name, replace): """ This one pretends this is the data source client and serves @@ -127,6 +150,29 @@ class DiffTest(unittest.TestCase): diff = Diff(self, Name('example.org.')) self.__data_common(diff, diff.remove_data, 'remove') + def test_apply(self): + """ + Schedule few additions and check the apply works by passing the + data into the updater. + """ + # Prepare the diff + diff = Diff(self, Name('example.org.')) + diff.add_data(self.__rrset1) + diff.remove_data(self.__rrset2) + dlist = [('add', self.__rrset1), ('remove', self.__rrset2)] + self.assertEqual(dlist, diff.get_buffer()) + # Do the apply, hook the compact method + diff.compact = self.__mock_compact + diff.apply() + # It should call the compact + self.assertTrue(self.__compact_called) + # And pass the data. Our local history of what happened is the same + # format, so we can check the same way + self.assertEqual(dlist, self.__data_operations) + # And the buffer in diff should become empty, as everything + # got inside. + self.assertEqual([], diff.get_buffer()) + if __name__ == "__main__": isc.log.init("bind10") unittest.main() From eef5b0eb5defdd22ef5e351213ab66531f788c5d Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 14:31:19 +0200 Subject: [PATCH 844/974] [1259] The apply method --- src/lib/python/isc/xfrin/diff.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 7859274d00..cf5de6abcf 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -117,7 +117,18 @@ class Diff: It also can raise isc.datasrc.Error. If that happens, you should stop using this object and abort the modification. """ - pass + # First, compact the data + self.compact() + # Then pass the data inside the data source + for (operation, rrset) in self.__buffer: + if operation == 'add': + self.__updater.add_rrset(rrset) + elif operation == 'remove': + self.__updater.remove_rrset(rrset) + else: + raise ValueError('Unknown operation ' + operation) + # As everything is already in, drop the buffer + self.__buffer = [] def commit(self): """ From 4bb4081381b39c563707c03818a0f9d16ef7846f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 14:34:26 +0200 Subject: [PATCH 845/974] [1259] Test for the commit, first part Only the commit itself, not yet that the rest of operations get disabled by it. --- src/lib/python/isc/xfrin/tests/diff_tests.py | 35 +++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 959841228e..27354018b8 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -35,6 +35,8 @@ class DiffTest(unittest.TestCase): self.__updater_requested = False self.__compact_called = False self.__data_operations = [] + self.__apply_called = False + self.__commit_called = False # Some common values self.__rrclass = RRClass.IN() self.__type = RRType.A() @@ -59,11 +61,25 @@ class DiffTest(unittest.TestCase): def __mock_compact(self): """ - This can be put into the diff to hook into it's compact method and see + This can be put into the diff to hook into its compact method and see if it gets called. """ self.__compact_called = True + def __mock_apply(self): + """ + This can be put into the diff to hook into its apply method and see + it gets called. + """ + self.__apply_called = True + + def commit(self): + """ + This is part of pretending to be a zone updater. This notes the commit + was called. + """ + self.__commit_called = True + def add_rrset(self, rrset): """ This one is part of pretending to be a zone updater. It writes down @@ -173,6 +189,23 @@ class DiffTest(unittest.TestCase): # got inside. self.assertEqual([], diff.get_buffer()) + def test_commit(self): + """ + If we call a commit, it should first apply whatever changes are + left (we hook into that instead of checking the effect) and then + the commit on the updater should have been called. + + Then we check it raises value error for whatever operation we try. + """ + diff = Diff(self, Name('example.org.')) + diff.add_data(self.__rrset1) + diff.apply = self.__mock_apply + diff.commit() + self.assertTrue(self.__apply_called) + self.assertTrue(self.__commit_called) + # The data should be handled by apply which we replaced. + self.assertEqual([], self.__data_operations) + if __name__ == "__main__": isc.log.init("bind10") unittest.main() From 9b23d60d6f58b18da3995dc3e090d7fd63233bcc Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Thu, 29 Sep 2011 14:38:31 +0200 Subject: [PATCH 846/974] [1259] The commit of transaction --- src/lib/python/isc/xfrin/diff.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index cf5de6abcf..38b0e108dc 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -138,7 +138,8 @@ class Diff: This might raise isc.datasrc.Error. """ - pass + self.apply() + self.__updater.commit() def get_buffer(self): """ From c69a1675dd0434db0b99682d14fa7905fcd3af8f Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Thu, 29 Sep 2011 13:46:52 +0100 Subject: [PATCH 847/974] [master] ChnageLog for trac1202 --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index d0565e18c3..2d4ad2b6e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +288. [bug] stephen + Fixed problem whereby the order in which component files appeared in + rdataclass.cc was system dependent, leading to problems on some + systems where data types were used before the header file in which + they were declared was included. + (Trac #1202, git 4a605525cda67bea8c43ca8b3eae6e6749797450) + 287. [bug]* jinmei Python script files for log messages (xxx_messages.py) should have been installed under the "isc" package. This fix itself should From 33ee923f7139cbda7a616a83d572a4358f456e16 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 29 Sep 2011 15:10:49 +0200 Subject: [PATCH 848/974] [1206] comments pt1 --- src/lib/datasrc/factory.cc | 8 +++++--- src/lib/datasrc/factory.h | 36 ++++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index e464c89fa1..8ccf27fd39 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -51,7 +51,7 @@ LibraryContainer::getSym(const char* name) { const char* dlsym_error = dlerror(); if (dlsym_error != NULL) { - isc_throw(DataSourceLibraryError, dlsym_error); + isc_throw(DataSourceLibrarySymbolError, dlsym_error); } return (sym); @@ -61,8 +61,10 @@ DataSourceClientContainer::DataSourceClientContainer(const std::string& type, ConstElementPtr config) : ds_lib_(type + "_ds.so") { - ds_creator* ds_create = (ds_creator*)ds_lib_.getSym("createInstance"); - destructor_ = (ds_destructor*)ds_lib_.getSym("destroyInstance"); + ds_creator* ds_create = + reinterpret_cast(ds_lib_.getSym("createInstance")); + destructor_ = + reinterpret_cast(ds_lib_.getSym("destroyInstance")); instance_ = ds_create(config); } diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index 813b6d75ff..eff8891ffc 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -15,10 +15,7 @@ #ifndef __DATA_SOURCE_FACTORY_H #define __DATA_SOURCE_FACTORY_H 1 -//#include -//#include - -//#include +#include #include #include @@ -31,13 +28,22 @@ namespace datasrc { /// \brief Raised if there is an error loading the datasource implementation -/// library, or if that library misses a needed symbol +/// library class DataSourceLibraryError : public DataSourceError { public: DataSourceLibraryError(const char* file, size_t line, const char* what) : DataSourceError(file, line, what) {} }; +/// \brief Raised if there is an error reading a symbol from the datasource +/// implementation library +class DataSourceLibrarySymbolError : public DataSourceError { +public: + DataSourceLibrarySymbolError(const char* file, size_t line, + const char* what) : + DataSourceError(file, line, what) {} +}; + /// \brief Raised if the given config contains bad data /// /// Depending on the datasource type, the configuration may differ (for @@ -63,7 +69,7 @@ typedef void ds_destructor(DataSourceClient* instance); /// in other places than for dynamically loading datasources, then, apart /// from moving it to another location, we also need to make the /// exceptions raised more general. -class LibraryContainer { +class LibraryContainer : boost::noncopyable { public: /// \brief Constructor /// @@ -83,11 +89,19 @@ public: /// /// This retrieves a symbol from the loaded library. /// - /// \exception DataSourceLibraryError if the symbol cannot be found, or if - /// another error (as reported by dlerror() occurs. + /// \exception DataSourceLibrarySymbolError if the symbol cannot be found, + /// or if another error (as reported by dlerror() occurs. /// /// \param name The name of the symbol to retrieve - /// \return A pointer to the symbol + /// \return A pointer to the symbol. This may be NULL, and if so, indicates + /// the symbol does indeed exist, but has the value NULL itself. + /// If the symbol does not exist, a DataSourceLibrarySymbolError is + /// raised. + /// + /// \note The argument is a const char* (and not a std::string like the + /// argument in the constructor). This argument is always a fixed + /// string in the code, while the other can be read from + /// configuration, and needs modification void* getSym(const char* name); private: /// Pointer to the dynamically loaded library structure @@ -120,12 +134,14 @@ private: /// /// extern "C" void destroyInstance(isc::data::DataSourceClient* instance); /// \endcode -class DataSourceClientContainer { +class DataSourceClientContainer : boost::noncopyable { public: /// \brief Constructor /// /// \exception DataSourceLibraryError if there is an error loading the /// backend library + /// \exception DataSourceLibrarySymbolError if the library does not have + /// the needed symbols, or if there is an error reading them /// \exception DataSourceConfigError if the given config is not correct /// for the given type /// From fdf1c88a53f5970aa4e6d55da42303ce7d4730f7 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 29 Sep 2011 15:23:48 +0200 Subject: [PATCH 849/974] [1206] move checkConfig into cozy anonymous namespace --- src/lib/datasrc/memory_datasrc.cc | 4 ++-- src/lib/datasrc/sqlite3_accessor.cc | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 13ce348712..a29e90277f 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -856,8 +856,6 @@ checkZoneConfig(ConstElementPtr config, ElementPtr errors) { return result; } -} // end anonymous namespace - bool checkConfig(ConstElementPtr config, ElementPtr errors) { /* Specific configuration is under discussion, right now this accepts @@ -924,6 +922,8 @@ checkConfig(ConstElementPtr config, ElementPtr errors) { return true; } +} // end anonymous namespace + DataSourceClient * createInstance(isc::data::ConstElementPtr config) { ElementPtr errors(Element::createList()); diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 5ee58d4a85..8e9f511085 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -669,7 +669,6 @@ addError(ElementPtr errors, const std::string& error) { errors->add(Element::create(error)); } } -} // end anonymous namespace bool checkConfig(ConstElementPtr config, ElementPtr errors) { @@ -705,6 +704,8 @@ checkConfig(ConstElementPtr config, ElementPtr errors) { return (result); } +} // end anonymous namespace + DataSourceClient * createInstance(isc::data::ConstElementPtr config) { ElementPtr errors(Element::createList()); From d5a58bbe641d32257035a6087f18655e7b66d8fd Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 29 Sep 2011 11:17:37 -0500 Subject: [PATCH 850/974] [master] use single = with test Noticed on FreeBSD: test: yes: unexpected operator Trivial fix (and consistent with rest), no review. --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index cd44b4ee29..a94912ec9e 100644 --- a/configure.ac +++ b/configure.ac @@ -437,7 +437,7 @@ AC_ARG_WITH([botan], AC_HELP_STRING([--with-botan=PATH], [specify exact directory of Botan library]), [botan_path="$withval"]) -if test "${botan_path}" == "no" ; then +if test "${botan_path}" = "no" ; then AC_MSG_ERROR([Need botan for libcryptolink]) fi if test "${botan_path}" != "yes" ; then @@ -510,7 +510,7 @@ AC_ARG_WITH([log4cplus], AC_HELP_STRING([--with-log4cplus=PATH], [specify exact directory of log4cplus library and headers]), [log4cplus_path="$withval"]) -if test "${log4cplus_path}" == "no" ; then +if test "${log4cplus_path}" = "no" ; then AC_MSG_ERROR([Need log4cplus]) elif test "${log4cplus_path}" != "yes" ; then LOG4CPLUS_INCLUDES="-I${log4cplus_path}/include" From d845ae918fe8dce6806c3f927a7c101fc0e2173d Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Thu, 29 Sep 2011 12:01:22 -0500 Subject: [PATCH 851/974] [master] remove repeated sentence and regenerate nroff version of manual. --- src/bin/host/b10-host.1 | 4 ---- src/bin/host/b10-host.xml | 5 ----- 2 files changed, 9 deletions(-) diff --git a/src/bin/host/b10-host.1 b/src/bin/host/b10-host.1 index ed0068b9a1..050f6a3185 100644 --- a/src/bin/host/b10-host.1 +++ b/src/bin/host/b10-host.1 @@ -103,10 +103,6 @@ It doesn\'t use at this time\&. The default name server used is 127\&.0\&.0\&.1\&. .PP -\fBb10\-host\fR -does not do reverse lookups by default yet (by detecting if name is a IPv4 or IPv6 address)\&. -.PP - \fB\-p\fR is not a standard feature\&. .SH "HISTORY" diff --git a/src/bin/host/b10-host.xml b/src/bin/host/b10-host.xml index 7da07dd16c..a17ef67a56 100644 --- a/src/bin/host/b10-host.xml +++ b/src/bin/host/b10-host.xml @@ -175,11 +175,6 @@ The default name server used is 127.0.0.1. - - b10-host does not do reverse lookups by - default yet (by detecting if name is a IPv4 or IPv6 address). - - is not a standard feature. From 41cbf5a91bdfa0b311aade6b05d2f51f59cce978 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 10:19:44 -0700 Subject: [PATCH 852/974] [master] adjusted cppcheck filter to suppress a false alarm about a template file. trivial, directly pushing the fix. --- src/cppcheck-suppress.lst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst index a4fea30c4c..8a4c7c18de 100644 --- a/src/cppcheck-suppress.lst +++ b/src/cppcheck-suppress.lst @@ -3,7 +3,7 @@ debug missingInclude // This is a template, and should be excluded from the check -unreadVariable:src/lib/dns/rdata/template.cc:60 +unreadVariable:src/lib/dns/rdata/template.cc:61 // Intentional self assignment tests. Suppress warning about them. selfAssignment:src/lib/dns/tests/name_unittest.cc:293 selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228 From 338b54ef4631f0d35601f174eabfa10f1541f46d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 10:33:59 -0700 Subject: [PATCH 853/974] [master] add a changelog entry for #1165 --- ChangeLog | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ChangeLog b/ChangeLog index 2d4ad2b6e8..b006df1f98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +289. [func]* jinmei + b10-xfrout: ACLs for xfrout can now be configured per zone basis. + A per zone ACl is part of a more general zone configuration. A + quick example for configuring an ACL for zone "example.com" that + rejects any transfer request for that zone is as follows: + > config add Xfrout/zone_config + > config set Xfrout/zone_config[0]/origin "example.com" + > config add Xfrout/zone_config[0]/transfer_acl + > config set Xfrout/zone_config[0]/transfer_acl[0] {"action": "REJECT"} + The previous global ACL (query_acl) was renamed to transfer_acl, + which now works as the default ACL. Note: backward compatibility + is not provided, so an existing configuration using query_acl + needs to be updated by hand. + Note: the per zone configuration framework is a temporary + workaround. It will eventually be redesigned as a system wide + configuration. + (Trac #1165, git 698176eccd5d55759fe9448b2c249717c932ac31) + 288. [bug] stephen Fixed problem whereby the order in which component files appeared in rdataclass.cc was system dependent, leading to problems on some From 53d45f54e33d23a5b4df42dc977a3a6ab597f5c5 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 19:05:13 +0000 Subject: [PATCH 854/974] [master] introduced a short term workaround to fix build failure with clang++ on freebsd by reducing the amount of code to be compiled. okayed on jabber. will need to create a ticket for a more complete solution. --- src/lib/datasrc/tests/database_unittest.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 94ae022807..fe57185235 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -851,7 +851,17 @@ public: // The following two lines instantiate test cases with concrete accessor // classes to be tested. +// XXX: clang++ installed on our FreeBSD buildbot cannot complete compiling +// this file, seemingly due to the size of the code. We'll consider more +// complete workaround, but for a short term workaround we'll reduce the +// number of tested accessor classes (thus reducing the amount of code +// to be compiled) for this particular environment. +#if defined(__clang__) && defined(__FreeBSD__) +typedef ::testing::Types TestAccessorTypes; +#else typedef ::testing::Types TestAccessorTypes; +#endif + TYPED_TEST_CASE(DatabaseClientTest, TestAccessorTypes); // In some cases the entire test fixture is for the mock accessor only. From 2d325650009f46a1f16ef2e7c1f4ed0827db236f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 15:08:38 -0700 Subject: [PATCH 855/974] [1258] added some more primitive tests for dns::Message --- src/lib/dns/tests/message_unittest.cc | 40 ++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 6430626228..2b8be7adac 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -175,7 +175,6 @@ TEST_F(MessageTest, headerFlag) { EXPECT_THROW(message_parse.setHeaderFlag(Message::HEADERFLAG_QR), InvalidMessageOperation); } - TEST_F(MessageTest, getEDNS) { EXPECT_FALSE(message_parse.getEDNS()); // by default EDNS isn't set @@ -532,7 +531,46 @@ TEST_F(MessageTest, appendSection) { } +TEST_F(MessageTest, parseHeader) { + received_data.clear(); + UnitTestUtil::readWireData("message_fromWire1", received_data); + + // parseHeader() isn't allowed in the render mode. + InputBuffer buffer(&received_data[0], received_data.size()); + EXPECT_THROW(message_render.parseHeader(buffer), InvalidMessageOperation); + + message_parse.parseHeader(buffer); + EXPECT_EQ(0x1035, message_parse.getQid()); + EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode()); + EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode()); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_QR)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_AA)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_RD)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_RA)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_AD)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_CD)); + EXPECT_EQ(1, message_parse.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(2, message_parse.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_ADDITIONAL)); + + // Only the header part should have been examined. + EXPECT_EQ(12, buffer.getPosition()); // 12 = size of the header section + EXPECT_TRUE(message_parse.beginQuestion() == message_parse.endQuestion()); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_ANSWER) == + message_parse.endSection(Message::SECTION_ANSWER)); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_AUTHORITY) == + message_parse.endSection(Message::SECTION_AUTHORITY)); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_ADDITIONAL) == + message_parse.endSection(Message::SECTION_ADDITIONAL)); +} + TEST_F(MessageTest, fromWire) { + // fromWire() isn't allowed in the render mode. + EXPECT_THROW(factoryFromFile(message_render, "message_fromWire1"), + InvalidMessageOperation); + factoryFromFile(message_parse, "message_fromWire1"); EXPECT_EQ(0x1035, message_parse.getQid()); EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode()); From 0d874a95d3c782b9c663c64be619f449956df457 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 15:29:04 -0700 Subject: [PATCH 856/974] [1258] added a test to confirm the existing behavior: combining RRs of the same type into a single RRset despite the ordering. --- src/lib/dns/tests/message_unittest.cc | 20 ++++++++++++++++++++ src/lib/dns/tests/testdata/Makefile.am | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 2b8be7adac..b5f0f134b2 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -602,6 +602,26 @@ TEST_F(MessageTest, fromWire) { EXPECT_TRUE(it->isLast()); } +TEST_F(MessageTest, fromWireCombineRRs) { + // This message contains 3 RRs in the answer section in the order of + // A, AAAA, A types. fromWire() should combine the two A RRs into a + // single RRset by default. + UnitTestUtil::readWireData("message_fromWire19.wire", received_data); + InputBuffer buffer(&received_data[0], received_data.size()); + message_parse.fromWire(buffer); + + RRsetIterator it = message_parse.beginSection(Message::SECTION_ANSWER); + RRsetIterator it_end = message_parse.endSection(Message::SECTION_ANSWER); + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(2, (*it)->getRdataCount()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::AAAA(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); +} + TEST_F(MessageTest, EDNS0ExtRcode) { // Extended Rcode = BADVERS factoryFromFile(message_parse, "message_fromWire10.wire"); diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 3aa49375d8..4e91cc6f23 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -6,7 +6,7 @@ BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire BUILT_SOURCES += message_fromWire16.wire message_fromWire17.wire -BUILT_SOURCES += message_fromWire18.wire +BUILT_SOURCES += message_fromWire18.wire message_fromWire19.wire BUILT_SOURCES += message_toWire2.wire message_toWire3.wire BUILT_SOURCES += message_toWire4.wire message_toWire5.wire BUILT_SOURCES += message_toText1.wire message_toText2.wire @@ -71,6 +71,7 @@ EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec EXTRA_DIST += message_fromWire17.spec message_fromWire18.spec +EXTRA_DIST += message_fromWire19.spec EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec EXTRA_DIST += message_toWire4.spec message_toWire5.spec EXTRA_DIST += message_toText1.txt message_toText1.spec From c943619d223be1158ae8db5223f655343d06785f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 16:20:55 -0700 Subject: [PATCH 857/974] [1258] added support for the PRESERVE_ORDER option for Message::fromWire(). --- src/lib/dns/message.cc | 43 ++++++++------ src/lib/dns/message.h | 17 +++++- src/lib/dns/tests/message_unittest.cc | 58 +++++++++++++++++++ src/lib/dns/tests/testdata/Makefile.am | 5 +- .../tests/testdata/message_fromWire19.spec | 20 +++++++ .../tests/testdata/message_fromWire20.spec | 20 +++++++ .../tests/testdata/message_fromWire21.spec | 20 +++++++ 7 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 src/lib/dns/tests/testdata/message_fromWire19.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire20.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire21.spec diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index c5ba4e1a07..b3e9229ae8 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -124,10 +124,12 @@ public: void setOpcode(const Opcode& opcode); void setRcode(const Rcode& rcode); int parseQuestion(InputBuffer& buffer); - int parseSection(const Message::Section section, InputBuffer& buffer); + int parseSection(const Message::Section section, InputBuffer& buffer, + Message::ParseOptions options); void addRR(Message::Section section, const Name& name, const RRClass& rrclass, const RRType& rrtype, - const RRTTL& ttl, ConstRdataPtr rdata); + const RRTTL& ttl, ConstRdataPtr rdata, + Message::ParseOptions options); void addEDNS(Message::Section section, const Name& name, const RRClass& rrclass, const RRType& rrtype, const RRTTL& ttl, const Rdata& rdata); @@ -614,7 +616,7 @@ Message::parseHeader(InputBuffer& buffer) { } void -Message::fromWire(InputBuffer& buffer) { +Message::fromWire(InputBuffer& buffer, ParseOptions options) { if (impl_->mode_ != Message::PARSE) { isc_throw(InvalidMessageOperation, "Message parse attempted in non parse mode"); @@ -626,11 +628,11 @@ Message::fromWire(InputBuffer& buffer) { impl_->counts_[SECTION_QUESTION] = impl_->parseQuestion(buffer); impl_->counts_[SECTION_ANSWER] = - impl_->parseSection(SECTION_ANSWER, buffer); + impl_->parseSection(SECTION_ANSWER, buffer, options); impl_->counts_[SECTION_AUTHORITY] = - impl_->parseSection(SECTION_AUTHORITY, buffer); + impl_->parseSection(SECTION_AUTHORITY, buffer, options); impl_->counts_[SECTION_ADDITIONAL] = - impl_->parseSection(SECTION_ADDITIONAL, buffer); + impl_->parseSection(SECTION_ADDITIONAL, buffer, options); } int @@ -706,7 +708,7 @@ struct MatchRR : public unary_function { // is hardcoded here. int MessageImpl::parseSection(const Message::Section section, - InputBuffer& buffer) + InputBuffer& buffer, Message::ParseOptions options) { assert(section < MessageImpl::NUM_SECTIONS); @@ -738,7 +740,7 @@ MessageImpl::parseSection(const Message::Section section, addTSIG(section, count, buffer, start_position, name, rrclass, ttl, *rdata); } else { - addRR(section, name, rrclass, rrtype, ttl, rdata); + addRR(section, name, rrclass, rrtype, ttl, rdata, options); ++added; } } @@ -749,19 +751,22 @@ MessageImpl::parseSection(const Message::Section section, void MessageImpl::addRR(Message::Section section, const Name& name, const RRClass& rrclass, const RRType& rrtype, - const RRTTL& ttl, ConstRdataPtr rdata) + const RRTTL& ttl, ConstRdataPtr rdata, + Message::ParseOptions options) { - vector::iterator it = - find_if(rrsets_[section].begin(), rrsets_[section].end(), - MatchRR(name, rrtype, rrclass)); - if (it != rrsets_[section].end()) { - (*it)->setTTL(min((*it)->getTTL(), ttl)); - (*it)->addRdata(rdata); - } else { - RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl)); - rrset->addRdata(rdata); - rrsets_[section].push_back(rrset); + if ((options & Message::PRESERVE_ORDER) == 0) { + vector::iterator it = + find_if(rrsets_[section].begin(), rrsets_[section].end(), + MatchRR(name, rrtype, rrclass)); + if (it != rrsets_[section].end()) { + (*it)->setTTL(min((*it)->getTTL(), ttl)); + (*it)->addRdata(rdata); + return; + } } + RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl)); + rrset->addRdata(rdata); + rrsets_[section].push_back(rrset); } void diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h index 6a8bf9f1c9..02d3b5eacc 100644 --- a/src/lib/dns/message.h +++ b/src/lib/dns/message.h @@ -581,11 +581,26 @@ public: /// message void toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx); + /// Parse options. + /// + /// describe PRESERVE_ORDER: note doesn't affect EDNS or TSIG. + /// + /// The option values are used as a parameter for \c fromWire(). + /// These are values of a bitmask type. Bitwise operations can be + /// performed on these values to express compound options. + enum ParseOptions { + PARSE_DEFAULT = 0, ///< The default options + PRESERVE_ORDER = 1 ///< Preserve RR order and don't combining + }; + /// \brief Parse the header section of the \c Message. void parseHeader(isc::util::InputBuffer& buffer); /// \brief Parse the \c Message. - void fromWire(isc::util::InputBuffer& buffer); + /// + /// \param buffer + void fromWire(isc::util::InputBuffer& buffer, ParseOptions options + = PARSE_DEFAULT); /// /// \name Protocol constants diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index b5f0f134b2..92db0daec2 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -622,6 +622,64 @@ TEST_F(MessageTest, fromWireCombineRRs) { EXPECT_EQ(1, (*it)->getRdataCount()); } +// A helper function for a test pattern commonly used in several tests below. +void +preserveRRCheck(const Message& message, Message::Section section) { + RRsetIterator it = message.beginSection(section); + RRsetIterator it_end = message.endSection(section); + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("192.0.2.1", (*it)->getRdataIterator()->getCurrent().toText()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::AAAA(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("2001:db8::1", (*it)->getRdataIterator()->getCurrent().toText()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("192.0.2.2", (*it)->getRdataIterator()->getCurrent().toText()); +} + +TEST_F(MessageTest, fromWirePreserveAnswer) { + // Using the same data as the previous test, but specify the PRESERVE_ORDER + // option. The received order of RRs should be preserved, and each RR + // should be stored in a single RRset. + UnitTestUtil::readWireData("message_fromWire19.wire", received_data); + InputBuffer buffer(&received_data[0], received_data.size()); + message_parse.fromWire(buffer, Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve answer RRs"); + preserveRRCheck(message_parse, Message::SECTION_ANSWER); + } +} + +TEST_F(MessageTest, fromWirePreserveAuthority) { + // Same for the previous test, but for the authority section. + UnitTestUtil::readWireData("message_fromWire20.wire", received_data); + InputBuffer buffer(&received_data[0], received_data.size()); + message_parse.fromWire(buffer, Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve authority RRs"); + preserveRRCheck(message_parse, Message::SECTION_AUTHORITY); + } +} + +TEST_F(MessageTest, fromWirePreserveAdditional) { + // Same for the previous test, but for the additional section. + UnitTestUtil::readWireData("message_fromWire21.wire", received_data); + InputBuffer buffer(&received_data[0], received_data.size()); + message_parse.fromWire(buffer, Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve additional RRs"); + preserveRRCheck(message_parse, Message::SECTION_ADDITIONAL); + } +} + TEST_F(MessageTest, EDNS0ExtRcode) { // Extended Rcode = BADVERS factoryFromFile(message_parse, "message_fromWire10.wire"); diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 4e91cc6f23..21e72d38c0 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -7,6 +7,8 @@ BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire BUILT_SOURCES += message_fromWire16.wire message_fromWire17.wire BUILT_SOURCES += message_fromWire18.wire message_fromWire19.wire +BUILT_SOURCES += message_fromWire19.wire message_fromWire20.wire +BUILT_SOURCES += message_fromWire21.wire BUILT_SOURCES += message_toWire2.wire message_toWire3.wire BUILT_SOURCES += message_toWire4.wire message_toWire5.wire BUILT_SOURCES += message_toText1.wire message_toText2.wire @@ -71,7 +73,8 @@ EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec EXTRA_DIST += message_fromWire17.spec message_fromWire18.spec -EXTRA_DIST += message_fromWire19.spec +EXTRA_DIST += message_fromWire19.spec message_fromWire20.spec +EXTRA_DIST += message_fromWire21.spec EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec EXTRA_DIST += message_toWire4.spec message_toWire5.spec EXTRA_DIST += message_toText1.txt message_toText1.spec diff --git a/src/lib/dns/tests/testdata/message_fromWire19.spec b/src/lib/dns/tests/testdata/message_fromWire19.spec new file mode 100644 index 0000000000..8212dbfa9f --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire19.spec @@ -0,0 +1,20 @@ +# +# A non realistic DNS response message containing mixed types of RRs in the +# answer section in a mixed order. +# + +[custom] +sections: header:question:a/1:aaaa:a/2 +[header] +qr: 1 +ancount: 3 +[question] +name: www.example.com +rrtype: A +[a/1] +as_rr: True +[aaaa] +as_rr: True +[a/2] +as_rr: True +address: 192.0.2.2 diff --git a/src/lib/dns/tests/testdata/message_fromWire20.spec b/src/lib/dns/tests/testdata/message_fromWire20.spec new file mode 100644 index 0000000000..91986e4818 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire20.spec @@ -0,0 +1,20 @@ +# +# A non realistic DNS response message containing mixed types of RRs in the +# authority section in a mixed order. +# + +[custom] +sections: header:question:a/1:aaaa:a/2 +[header] +qr: 1 +nscount: 3 +[question] +name: www.example.com +rrtype: A +[a/1] +as_rr: True +[aaaa] +as_rr: True +[a/2] +as_rr: True +address: 192.0.2.2 diff --git a/src/lib/dns/tests/testdata/message_fromWire21.spec b/src/lib/dns/tests/testdata/message_fromWire21.spec new file mode 100644 index 0000000000..cd6aac9b42 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire21.spec @@ -0,0 +1,20 @@ +# +# A non realistic DNS response message containing mixed types of RRs in the +# additional section in a mixed order. +# + +[custom] +sections: header:question:a/1:aaaa:a/2 +[header] +qr: 1 +arcount: 3 +[question] +name: www.example.com +rrtype: A +[a/1] +as_rr: True +[aaaa] +as_rr: True +[a/2] +as_rr: True +address: 192.0.2.2 From 1ee8ad4a2b092a6edc35c111c5a3b5b761da0dae Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 17:04:16 -0700 Subject: [PATCH 858/974] [1258] a small refactoring: made initModulePart_Message() safer using pycppwrapper_util. basically no behavior change except for error handling. --- src/lib/dns/python/pydnspp.cc | 123 ++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 51 deletions(-) diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 830876c7a1..14a7bda2c0 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -89,64 +89,85 @@ initModulePart_Message(PyObject* mod) { if (PyType_Ready(&message_type) < 0) { return (false); } + void* p = &message_type; + if (PyModule_AddObject(mod, "Message", static_cast(p)) < 0) { + return (false); + } Py_INCREF(&message_type); - // Class variables - // These are added to the tp_dict of the type object - // - addClassVariable(message_type, "PARSE", - Py_BuildValue("I", Message::PARSE)); - addClassVariable(message_type, "RENDER", - Py_BuildValue("I", Message::RENDER)); + try { + // + // Constant class variables + // - addClassVariable(message_type, "HEADERFLAG_QR", - Py_BuildValue("I", Message::HEADERFLAG_QR)); - addClassVariable(message_type, "HEADERFLAG_AA", - Py_BuildValue("I", Message::HEADERFLAG_AA)); - addClassVariable(message_type, "HEADERFLAG_TC", - Py_BuildValue("I", Message::HEADERFLAG_TC)); - addClassVariable(message_type, "HEADERFLAG_RD", - Py_BuildValue("I", Message::HEADERFLAG_RD)); - addClassVariable(message_type, "HEADERFLAG_RA", - Py_BuildValue("I", Message::HEADERFLAG_RA)); - addClassVariable(message_type, "HEADERFLAG_AD", - Py_BuildValue("I", Message::HEADERFLAG_AD)); - addClassVariable(message_type, "HEADERFLAG_CD", - Py_BuildValue("I", Message::HEADERFLAG_CD)); + // Parse mode + installClassVariable(message_type, "PARSE", + Py_BuildValue("I", Message::PARSE)); + installClassVariable(message_type, "RENDER", + Py_BuildValue("I", Message::RENDER)); - addClassVariable(message_type, "SECTION_QUESTION", - Py_BuildValue("I", Message::SECTION_QUESTION)); - addClassVariable(message_type, "SECTION_ANSWER", - Py_BuildValue("I", Message::SECTION_ANSWER)); - addClassVariable(message_type, "SECTION_AUTHORITY", - Py_BuildValue("I", Message::SECTION_AUTHORITY)); - addClassVariable(message_type, "SECTION_ADDITIONAL", - Py_BuildValue("I", Message::SECTION_ADDITIONAL)); + // Header flags + installClassVariable(message_type, "HEADERFLAG_QR", + Py_BuildValue("I", Message::HEADERFLAG_QR)); + installClassVariable(message_type, "HEADERFLAG_AA", + Py_BuildValue("I", Message::HEADERFLAG_AA)); + installClassVariable(message_type, "HEADERFLAG_TC", + Py_BuildValue("I", Message::HEADERFLAG_TC)); + installClassVariable(message_type, "HEADERFLAG_RD", + Py_BuildValue("I", Message::HEADERFLAG_RD)); + installClassVariable(message_type, "HEADERFLAG_RA", + Py_BuildValue("I", Message::HEADERFLAG_RA)); + installClassVariable(message_type, "HEADERFLAG_AD", + Py_BuildValue("I", Message::HEADERFLAG_AD)); + installClassVariable(message_type, "HEADERFLAG_CD", + Py_BuildValue("I", Message::HEADERFLAG_CD)); - addClassVariable(message_type, "DEFAULT_MAX_UDPSIZE", - Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE)); + // Sections + installClassVariable(message_type, "SECTION_QUESTION", + Py_BuildValue("I", Message::SECTION_QUESTION)); + installClassVariable(message_type, "SECTION_ANSWER", + Py_BuildValue("I", Message::SECTION_ANSWER)); + installClassVariable(message_type, "SECTION_AUTHORITY", + Py_BuildValue("I", Message::SECTION_AUTHORITY)); + installClassVariable(message_type, "SECTION_ADDITIONAL", + Py_BuildValue("I", Message::SECTION_ADDITIONAL)); - /* Class-specific exceptions */ - po_MessageTooShort = PyErr_NewException("pydnspp.MessageTooShort", NULL, - NULL); - PyModule_AddObject(mod, "MessageTooShort", po_MessageTooShort); - po_InvalidMessageSection = - PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL); - PyModule_AddObject(mod, "InvalidMessageSection", po_InvalidMessageSection); - po_InvalidMessageOperation = - PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL); - PyModule_AddObject(mod, "InvalidMessageOperation", - po_InvalidMessageOperation); - po_InvalidMessageUDPSize = - PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL); - PyModule_AddObject(mod, "InvalidMessageUDPSize", po_InvalidMessageUDPSize); - po_DNSMessageBADVERS = PyErr_NewException("pydnspp.DNSMessageBADVERS", - NULL, NULL); - PyModule_AddObject(mod, "DNSMessageBADVERS", po_DNSMessageBADVERS); - - PyModule_AddObject(mod, "Message", - reinterpret_cast(&message_type)); + // Protocol constant + installClassVariable(message_type, "DEFAULT_MAX_UDPSIZE", + Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE)); + /* Class-specific exceptions */ + po_MessageTooShort = + PyErr_NewException("pydnspp.MessageTooShort", NULL, NULL); + PyObjectContainer(po_MessageTooShort).installToModule( + mod, "MessageTooShort"); + po_InvalidMessageSection = + PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL); + PyObjectContainer(po_InvalidMessageSection).installToModule( + mod, "InvalidMessageSection"); + po_InvalidMessageOperation = + PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL); + PyObjectContainer(po_InvalidMessageOperation).installToModule( + mod, "InvalidMessageOperation"); + po_InvalidMessageUDPSize = + PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL); + PyObjectContainer(po_InvalidMessageUDPSize).installToModule( + mod, "InvalidMessageUDPSize"); + po_DNSMessageBADVERS = + PyErr_NewException("pydnspp.DNSMessageBADVERS", NULL, NULL); + PyObjectContainer(po_DNSMessageBADVERS).installToModule( + mod, "DNSMessageBADVERS"); + } catch (const std::exception& ex) { + const std::string ex_what = + "Unexpected failure in Message initialization: " + + std::string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (false); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure in Message initialization"); + return (false); + } return (true); } From b0b0da67c915f3c02020397b8dcf6a078a9b3a90 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 17:38:54 -0700 Subject: [PATCH 859/974] [1258] added a python wrapper for Message.from_wire(PRESERVE_ORDER) --- src/lib/dns/python/message_python.cc | 45 +++++++++------- src/lib/dns/python/pydnspp.cc | 4 ++ .../dns/python/tests/message_python_test.py | 51 ++++++++++++++++++- 3 files changed, 79 insertions(+), 21 deletions(-) diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index b40ab45e6f..e9b1d64a35 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -649,27 +649,34 @@ PyObject* Message_fromWire(s_Message* self, PyObject* args) { const char* b; Py_ssize_t len; - if (!PyArg_ParseTuple(args, "y#", &b, &len)) { - return (NULL); + Message::ParseOptions options = Message::PARSE_DEFAULT; + if (PyArg_ParseTuple(args, "y#", &b, &len) || + PyArg_ParseTuple(args, "y#I", &b, &len, &options)) { + // We need to clear the error in case the first call to ParseTuple + // fails. + PyErr_Clear(); + + InputBuffer inbuf(b, len); + try { + self->cppobj->fromWire(inbuf, options); + Py_RETURN_NONE; + } catch (const InvalidMessageOperation& imo) { + PyErr_SetString(po_InvalidMessageOperation, imo.what()); + return (NULL); + } catch (const DNSMessageFORMERR& dmfe) { + PyErr_SetString(po_DNSMessageFORMERR, dmfe.what()); + return (NULL); + } catch (const DNSMessageBADVERS& dmfe) { + PyErr_SetString(po_DNSMessageBADVERS, dmfe.what()); + return (NULL); + } catch (const MessageTooShort& mts) { + PyErr_SetString(po_MessageTooShort, mts.what()); + return (NULL); + } } - InputBuffer inbuf(b, len); - try { - self->cppobj->fromWire(inbuf); - Py_RETURN_NONE; - } catch (const InvalidMessageOperation& imo) { - PyErr_SetString(po_InvalidMessageOperation, imo.what()); - return (NULL); - } catch (const DNSMessageFORMERR& dmfe) { - PyErr_SetString(po_DNSMessageFORMERR, dmfe.what()); - return (NULL); - } catch (const DNSMessageBADVERS& dmfe) { - PyErr_SetString(po_DNSMessageBADVERS, dmfe.what()); - return (NULL); - } catch (const MessageTooShort& mts) { - PyErr_SetString(po_MessageTooShort, mts.what()); - return (NULL); - } + PyErr_SetString(PyExc_TypeError, "Invalid arguments to Message.from_wire"); + return (NULL); } } // end of unnamed namespace diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 14a7bda2c0..da864b8949 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -106,6 +106,10 @@ initModulePart_Message(PyObject* mod) { installClassVariable(message_type, "RENDER", Py_BuildValue("I", Message::RENDER)); + // Parse options + installClassVariable(message_type, "PRESERVE_ORDER", + Py_BuildValue("I", Message::PRESERVE_ORDER)); + // Header flags installClassVariable(message_type, "HEADERFLAG_QR", Py_BuildValue("I", Message::HEADERFLAG_QR)); diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index c7312536ed..d07dab2146 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -29,9 +29,12 @@ if "TESTDATA_PATH" in os.environ: else: testdata_path = "../tests/testdata" -def factoryFromFile(message, file): +def factoryFromFile(message, file, parse_options=None): data = read_wire_data(file) - message.from_wire(data) + if parse_options is None: + message.from_wire(data) + else: + message.from_wire(data, parse_options) return data # we don't have direct comparison for rrsets right now (should we? @@ -466,6 +469,50 @@ test.example.com. 3600 IN A 192.0.2.2 self.assertEqual("192.0.2.2", rdata[1].to_text()) self.assertEqual(2, len(rdata)) + def test_from_wire_combind_rrs(self): + factoryFromFile(self.p, "message_fromWire19.wire") + rrset = self.p.get_section(Message.SECTION_ANSWER)[0] + self.assertEqual(RRType("A"), rrset.get_type()) + self.assertEqual(2, len(rrset.get_rdata())) + + rrset = self.p.get_section(Message.SECTION_ANSWER)[1] + self.assertEqual(RRType("AAAA"), rrset.get_type()) + self.assertEqual(1, len(rrset.get_rdata())) + + def check_preserve_rrs(self, message, section): + rrset = message.get_section(section)[0] + self.assertEqual(RRType("A"), rrset.get_type()) + rdata = rrset.get_rdata() + self.assertEqual(1, len(rdata)) + self.assertEqual('192.0.2.1', rdata[0].to_text()) + + rrset = message.get_section(section)[1] + self.assertEqual(RRType("AAAA"), rrset.get_type()) + rdata = rrset.get_rdata() + self.assertEqual(1, len(rdata)) + self.assertEqual('2001:db8::1', rdata[0].to_text()) + + rrset = message.get_section(section)[2] + self.assertEqual(RRType("A"), rrset.get_type()) + rdata = rrset.get_rdata() + self.assertEqual(1, len(rdata)) + self.assertEqual('192.0.2.2', rdata[0].to_text()) + + def test_from_wire_preserve_answer(self): + factoryFromFile(self.p, "message_fromWire19.wire", + Message.PRESERVE_ORDER) + self.check_preserve_rrs(self.p, Message.SECTION_ANSWER) + + def test_from_wire_preserve_authority(self): + factoryFromFile(self.p, "message_fromWire20.wire", + Message.PRESERVE_ORDER) + self.check_preserve_rrs(self.p, Message.SECTION_AUTHORITY) + + def test_from_wire_preserve_additional(self): + factoryFromFile(self.p, "message_fromWire21.wire", + Message.PRESERVE_ORDER) + self.check_preserve_rrs(self.p, Message.SECTION_ADDITIONAL) + def test_EDNS0ExtCode(self): # Extended Rcode = BADVERS message_parse = Message(Message.PARSE) From 938f4e9ba14954551fbc390abb7d1e06d38189c2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 17:41:14 -0700 Subject: [PATCH 860/974] [1258] a small refactoring: avoid using reinterpret_cast for Message_fromWire. --- src/lib/dns/python/message_python.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index e9b1d64a35..26fe9cb40d 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -75,7 +75,7 @@ PyObject* Message_makeResponse(s_Message* self); PyObject* Message_toText(s_Message* self); PyObject* Message_str(PyObject* self); PyObject* Message_toWire(s_Message* self, PyObject* args); -PyObject* Message_fromWire(s_Message* self, PyObject* args); +PyObject* Message_fromWire(PyObject* const pyself, PyObject* args); // This list contains the actual set of functions we have in // python. Each entry has @@ -157,7 +157,7 @@ PyMethodDef Message_methods[] = { "If the given message is not in RENDER mode, an " "InvalidMessageOperation is raised.\n" }, - { "from_wire", reinterpret_cast(Message_fromWire), METH_VARARGS, + { "from_wire", Message_fromWire, METH_VARARGS, "Parses the given wire format to a Message object.\n" "The first argument is a Message to parse the data into.\n" "The second argument must implement the buffer interface.\n" @@ -646,7 +646,8 @@ Message_toWire(s_Message* self, PyObject* args) { } PyObject* -Message_fromWire(s_Message* self, PyObject* args) { +Message_fromWire(PyObject* const pyself, PyObject* args) { + s_Message* self = static_cast(pyself); const char* b; Py_ssize_t len; Message::ParseOptions options = Message::PARSE_DEFAULT; From e89895b7e5f3b7074271c89de281e426c53be347 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 17:54:53 -0700 Subject: [PATCH 861/974] [1258] simplified test cases a bit by extended factoryFromFile() so that it can optionally accept parse options. --- src/lib/dns/tests/message_unittest.cc | 29 +++++++++++++------------- src/lib/dns/tests/testdata/Makefile.am | 6 +++--- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 92db0daec2..0741019dc5 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -118,16 +118,20 @@ protected: vector received_data; vector expected_data; - void factoryFromFile(Message& message, const char* datafile); + void factoryFromFile(Message& message, const char* datafile, + Message::ParseOptions options = + Message::PARSE_DEFAULT); }; void -MessageTest::factoryFromFile(Message& message, const char* datafile) { +MessageTest::factoryFromFile(Message& message, const char* datafile, + Message::ParseOptions options) +{ received_data.clear(); UnitTestUtil::readWireData(datafile, received_data); InputBuffer buffer(&received_data[0], received_data.size()); - message.fromWire(buffer); + message.fromWire(buffer, options); } TEST_F(MessageTest, headerFlag) { @@ -606,9 +610,7 @@ TEST_F(MessageTest, fromWireCombineRRs) { // This message contains 3 RRs in the answer section in the order of // A, AAAA, A types. fromWire() should combine the two A RRs into a // single RRset by default. - UnitTestUtil::readWireData("message_fromWire19.wire", received_data); - InputBuffer buffer(&received_data[0], received_data.size()); - message_parse.fromWire(buffer); + factoryFromFile(message_parse, "message_fromWire19.wire"); RRsetIterator it = message_parse.beginSection(Message::SECTION_ANSWER); RRsetIterator it_end = message_parse.endSection(Message::SECTION_ANSWER); @@ -649,9 +651,8 @@ TEST_F(MessageTest, fromWirePreserveAnswer) { // Using the same data as the previous test, but specify the PRESERVE_ORDER // option. The received order of RRs should be preserved, and each RR // should be stored in a single RRset. - UnitTestUtil::readWireData("message_fromWire19.wire", received_data); - InputBuffer buffer(&received_data[0], received_data.size()); - message_parse.fromWire(buffer, Message::PRESERVE_ORDER); + factoryFromFile(message_parse, "message_fromWire19.wire", + Message::PRESERVE_ORDER); { SCOPED_TRACE("preserve answer RRs"); preserveRRCheck(message_parse, Message::SECTION_ANSWER); @@ -660,9 +661,8 @@ TEST_F(MessageTest, fromWirePreserveAnswer) { TEST_F(MessageTest, fromWirePreserveAuthority) { // Same for the previous test, but for the authority section. - UnitTestUtil::readWireData("message_fromWire20.wire", received_data); - InputBuffer buffer(&received_data[0], received_data.size()); - message_parse.fromWire(buffer, Message::PRESERVE_ORDER); + factoryFromFile(message_parse, "message_fromWire20.wire", + Message::PRESERVE_ORDER); { SCOPED_TRACE("preserve authority RRs"); preserveRRCheck(message_parse, Message::SECTION_AUTHORITY); @@ -671,9 +671,8 @@ TEST_F(MessageTest, fromWirePreserveAuthority) { TEST_F(MessageTest, fromWirePreserveAdditional) { // Same for the previous test, but for the additional section. - UnitTestUtil::readWireData("message_fromWire21.wire", received_data); - InputBuffer buffer(&received_data[0], received_data.size()); - message_parse.fromWire(buffer, Message::PRESERVE_ORDER); + factoryFromFile(message_parse, "message_fromWire21.wire", + Message::PRESERVE_ORDER); { SCOPED_TRACE("preserve additional RRs"); preserveRRCheck(message_parse, Message::SECTION_ADDITIONAL); diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 21e72d38c0..d8f0d1c298 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -7,8 +7,8 @@ BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire BUILT_SOURCES += message_fromWire16.wire message_fromWire17.wire BUILT_SOURCES += message_fromWire18.wire message_fromWire19.wire -BUILT_SOURCES += message_fromWire19.wire message_fromWire20.wire -BUILT_SOURCES += message_fromWire21.wire +BUILT_SOURCES += message_fromWire20.wire message_fromWire21.wire +BUILT_SOURCES += message_fromWire22.wire BUILT_SOURCES += message_toWire2.wire message_toWire3.wire BUILT_SOURCES += message_toWire4.wire message_toWire5.wire BUILT_SOURCES += message_toText1.wire message_toText2.wire @@ -74,7 +74,7 @@ EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec EXTRA_DIST += message_fromWire17.spec message_fromWire18.spec EXTRA_DIST += message_fromWire19.spec message_fromWire20.spec -EXTRA_DIST += message_fromWire21.spec +EXTRA_DIST += message_fromWire21.spec message_fromWire22.spec EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec EXTRA_DIST += message_toWire4.spec message_toWire5.spec EXTRA_DIST += message_toText1.txt message_toText1.spec From 13e8bc43e4888fe9e6df7e536ea0b439c6351199 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 17:59:18 -0700 Subject: [PATCH 862/974] [1258] added one more basic test for fromWire(): a bogus input with a trimmed RR. --- src/lib/dns/tests/message_unittest.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 0741019dc5..f068791d4c 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -606,6 +606,14 @@ TEST_F(MessageTest, fromWire) { EXPECT_TRUE(it->isLast()); } +TEST_F(MessageTest, fromWireShortBuffer) { + // We trim a valid message (ending with an SOA RR) for one byte. + // fromWire() should throw an exception while parsing the trimmed RR. + UnitTestUtil::readWireData("message_fromWire22.wire", received_data); + InputBuffer buffer(&received_data[0], received_data.size() - 1); + EXPECT_THROW(message_parse.fromWire(buffer), InvalidBufferPosition); +} + TEST_F(MessageTest, fromWireCombineRRs) { // This message contains 3 RRs in the answer section in the order of // A, AAAA, A types. fromWire() should combine the two A RRs into a From 9016513b4d19d2781d0b6f2575b490431e04ec79 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 18:10:56 -0700 Subject: [PATCH 863/974] [1258] test data spec for the new test --- src/lib/dns/tests/testdata/message_fromWire22.spec | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/lib/dns/tests/testdata/message_fromWire22.spec diff --git a/src/lib/dns/tests/testdata/message_fromWire22.spec b/src/lib/dns/tests/testdata/message_fromWire22.spec new file mode 100644 index 0000000000..a52523b1ab --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire22.spec @@ -0,0 +1,14 @@ +# +# A simple DNS message containing one SOA RR in the answer section. This is +# intended to be trimmed to emulate a bogus message. +# + +[custom] +sections: header:question:soa +[header] +qr: 1 +ancount: 1 +[question] +rrtype: SOA +[soa] +as_rr: True From c1a72c46b572eee2d94ab53a5589c724fcb1fcf1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 18:11:38 -0700 Subject: [PATCH 864/974] [1258] ported the C++ fromWireShortBuffer test to python. this required more careful exception handling in the wrapper, and as a result it resolves --- src/lib/dns/python/message_python.cc | 12 ++++++++++++ src/lib/dns/python/tests/message_python_test.py | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 26fe9cb40d..dca292e884 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -673,6 +673,18 @@ Message_fromWire(PyObject* const pyself, PyObject* args) { } catch (const MessageTooShort& mts) { PyErr_SetString(po_MessageTooShort, mts.what()); return (NULL); + } catch (const InvalidBufferPosition& ex) { + PyErr_SetString(po_DNSMessageFORMERR, ex.what()); + return (NULL); + } catch (const exception& ex) { + const string ex_what = + "Error in Message.from_wire: " + string(ex.what()); + PyErr_SetString(PyExc_RuntimeError, ex_what.c_str()); + return (NULL); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, + "Unexpected exception in Message.from_wire"); + return (NULL); } } diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index d07dab2146..7adafb61af 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -469,6 +469,10 @@ test.example.com. 3600 IN A 192.0.2.2 self.assertEqual("192.0.2.2", rdata[1].to_text()) self.assertEqual(2, len(rdata)) + def test_from_wire_short_buffer(self): + data = read_wire_data("message_fromWire22.wire") + self.assertRaises(DNSMessageFORMERR, self.p.from_wire, data[:-1]) + def test_from_wire_combind_rrs(self): factoryFromFile(self.p, "message_fromWire19.wire") rrset = self.p.get_section(Message.SECTION_ANSWER)[0] From 9688dee697e9ad279c6542bf164b820e907e526f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 18:59:34 -0700 Subject: [PATCH 865/974] [1258] overall documentation update. also add PARSE_DEFAULT to the python wrapper mainly for consistency with the pydoc --- src/lib/dns/message.h | 36 +++++++++++++++- src/lib/dns/python/Makefile.am | 1 + src/lib/dns/python/message_python.cc | 12 ++---- src/lib/dns/python/message_python_inc.cc | 41 +++++++++++++++++++ src/lib/dns/python/pydnspp.cc | 2 + .../dns/python/tests/message_python_test.py | 7 +--- 6 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 src/lib/dns/python/message_python_inc.cc diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h index 02d3b5eacc..b9ccb34c2f 100644 --- a/src/lib/dns/message.h +++ b/src/lib/dns/message.h @@ -596,9 +596,41 @@ public: /// \brief Parse the header section of the \c Message. void parseHeader(isc::util::InputBuffer& buffer); - /// \brief Parse the \c Message. + /// \brief (Re)build a \c Message object from wire-format data. /// - /// \param buffer + /// This method parses the given wire format data to build a + /// complete Message object. On success, the values of the header section + /// fields can be accessible via corresponding get methods, and the + /// question and following sections can be accessible via the + /// corresponding iterators. If the message contains an EDNS or TSIG, + /// they can be accessible via \c getEDNS() and \c getTSIGRecord(), + /// respectively. + /// + /// This \c Message must be in the \c PARSE mode. + /// + /// This method performs strict validation on the given message based + /// on the DNS protocol specifications. If the given message data is + /// invalid, this method throws an exception (see the exception list). + /// + /// By default, this method combines RRs of the same name, RR type and + /// RR class in a section into a single RRset, even if they are interleaved + /// with a different type of RR (though it would be a rare case in + /// practice). If the \c PRESERVE_ORDER option is specified, it handles + /// each RR separately, in the appearing order, and converts it to a + /// separate RRset (so this RRset should contain exactly one Rdata). + /// This mode will be necessary when the higher level protocol is + /// ordering conscious. For example, in AXFR and IXFR, the position of + /// the SOA RRs are crucial. + /// + /// \exception InvalidMessageOperation \c Message is in the RENDER mode + /// \exception DNSMessageFORMERR The given message data is syntactically + /// \exception MessageTooShort The given data is shorter than a valid + /// header section + /// \exception std::bad_alloc Memory allocation failure + /// \exception Others \c Name, \c Rdata, and \c EDNS classes can also throw + /// + /// \param buffer A input buffer object that stores the wire data + /// \param options Parse options void fromWire(isc::util::InputBuffer& buffer, ParseOptions options = PARSE_DEFAULT); diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am index 4452e40816..3b89358ad0 100644 --- a/src/lib/dns/python/Makefile.am +++ b/src/lib/dns/python/Makefile.am @@ -39,6 +39,7 @@ pydnspp_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS) EXTRA_DIST = tsigerror_python_inc.cc +EXTRA_DIST += message_python_inc.cc # Python prefers .so, while some OSes (specifically MacOS) use a different # suffix for dynamic objects. -module is necessary to work this around. diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index dca292e884..2c3d836bb3 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -39,6 +39,9 @@ using namespace isc::dns; using namespace isc::dns::python; using namespace isc::util; +// Import pydoc text +#include "message_python_inc.cc" + namespace { class s_Message : public PyObject { public: @@ -157,14 +160,7 @@ PyMethodDef Message_methods[] = { "If the given message is not in RENDER mode, an " "InvalidMessageOperation is raised.\n" }, - { "from_wire", Message_fromWire, METH_VARARGS, - "Parses the given wire format to a Message object.\n" - "The first argument is a Message to parse the data into.\n" - "The second argument must implement the buffer interface.\n" - "If the given message is not in PARSE mode, an " - "InvalidMessageOperation is raised.\n" - "Raises MessageTooShort, DNSMessageFORMERR or DNSMessageBADVERS " - " if there is a problem parsing the message." }, + { "from_wire", Message_fromWire, METH_VARARGS, Message_fromWire_doc }, { NULL, NULL, 0, NULL } }; diff --git a/src/lib/dns/python/message_python_inc.cc b/src/lib/dns/python/message_python_inc.cc new file mode 100644 index 0000000000..561c494436 --- /dev/null +++ b/src/lib/dns/python/message_python_inc.cc @@ -0,0 +1,41 @@ +namespace { +const char* const Message_fromWire_doc = "\ +from_wire(data, options=PARSE_DEFAULT)\n\ +\n\ +(Re)build a Message object from wire-format data.\n\ +\n\ +This method parses the given wire format data to build a complete\n\ +Message object. On success, the values of the header section fields\n\ +can be accessible via corresponding get methods, and the question and\n\ +following sections can be accessible via the corresponding iterators.\n\ +If the message contains an EDNS or TSIG, they can be accessible via\n\ +get_edns() and get_tsig_record(), respectively.\n\ +\n\ +This Message must be in the PARSE mode.\n\ +\n\ +This method performs strict validation on the given message based on\n\ +the DNS protocol specifications. If the given message data is invalid,\n\ +this method throws an exception (see the exception list).\n\ +\n\ +By default, this method combines RRs of the same name, RR type and RR\n\ +class in a section into a single RRset, even if they are interleaved\n\ +with a different type of RR (though it would be a rare case in\n\ +practice). If the PRESERVE_ORDER option is specified, it handles each\n\ +RR separately, in the appearing order, and converts it to a separate\n\ +RRset (so this RRset should contain exactly one Rdata). This mode will\n\ +be necessary when the higher level protocol is ordering conscious. For\n\ +example, in AXFR and IXFR, the position of the SOA RRs are crucial.\n\ +\n\ +Exceptions:\n\ + InvalidMessageOperation Message is in the RENDER mode\n\ + DNSMessageFORMERR The given message data is syntactically\n\ + MessageTooShort The given data is shorter than a valid header\n\ + section\n\ + Others Name, Rdata, and EDNS classes can also throw\n\ +\n\ +Parameters:\n\ + data A byte object of the wire data\n\ + options Parse options\n\ +\n\ +"; +} // unnamed namespace diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index da864b8949..0a7d8e5324 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -107,6 +107,8 @@ initModulePart_Message(PyObject* mod) { Py_BuildValue("I", Message::RENDER)); // Parse options + installClassVariable(message_type, "PARSE_DEFAULT", + Py_BuildValue("I", Message::PARSE_DEFAULT)); installClassVariable(message_type, "PRESERVE_ORDER", Py_BuildValue("I", Message::PRESERVE_ORDER)); diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index 7adafb61af..8f2d7323f2 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -29,12 +29,9 @@ if "TESTDATA_PATH" in os.environ: else: testdata_path = "../tests/testdata" -def factoryFromFile(message, file, parse_options=None): +def factoryFromFile(message, file, parse_options=Message.PARSE_DEFAULT): data = read_wire_data(file) - if parse_options is None: - message.from_wire(data) - else: - message.from_wire(data, parse_options) + message.from_wire(data, parse_options) return data # we don't have direct comparison for rrsets right now (should we? From aba10a01b765b472d57112fd4e09a6fb47b49fa7 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 29 Sep 2011 21:31:03 -0700 Subject: [PATCH 866/974] [1258] pass unsigned int* to PyArg_ParseTuple instead of ParseOptions*. that would be a bit type safer. --- src/lib/dns/python/message_python.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 2c3d836bb3..8f99835337 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -646,7 +646,7 @@ Message_fromWire(PyObject* const pyself, PyObject* args) { s_Message* self = static_cast(pyself); const char* b; Py_ssize_t len; - Message::ParseOptions options = Message::PARSE_DEFAULT; + unsigned int options = Message::PARSE_DEFAULT; if (PyArg_ParseTuple(args, "y#", &b, &len) || PyArg_ParseTuple(args, "y#I", &b, &len, &options)) { // We need to clear the error in case the first call to ParseTuple @@ -655,7 +655,8 @@ Message_fromWire(PyObject* const pyself, PyObject* args) { InputBuffer inbuf(b, len); try { - self->cppobj->fromWire(inbuf, options); + self->cppobj->fromWire( + inbuf, static_cast(options)); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); From 39e529c506a4350cd676bf5ddff6d61686e8814f Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 30 Sep 2011 10:44:57 +0200 Subject: [PATCH 867/974] [1273] workaround for the hidden old python classes problem --- src/lib/python/isc/datasrc/Makefile.am | 5 ++--- src/lib/python/isc/datasrc/__init__.py | 11 ++++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am index d8f6b082d5..ca2d9b4f9b 100644 --- a/src/lib/python/isc/datasrc/Makefile.am +++ b/src/lib/python/isc/datasrc/Makefile.am @@ -1,17 +1,16 @@ SUBDIRS = . tests # old data, should be removed in the near future once conversion is done +pythondir = $(pyexecdir)/isc/datasrc python_PYTHON = __init__.py master.py sqlite3_ds.py -#pythondir = $(pyexecdir)/isc/pydatasrc # new data AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(SQLITE_CFLAGS) -pythondir = $(pyexecdir)/isc -pyexec_LTLIBRARIES = datasrc.la +python_LTLIBRARIES = datasrc.la datasrc_la_SOURCES = datasrc.cc datasrc.h datasrc_la_SOURCES += client_python.cc client_python.h datasrc_la_SOURCES += iterator_python.cc iterator_python.h diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py index 53711dbc1b..0b4ed989cf 100644 --- a/src/lib/python/isc/datasrc/__init__.py +++ b/src/lib/python/isc/datasrc/__init__.py @@ -1,12 +1,21 @@ import sys import os +# this setup is a temporary workaround to deal with the problem of +# having both 'normal' python modules and a wrapper module +# Once all programs use the new interface, we should remove the +# old, and the setup can be made similar to that of the log wrappers. +intree = False for base in sys.path[:]: datasrc_libdir = os.path.join(base, 'isc/datasrc/.libs') if os.path.exists(datasrc_libdir): sys.path.insert(0, datasrc_libdir) + intree = True -from datasrc import * +if intree: + from datasrc import * +else: + from isc.datasrc.datasrc import * from isc.datasrc.sqlite3_ds import * from isc.datasrc.master import * From bb7833f2054edca11a32d24d17486f153db00ec1 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 30 Sep 2011 14:36:28 +0200 Subject: [PATCH 868/974] [1258] minor minor change in comment --- src/lib/dns/message.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h index b9ccb34c2f..f286c6791f 100644 --- a/src/lib/dns/message.h +++ b/src/lib/dns/message.h @@ -590,7 +590,7 @@ public: /// performed on these values to express compound options. enum ParseOptions { PARSE_DEFAULT = 0, ///< The default options - PRESERVE_ORDER = 1 ///< Preserve RR order and don't combining + PRESERVE_ORDER = 1 ///< Preserve RR order and don't combine them }; /// \brief Parse the header section of the \c Message. @@ -676,6 +676,6 @@ std::ostream& operator<<(std::ostream& os, const Message& message); } #endif // __MESSAGE_H -// Local Variables: +// Local Variables: // mode: c++ -// End: +// End: From 8c838cf57adef3c004b910b086513d9620147692 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 30 Sep 2011 16:03:51 +0200 Subject: [PATCH 869/974] [master] one additional fix for #1206 pending #1253 the wrappers from #1179 also need a makefile workaround to have access to the currently hardcoded sqlite3 backend, which has now been moved to a dynloaded lib and is not a part of libdatasrc anymore --- src/lib/python/isc/datasrc/Makefile.am | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am index ca2d9b4f9b..07fb417b2a 100644 --- a/src/lib/python/isc/datasrc/Makefile.am +++ b/src/lib/python/isc/datasrc/Makefile.am @@ -16,6 +16,12 @@ datasrc_la_SOURCES += client_python.cc client_python.h datasrc_la_SOURCES += iterator_python.cc iterator_python.h datasrc_la_SOURCES += finder_python.cc finder_python.h datasrc_la_SOURCES += updater_python.cc updater_python.h +# This is a temporary workaround for #1206, where the InMemoryClient has been +# moved to an ldopened library. We could add that library to LDADD, but that +# is nonportable. When #1207 is done this becomes moot anyway, and the +# specific workaround is not needed anymore, so we can then remove this +# line again. +datasrc_la_SOURCES += ${top_srcdir}/src/lib/datasrc/sqlite3_accessor.cc datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS) From 80319933903fbdb359ef9472573bfaceda7c8cd5 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 30 Sep 2011 17:09:08 +0200 Subject: [PATCH 870/974] [1259] Check that stuff is not called after commit Because it just makes no sense. So we raise an exception. --- src/lib/python/isc/xfrin/diff.py | 17 +++++++++++++++++ src/lib/python/isc/xfrin/tests/diff_tests.py | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 38b0e108dc..b1bfe0790c 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -58,6 +58,15 @@ class Diff: str(datasource)) self.__buffer = [] + def __check_commited(self): + """ + This checks if the diff is already commited. If it is, it raises + ValueError. This check is for methods that need to work only on + yet uncommited diffs. + """ + if self.__updater is None: + raise ValueError("The diff is already commited, you come late") + def __data_common(self, rr, operation): """ Schedules an operation with rr. @@ -65,6 +74,7 @@ class Diff: It does all the real work of add_data and remove_data, including all checks. """ + self.__check_commited() if rr.get_rdata_count() != 1: raise ValueError('The rrset must contain exactly 1 Rdata, but ' + 'it holds ' + str(rr.get_rdata_count())) @@ -117,6 +127,7 @@ class Diff: It also can raise isc.datasrc.Error. If that happens, you should stop using this object and abort the modification. """ + self.__check_commited() # First, compact the data self.compact() # Then pass the data inside the data source @@ -138,8 +149,14 @@ class Diff: This might raise isc.datasrc.Error. """ + self.__check_commited() + # Push the data inside the data source self.apply() + # Make sure they are visible. self.__updater.commit() + # Remove the updater. That will free some resources for one, but + # mark this object as already commited, so we can check + self.__updater = None def get_buffer(self): """ diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 27354018b8..26858ff0c0 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -199,12 +199,22 @@ class DiffTest(unittest.TestCase): """ diff = Diff(self, Name('example.org.')) diff.add_data(self.__rrset1) + orig_apply = diff.apply diff.apply = self.__mock_apply diff.commit() self.assertTrue(self.__apply_called) self.assertTrue(self.__commit_called) # The data should be handled by apply which we replaced. self.assertEqual([], self.__data_operations) + # Now check all range of other methods raise ValueError + self.assertRaises(ValueError, diff.commit) + self.assertRaises(ValueError, diff.add_data, self.__rrset2) + self.assertRaises(ValueError, diff.remove_data, self.__rrset1) + diff.apply = orig_apply + self.assertRaises(ValueError, diff.apply) + # This one does not state it should raise, so check it doesn't + # But it is NOP in this situation anyway + diff.compact() if __name__ == "__main__": isc.log.init("bind10") From 8d2c46f19c1b4f435d7b9180ff6c2e8daf78ab2b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 30 Sep 2011 17:52:50 +0200 Subject: [PATCH 871/974] [1259] Test for auto-apply every 100 When the diff grows to 100 RRs, the apply should be called by itself. --- src/lib/python/isc/xfrin/tests/diff_tests.py | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 26858ff0c0..313963e135 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -216,6 +216,46 @@ class DiffTest(unittest.TestCase): # But it is NOP in this situation anyway diff.compact() + def test_autoapply(self): + """ + Test the apply is called all by itself after 100 tasks are added. + """ + diff = Diff(self, Name('example.org.')) + # A method to check the apply is called _after_ the 100th element + # is added. We don't use it anywhere else, so we define it locally + # as lambda function + def check(): + self.assertEqual(100, len(diff.get_buffer())) + self.__mock_apply() + orig_apply = diff.apply + diff.apply = check + # If we put 99, nothing happens yet + for i in range(0, 99): + diff.add_data(self.__rrset1) + expected = [('add', self.__rrset1)] * 99 + self.assertEqual(expected, diff.get_buffer()) + self.assertFalse(self.__apply_called) + # Now we push the 100th and it should call the apply method + # This will _not_ flush the data yet, as we replaced the method. + # It, however, would in the real life. + diff.add_data(self.__rrset1) + # Now the apply method (which is replaced by our check) should + # have been called. If it wasn't, this is false. If it was, but + # still with 99 elements, the check would complain + self.assertTrue(self.__apply_called) + # Reset the buffer by calling the original apply. + orig_apply() + self.assertEqual([], diff.get_buffer()) + # Similar with remove + self.__apply_called = False + for i in range(0, 99): + diff.remove_data(self.__rrset2) + expected = [('remove', self.__rrset2)] * 99 + self.assertEqual(expected, diff.get_buffer()) + self.assertFalse(self.__apply_called) + diff.remove_data(self.__rrset2) + self.assertTrue(self.__apply_called) + if __name__ == "__main__": isc.log.init("bind10") unittest.main() From c191f23dfc2b0179ec0a010a1ff00fa3ae1d9398 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 30 Sep 2011 17:59:31 +0200 Subject: [PATCH 872/974] [1259] Auto-apply every 100 rdata --- src/lib/python/isc/xfrin/diff.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index b1bfe0790c..2be33a08c7 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -79,6 +79,9 @@ class Diff: raise ValueError('The rrset must contain exactly 1 Rdata, but ' + 'it holds ' + str(rr.get_rdata_count())) self.__buffer.append((operation, rr)) + if len(self.__buffer) >= 100: + # Time to auto-apply, so the data don't accumulate too much + self.apply() def add_data(self, rr): """ From 48d5ac59277e2e8b43f697a0d1d4b0991a40caa0 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 30 Sep 2011 18:08:40 +0200 Subject: [PATCH 873/974] [1259] Comment what the compact method really does The way it compacts is explained. --- src/lib/python/isc/xfrin/diff.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 2be33a08c7..e659e78e4e 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -108,9 +108,13 @@ class Diff: Tries to compact the operations in buffer a little by putting some of the operations together, forming RRsets with more than one RR. - This is called by apply before putting the data into datasource. + This is called by apply before putting the data into datasource. You + may, but not have to, call this manually. - It is currently empty and needs implementing. + Currently it merges consecutive same operations on the same + domain/type. We could do more fancy things, like sorting by the domain + and do more merging, but such diffs should be rare in practice anyway, + so we don't bother and do it this simple way. """ pass From b86d51b24e7d1bb4980426c9a74962628c096ba7 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 30 Sep 2011 20:07:03 +0200 Subject: [PATCH 874/974] [1259] Test of compaction of the diff --- src/lib/python/isc/xfrin/tests/diff_tests.py | 66 ++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 313963e135..963b16d008 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -256,6 +256,72 @@ class DiffTest(unittest.TestCase): diff.remove_data(self.__rrset2) self.assertTrue(self.__apply_called) + def test_compact(self): + """ + Test the compaction works as expected, eg. it compacts only consecutive + changes of the same operation and on the same domain/type. + + The test case checks that it does merge them, but also puts some + different operations "in the middle", changes the type and name and + places the same kind of change further away of each other to see they + are not merged in that case. + """ + diff = Diff(self, Name('example.org.')) + # Check we can do a compact on empty data, it shouldn't break + diff.compact() + self.assertEqual([], diff.get_buffer()) + # This data is the way it should look like after the compact + # ('operation', 'domain.prefix', 'type', ['rdata', 'rdata']) + # The notes say why the each of consecutive can't be merged + data = [ + ('add', 'a', 'A', ['192.0.2.1', '192.0.2.2']), + # Different type. + ('add', 'a', 'AAAA', ['2001:db8::1', '2001:db8::2']), + # Different operation + ('remove', 'a', 'AAAA', ['2001:db8::3']), + # Different domain + ('remove', 'b', 'AAAA', ['2001:db8::4']), + # This does not get merged with the first, even if logically + # possible. We just don't do this. + ('add', 'a', 'A', ['192.0.2.3']) + ] + # Now, fill the data into the diff, in a "flat" way, one by one + for (op, nprefix, rrtype, rdata) in data: + name = Name(nprefix + '.example.org.') + rrtype_obj = RRType(rrtype) + for rdatum in rdata: + rrset = RRset(name, self.__rrclass, rrtype_obj, self.__ttl) + rrset.add_rdata(Rdata(rrtype_obj, self.__rrclass, rdatum)) + if op == 'add': + diff.add_data(rrset) + else: + diff.remove_data(rrset) + # Compact it + diff.compact() + # Now check they got compacted. They should be in the same order as + # pushed inside. So it should be the same as data modulo being in + # the rrsets and isc.dns objects. + def check(): + buf = diff.get_buffer() + self.assertEqual(len(data), len(buf)) + for (expected, received) in zip(data, buf): + (eop, ename, etype, edata) = expected + (rop, rrrset) = received + self.assertEqual(eop, rop) + ename_obj = Name(ename + '.example.org.') + self.assertEqual(ename_obj, rrrset.get_name()) + # We check on names to make sure they are printed nicely + self.assertEqual(etype, str(rrrset.get_type())) + rdata = rrrset.get_rdata() + self.assertEqual(len(edata), len(rdata)) + # It should also preserve the order + for (edatum, rdatum) in zip(edata, rdata): + self.assertEqual(edatum, str(rdatum)) + check() + # Try another compact does nothing, but survives + diff.compact() + check() + if __name__ == "__main__": isc.log.init("bind10") unittest.main() From 763a994cb14bb11ba823831f54d64071319bfac0 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Fri, 30 Sep 2011 20:07:21 +0200 Subject: [PATCH 875/974] [1259] Compaction of the diff --- src/lib/python/isc/xfrin/diff.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index e659e78e4e..6c0540121b 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -18,6 +18,8 @@ This helps the XFR in process with accumulating parts of diff and applying it to the datasource. """ +import isc.dns + class NoSuchZone(Exception): """ This is raised if a diff for non-existant zone is being created. @@ -116,7 +118,19 @@ class Diff: and do more merging, but such diffs should be rare in practice anyway, so we don't bother and do it this simple way. """ - pass + buf = [] + for (op, rrset) in self.__buffer: + old = buf[-1][1] if len(buf) > 0 else None + if old is None or op != buf[-1][0] or \ + rrset.get_name() != old.get_name() or \ + rrset.get_type() != old.get_type(): + buf.append((op, isc.dns.RRset(rrset.get_name(), + rrset.get_class(), + rrset.get_type(), + rrset.get_ttl()))) + for rdatum in rrset.get_rdata(): + buf[-1][1].add_rdata(rdatum) + self.__buffer = buf def apply(self): """ From 84ada921a2fe98489b578b6d780c1ad2e6c31482 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 30 Sep 2011 11:23:00 -0700 Subject: [PATCH 876/974] [1258] improved type error string for Message.from_wire() --- ChangeLog | 8 ++++++++ src/lib/dns/python/message_python.cc | 5 ++++- src/lib/dns/python/tests/message_python_test.py | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d0565e18c3..73a63ab4cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +288.? [func] jinmei + libdns++/pydnspp: added an option parameter to the "from wire" + methods of the Message class. One option is defined, + PRESERVE_ORDER, which specifies the parser to handle each RR + separately, preserving the order, and construct RRsets in the + message sections so that each RRset contains only one RR. + (Trac #1258, git TBD) + 287. [bug]* jinmei Python script files for log messages (xxx_messages.py) should have been installed under the "isc" package. This fix itself should diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 8f99835337..601215326b 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -647,6 +647,7 @@ Message_fromWire(PyObject* const pyself, PyObject* args) { const char* b; Py_ssize_t len; unsigned int options = Message::PARSE_DEFAULT; + if (PyArg_ParseTuple(args, "y#", &b, &len) || PyArg_ParseTuple(args, "y#I", &b, &len, &options)) { // We need to clear the error in case the first call to ParseTuple @@ -685,7 +686,9 @@ Message_fromWire(PyObject* const pyself, PyObject* args) { } } - PyErr_SetString(PyExc_TypeError, "Invalid arguments to Message.from_wire"); + PyErr_SetString(PyExc_TypeError, + "from_wire() arguments must be a byte object and " + "(optional) parse options"); return (NULL); } diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index 8f2d7323f2..d769f27aad 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -427,6 +427,7 @@ test.example.com. 3600 IN A 192.0.2.2 self.assertRaises(InvalidMessageOperation, self.r.to_text) def test_from_wire(self): + self.r.from_wire(2, 2) self.assertRaises(TypeError, self.r.from_wire, 1) self.assertRaises(InvalidMessageOperation, Message.from_wire, self.r, bytes()) From 12fd115d2e1ea8b55f43313ac665c32e07f9498e Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 30 Sep 2011 11:26:53 -0700 Subject: [PATCH 877/974] [1258] removed an experimental test case (which would fail) --- src/lib/dns/python/tests/message_python_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index d769f27aad..8f2d7323f2 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -427,7 +427,6 @@ test.example.com. 3600 IN A 192.0.2.2 self.assertRaises(InvalidMessageOperation, self.r.to_text) def test_from_wire(self): - self.r.from_wire(2, 2) self.assertRaises(TypeError, self.r.from_wire, 1) self.assertRaises(InvalidMessageOperation, Message.from_wire, self.r, bytes()) From 4cde36d2b97a24f03c192a61248545d0180fb856 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 30 Sep 2011 12:24:06 -0700 Subject: [PATCH 878/974] [master] changelog entry for #1258 --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index b006df1f98..b0b2aea21f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +290. [func] jinmei + libdns++/pydnspp: added an option parameter to the "from wire" + methods of the Message class. One option is defined, + PRESERVE_ORDER, which specifies the parser to handle each RR + separately, preserving the order, and constructs RRsets in the + message sections so that each RRset contains only one RR. + (Trac #1258, git c874cb056e2a5e656165f3c160e1b34ccfe8b302) + 289. [func]* jinmei b10-xfrout: ACLs for xfrout can now be configured per zone basis. A per zone ACl is part of a more general zone configuration. A From 26691e282b76d74959e63524b280e77b09ac89df Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 30 Sep 2011 13:25:08 -0700 Subject: [PATCH 879/974] [1259] removed a space char at EOL --- src/lib/python/isc/xfrin/tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/tests/Makefile.am b/src/lib/python/isc/xfrin/tests/Makefile.am index e5325fccc8..416d62b45e 100644 --- a/src/lib/python/isc/xfrin/tests/Makefile.am +++ b/src/lib/python/isc/xfrin/tests/Makefile.am @@ -12,7 +12,7 @@ endif # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif From f9cb0d187f02078b27a0119ce42c83f62461a507 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 2 Oct 2011 17:49:13 -0700 Subject: [PATCH 880/974] [master] fixed a trivial typo I happened to notice. directly pushing. --- src/lib/python/isc/log/log.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc index aa1266438a..5bb6a94c34 100644 --- a/src/lib/python/isc/log/log.cc +++ b/src/lib/python/isc/log/log.cc @@ -185,7 +185,7 @@ init(PyObject*, PyObject* args) { Py_RETURN_NONE; } -// This initialization is for unit tests. It allows message settings to be +// This initialization is for unit tests. It allows message settings to // be determined by a set of B10_xxx environment variables. (See the // description of initLogger() for more details.) The function has been named // resetUnitTestRootLogger() here as being more descriptive and From cc0d6e4674fd2e6ebe3775a28ec87fc5c869f924 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:55:55 +0900 Subject: [PATCH 881/974] [trac930] remove unneeded specfile "stats-schema.spec" --- src/bin/stats/Makefile.am | 4 +- src/bin/stats/stats-schema.spec | 86 --------------------------------- 2 files changed, 2 insertions(+), 88 deletions(-) delete mode 100644 src/bin/stats/stats-schema.spec diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index 3289765a30..63e2a3b38b 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -5,7 +5,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@ pkglibexec_SCRIPTS = b10-stats b10-stats-httpd b10_statsdir = $(pkgdatadir) -b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec +b10_stats_DATA = stats.spec stats-httpd.spec b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py @@ -21,7 +21,7 @@ CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.pyc man_MANS = b10-stats.8 b10-stats-httpd.8 EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml -EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec +EXTRA_DIST += stats.spec stats-httpd.spec EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl EXTRA_DIST += stats_messages.mes stats_httpd_messages.mes diff --git a/src/bin/stats/stats-schema.spec b/src/bin/stats/stats-schema.spec deleted file mode 100644 index 52528657e8..0000000000 --- a/src/bin/stats/stats-schema.spec +++ /dev/null @@ -1,86 +0,0 @@ -{ - "module_spec": { - "module_name": "Stats", - "module_description": "Statistics data schema", - "config_data": [ - { - "item_name": "report_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "Report time", - "item_description": "A date time when stats module reports", - "item_format": "date-time" - }, - { - "item_name": "bind10.boot_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "bind10.BootTime", - "item_description": "A date time when bind10 process starts initially", - "item_format": "date-time" - }, - { - "item_name": "stats.boot_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "stats.BootTime", - "item_description": "A date time when the stats module starts initially or when the stats module restarts", - "item_format": "date-time" - }, - { - "item_name": "stats.start_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "stats.StartTime", - "item_description": "A date time when the stats module starts collecting data or resetting values last time", - "item_format": "date-time" - }, - { - "item_name": "stats.last_update_time", - "item_type": "string", - "item_optional": false, - "item_default": "1970-01-01T00:00:00Z", - "item_title": "stats.LastUpdateTime", - "item_description": "The latest date time when the stats module receives from other modules like auth server or boss process and so on", - "item_format": "date-time" - }, - { - "item_name": "stats.timestamp", - "item_type": "real", - "item_optional": false, - "item_default": 0.0, - "item_title": "stats.Timestamp", - "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)" - }, - { - "item_name": "stats.lname", - "item_type": "string", - "item_optional": false, - "item_default": "", - "item_title": "stats.LocalName", - "item_description": "A localname of stats module given via CC protocol" - }, - { - "item_name": "auth.queries.tcp", - "item_type": "integer", - "item_optional": false, - "item_default": 0, - "item_title": "auth.queries.tcp", - "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" - }, - { - "item_name": "auth.queries.udp", - "item_type": "integer", - "item_optional": false, - "item_default": 0, - "item_title": "auth.queries.udp", - "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially" - } - ], - "commands": [] - } -} From 8023760a5fc6f346cf82340aa50df755b0d0d00a Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 15:57:41 +0900 Subject: [PATCH 882/974] [trac930] remove unneeded mockups, fake modules and dummy data --- configure.ac | 8 - src/bin/stats/tests/fake_select.py | 43 ---- src/bin/stats/tests/fake_socket.py | 70 ------ src/bin/stats/tests/fake_time.py | 47 ---- src/bin/stats/tests/http/Makefile.am | 6 - src/bin/stats/tests/http/__init__.py | 0 src/bin/stats/tests/http/server.py | 96 ------- src/bin/stats/tests/isc/Makefile.am | 8 - src/bin/stats/tests/isc/__init__.py | 0 src/bin/stats/tests/isc/cc/Makefile.am | 7 - src/bin/stats/tests/isc/cc/__init__.py | 1 - src/bin/stats/tests/isc/cc/session.py | 156 ------------ src/bin/stats/tests/isc/config/Makefile.am | 7 - src/bin/stats/tests/isc/config/__init__.py | 1 - src/bin/stats/tests/isc/config/ccsession.py | 249 ------------------- src/bin/stats/tests/isc/log/Makefile.am | 7 - src/bin/stats/tests/isc/log/__init__.py | 33 --- src/bin/stats/tests/isc/util/Makefile.am | 7 - src/bin/stats/tests/isc/util/__init__.py | 0 src/bin/stats/tests/isc/util/process.py | 21 -- src/bin/stats/tests/testdata/Makefile.am | 1 - src/bin/stats/tests/testdata/stats_test.spec | 19 -- 22 files changed, 787 deletions(-) delete mode 100644 src/bin/stats/tests/fake_select.py delete mode 100644 src/bin/stats/tests/fake_socket.py delete mode 100644 src/bin/stats/tests/fake_time.py delete mode 100644 src/bin/stats/tests/http/Makefile.am delete mode 100644 src/bin/stats/tests/http/__init__.py delete mode 100644 src/bin/stats/tests/http/server.py delete mode 100644 src/bin/stats/tests/isc/Makefile.am delete mode 100644 src/bin/stats/tests/isc/__init__.py delete mode 100644 src/bin/stats/tests/isc/cc/Makefile.am delete mode 100644 src/bin/stats/tests/isc/cc/__init__.py delete mode 100644 src/bin/stats/tests/isc/cc/session.py delete mode 100644 src/bin/stats/tests/isc/config/Makefile.am delete mode 100644 src/bin/stats/tests/isc/config/__init__.py delete mode 100644 src/bin/stats/tests/isc/config/ccsession.py delete mode 100644 src/bin/stats/tests/isc/log/Makefile.am delete mode 100644 src/bin/stats/tests/isc/log/__init__.py delete mode 100644 src/bin/stats/tests/isc/util/Makefile.am delete mode 100644 src/bin/stats/tests/isc/util/__init__.py delete mode 100644 src/bin/stats/tests/isc/util/process.py delete mode 100644 src/bin/stats/tests/testdata/Makefile.am delete mode 100644 src/bin/stats/tests/testdata/stats_test.spec diff --git a/configure.ac b/configure.ac index a94912ec9e..1cb0bb0766 100644 --- a/configure.ac +++ b/configure.ac @@ -817,14 +817,6 @@ AC_CONFIG_FILES([Makefile src/bin/zonemgr/tests/Makefile src/bin/stats/Makefile src/bin/stats/tests/Makefile - src/bin/stats/tests/isc/Makefile - src/bin/stats/tests/isc/cc/Makefile - src/bin/stats/tests/isc/config/Makefile - src/bin/stats/tests/isc/util/Makefile - src/bin/stats/tests/isc/log/Makefile - src/bin/stats/tests/isc/log_messages/Makefile - src/bin/stats/tests/testdata/Makefile - src/bin/stats/tests/http/Makefile src/bin/usermgr/Makefile src/bin/tests/Makefile src/lib/Makefile diff --git a/src/bin/stats/tests/fake_select.py b/src/bin/stats/tests/fake_select.py deleted file mode 100644 index ca0ca82619..0000000000 --- a/src/bin/stats/tests/fake_select.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 mock-up module of select - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import fake_socket -import errno - -class error(Exception): - pass - -def select(rlst, wlst, xlst, timeout): - if type(timeout) != int and type(timeout) != float: - raise TypeError("Error: %s must be integer or float" - % timeout.__class__.__name__) - for s in rlst + wlst + xlst: - if type(s) != fake_socket.socket: - raise TypeError("Error: %s must be a dummy socket" - % s.__class__.__name__) - s._called = s._called + 1 - if s._called > 3: - raise error("Something is happened!") - elif s._called > 2: - raise error(errno.EINTR) - return (rlst, wlst, xlst) diff --git a/src/bin/stats/tests/fake_socket.py b/src/bin/stats/tests/fake_socket.py deleted file mode 100644 index 4e3a4581a5..0000000000 --- a/src/bin/stats/tests/fake_socket.py +++ /dev/null @@ -1,70 +0,0 @@ -# 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 mock-up module of socket - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import re - -AF_INET = 'AF_INET' -AF_INET6 = 'AF_INET6' -_ADDRFAMILY = AF_INET -has_ipv6 = True -_CLOSED = False - -class gaierror(Exception): - pass - -class error(Exception): - pass - -class socket: - - def __init__(self, family=None): - if family is None: - self.address_family = _ADDRFAMILY - else: - self.address_family = family - self._closed = _CLOSED - if self._closed: - raise error('socket is already closed!') - self._called = 0 - - def close(self): - self._closed = True - - def fileno(self): - return id(self) - - def bind(self, server_class): - (self.server_address, self.server_port) = server_class - if self.address_family not in set([AF_INET, AF_INET6]): - raise error("Address family not supported by protocol: %s" % self.address_family) - if self.address_family == AF_INET6 and not has_ipv6: - raise error("Address family not supported in this machine: %s has_ipv6: %s" - % (self.address_family, str(has_ipv6))) - if self.address_family == AF_INET and re.search(':', self.server_address) is not None: - raise gaierror("Address family for hostname not supported : %s %s" % (self.server_address, self.address_family)) - if self.address_family == AF_INET6 and re.search(':', self.server_address) is None: - raise error("Cannot assign requested address : %s" % str(self.server_address)) - if type(self.server_port) is not int: - raise TypeError("an integer is required: %s" % str(self.server_port)) - if self.server_port < 0 or self.server_port > 65535: - raise OverflowError("port number must be 0-65535.: %s" % str(self.server_port)) diff --git a/src/bin/stats/tests/fake_time.py b/src/bin/stats/tests/fake_time.py deleted file mode 100644 index 65e02371d6..0000000000 --- a/src/bin/stats/tests/fake_time.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) 2010 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. - -__version__ = "$Revision$" - -# This is a dummy time class against a Python standard time class. -# It is just testing use only. -# Other methods which time class has is not implemented. -# (This class isn't orderloaded for time class.) - -# These variables are constant. These are example. -_TEST_TIME_SECS = 1283364938.229088 -_TEST_TIME_STRF = '2010-09-01T18:15:38Z' - -def time(): - """ - This is a dummy time() method against time.time() - """ - # return float constant value - return _TEST_TIME_SECS - -def gmtime(): - """ - This is a dummy gmtime() method against time.gmtime() - """ - # always return nothing - return None - -def strftime(*arg): - """ - This is a dummy gmtime() method against time.gmtime() - """ - return _TEST_TIME_STRF - - diff --git a/src/bin/stats/tests/http/Makefile.am b/src/bin/stats/tests/http/Makefile.am deleted file mode 100644 index 79263a98b4..0000000000 --- a/src/bin/stats/tests/http/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -EXTRA_DIST = __init__.py server.py -CLEANFILES = __init__.pyc server.pyc -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/http/__init__.py b/src/bin/stats/tests/http/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/bin/stats/tests/http/server.py b/src/bin/stats/tests/http/server.py deleted file mode 100644 index 70ed6faa30..0000000000 --- a/src/bin/stats/tests/http/server.py +++ /dev/null @@ -1,96 +0,0 @@ -# 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 mock-up module of http.server - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import fake_socket - -class DummyHttpResponse: - def __init__(self, path): - self.path = path - self.headers={} - self.log = "" - - def _write_log(self, msg): - self.log = self.log + msg - -class HTTPServer: - """ - A mock-up class of http.server.HTTPServer - """ - address_family = fake_socket.AF_INET - def __init__(self, server_class, handler_class): - self.socket = fake_socket.socket(self.address_family) - self.server_class = server_class - self.socket.bind(self.server_class) - self._handler = handler_class(None, None, self) - - def handle_request(self): - pass - - def server_close(self): - self.socket.close() - -class BaseHTTPRequestHandler: - """ - A mock-up class of http.server.BaseHTTPRequestHandler - """ - - def __init__(self, request, client_address, server): - self.path = "/path/to" - self.headers = {} - self.server = server - self.response = DummyHttpResponse(path=self.path) - self.response.write = self._write - self.wfile = self.response - - def send_response(self, code=0): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.code = code - - def send_header(self, key, value): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.headers[key] = value - - def end_headers(self): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.wrote_headers = True - - def send_error(self, code, message=None): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.code = code - self.response.body = message - - def address_string(self): - return 'dummyhost' - - def log_date_time_string(self): - return '[DD/MM/YYYY HH:MI:SS]' - - def _write(self, obj): - if self.path != self.response.path: - self.response = DummyHttpResponse(path=self.path) - self.response.body = obj.decode() - diff --git a/src/bin/stats/tests/isc/Makefile.am b/src/bin/stats/tests/isc/Makefile.am deleted file mode 100644 index bdfa1eb38a..0000000000 --- a/src/bin/stats/tests/isc/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -SUBDIRS = cc config util log log_messages -EXTRA_DIST = __init__.py -CLEANFILES = __init__.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/__init__.py b/src/bin/stats/tests/isc/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/bin/stats/tests/isc/cc/Makefile.am b/src/bin/stats/tests/isc/cc/Makefile.am deleted file mode 100644 index 67323b5f1b..0000000000 --- a/src/bin/stats/tests/isc/cc/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -EXTRA_DIST = __init__.py session.py -CLEANFILES = __init__.pyc session.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/cc/__init__.py b/src/bin/stats/tests/isc/cc/__init__.py deleted file mode 100644 index 9a3eaf6185..0000000000 --- a/src/bin/stats/tests/isc/cc/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from isc.cc.session import * diff --git a/src/bin/stats/tests/isc/cc/session.py b/src/bin/stats/tests/isc/cc/session.py deleted file mode 100644 index e18a695200..0000000000 --- a/src/bin/stats/tests/isc/cc/session.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (C) 2010,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 mock-up module of isc.cc.session - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import sys -import fake_socket - -# set a dummy lname -_TEST_LNAME = '123abc@xxxx' - -class Queue(): - def __init__(self, msg=None, env={}): - self.msg = msg - self.env = env - - def dump(self): - return { 'msg': self.msg, 'env': self.env } - -class SessionError(Exception): - pass - -class SessionTimeout(Exception): - pass - -class Session: - def __init__(self, socket_file=None, verbose=False): - self._lname = _TEST_LNAME - self.message_queue = [] - self.old_message_queue = [] - try: - self._socket = fake_socket.socket() - except fake_socket.error as se: - raise SessionError(se) - self.verbose = verbose - - @property - def lname(self): - return self._lname - - def close(self): - self._socket.close() - - def _clear_queues(self): - while len(self.message_queue) > 0: - self.dequeue() - - def _next_sequence(self, que=None): - return len(self.message_queue) - - def enqueue(self, msg=None, env={}): - if self._socket._closed: - raise SessionError("Session has been closed.") - seq = self._next_sequence() - env.update({"seq": 0}) # fixed here - que = Queue(msg=msg, env=env) - self.message_queue.append(que) - if self.verbose: - sys.stdout.write("[Session] enqueue: " + str(que.dump()) + "\n") - return seq - - def dequeue(self): - if self._socket._closed: - raise SessionError("Session has been closed.") - que = None - try: - que = self.message_queue.pop(0) # always pop at index 0 - self.old_message_queue.append(que) - except IndexError: - que = Queue() - if self.verbose: - sys.stdout.write("[Session] dequeue: " + str(que.dump()) + "\n") - return que - - def get_queue(self, seq=None): - if self._socket._closed: - raise SessionError("Session has been closed.") - if seq is None: - seq = len(self.message_queue) - 1 - que = None - try: - que = self.message_queue[seq] - except IndexError: - raise IndexError - que = Queue() - if self.verbose: - sys.stdout.write("[Session] get_queue: " + str(que.dump()) + "\n") - return que - - def group_sendmsg(self, msg, group, instance="*", to="*"): - return self.enqueue(msg=msg, env={ - "type": "send", - "from": self._lname, - "to": to, - "group": group, - "instance": instance }) - - def group_recvmsg(self, nonblock=True, seq=0): - que = self.dequeue() - if que.msg != None: - cmd = que.msg.get("command") - if cmd and cmd[0] == 'getstats': - # Create answer for command 'getstats' - retdata = { "stats_data": { - 'bind10.boot_time' : "1970-01-01T00:00:00Z" - }} - return {'result': [0, retdata]}, que.env - return que.msg, que.env - - def group_reply(self, routing, msg): - return self.enqueue(msg=msg, env={ - "type": "send", - "from": self._lname, - "to": routing["from"], - "group": routing["group"], - "instance": routing["instance"], - "reply": routing["seq"] }) - - def get_message(self, group, to='*'): - if self._socket._closed: - raise SessionError("Session has been closed.") - que = Queue() - for q in self.message_queue: - if q.env['group'] == group: - self.message_queue.remove(q) - self.old_message_queue.append(q) - que = q - if self.verbose: - sys.stdout.write("[Session] get_message: " + str(que.dump()) + "\n") - return q.msg - - def group_subscribe(self, group, instance = "*"): - if self._socket._closed: - raise SessionError("Session has been closed.") - - def group_unsubscribe(self, group, instance = "*"): - if self._socket._closed: - raise SessionError("Session has been closed.") diff --git a/src/bin/stats/tests/isc/config/Makefile.am b/src/bin/stats/tests/isc/config/Makefile.am deleted file mode 100644 index ffbecdae03..0000000000 --- a/src/bin/stats/tests/isc/config/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -EXTRA_DIST = __init__.py ccsession.py -CLEANFILES = __init__.pyc ccsession.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/config/__init__.py b/src/bin/stats/tests/isc/config/__init__.py deleted file mode 100644 index 4c49e956aa..0000000000 --- a/src/bin/stats/tests/isc/config/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from isc.config.ccsession import * diff --git a/src/bin/stats/tests/isc/config/ccsession.py b/src/bin/stats/tests/isc/config/ccsession.py deleted file mode 100644 index 50f7c1b163..0000000000 --- a/src/bin/stats/tests/isc/config/ccsession.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (C) 2010,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 mock-up module of isc.cc.session - -*** NOTE *** -It is only for testing stats_httpd module and not reusable for -external module. -""" - -import json -import os -import time -from isc.cc.session import Session - -COMMAND_CONFIG_UPDATE = "config_update" - -def parse_answer(msg): - assert 'result' in msg - try: - return msg['result'][0], msg['result'][1] - except IndexError: - return msg['result'][0], None - -def create_answer(rcode, arg = None): - if arg is None: - return { 'result': [ rcode ] } - else: - return { 'result': [ rcode, arg ] } - -def parse_command(msg): - assert 'command' in msg - try: - return msg['command'][0], msg['command'][1] - except IndexError: - return msg['command'][0], None - -def create_command(command_name, params = None): - if params is None: - return {"command": [command_name]} - else: - return {"command": [command_name, params]} - -def module_spec_from_file(spec_file, check = True): - try: - file = open(spec_file) - json_str = file.read() - module_spec = json.loads(json_str) - file.close() - return ModuleSpec(module_spec['module_spec'], check) - except IOError as ioe: - raise ModuleSpecError("JSON read error: " + str(ioe)) - except ValueError as ve: - raise ModuleSpecError("JSON parse error: " + str(ve)) - except KeyError as err: - raise ModuleSpecError("Data definition has no module_spec element") - -class ModuleSpecError(Exception): - pass - -class ModuleSpec: - def __init__(self, module_spec, check = True): - # check only confi_data for testing - if check and "config_data" in module_spec: - _check_config_spec(module_spec["config_data"]) - self._module_spec = module_spec - - def get_config_spec(self): - return self._module_spec['config_data'] - - def get_commands_spec(self): - return self._module_spec['commands'] - - def get_module_name(self): - return self._module_spec['module_name'] - -def _check_config_spec(config_data): - # config data is a list of items represented by dicts that contain - # things like "item_name", depending on the type they can have - # specific subitems - """Checks a list that contains the configuration part of the - specification. Raises a ModuleSpecError if there is a - problem.""" - if type(config_data) != list: - raise ModuleSpecError("config_data is of type " + str(type(config_data)) + ", not a list of items") - for config_item in config_data: - _check_item_spec(config_item) - -def _check_item_spec(config_item): - """Checks the dict that defines one config item - (i.e. containing "item_name", "item_type", etc. - Raises a ModuleSpecError if there is an error""" - if type(config_item) != dict: - raise ModuleSpecError("item spec not a dict") - if "item_name" not in config_item: - raise ModuleSpecError("no item_name in config item") - if type(config_item["item_name"]) != str: - raise ModuleSpecError("item_name is not a string: " + str(config_item["item_name"])) - item_name = config_item["item_name"] - if "item_type" not in config_item: - raise ModuleSpecError("no item_type in config item") - item_type = config_item["item_type"] - if type(item_type) != str: - raise ModuleSpecError("item_type in " + item_name + " is not a string: " + str(type(item_type))) - if item_type not in ["integer", "real", "boolean", "string", "list", "map", "any"]: - raise ModuleSpecError("unknown item_type in " + item_name + ": " + item_type) - if "item_optional" in config_item: - if type(config_item["item_optional"]) != bool: - raise ModuleSpecError("item_default in " + item_name + " is not a boolean") - if not config_item["item_optional"] and "item_default" not in config_item: - raise ModuleSpecError("no default value for non-optional item " + item_name) - else: - raise ModuleSpecError("item_optional not in item " + item_name) - if "item_default" in config_item: - item_default = config_item["item_default"] - if (item_type == "integer" and type(item_default) != int) or \ - (item_type == "real" and type(item_default) != float) or \ - (item_type == "boolean" and type(item_default) != bool) or \ - (item_type == "string" and type(item_default) != str) or \ - (item_type == "list" and type(item_default) != list) or \ - (item_type == "map" and type(item_default) != dict): - raise ModuleSpecError("Wrong type for item_default in " + item_name) - # TODO: once we have check_type, run the item default through that with the list|map_item_spec - if item_type == "list": - if "list_item_spec" not in config_item: - raise ModuleSpecError("no list_item_spec in list item " + item_name) - if type(config_item["list_item_spec"]) != dict: - raise ModuleSpecError("list_item_spec in " + item_name + " is not a dict") - _check_item_spec(config_item["list_item_spec"]) - if item_type == "map": - if "map_item_spec" not in config_item: - raise ModuleSpecError("no map_item_sepc in map item " + item_name) - if type(config_item["map_item_spec"]) != list: - raise ModuleSpecError("map_item_spec in " + item_name + " is not a list") - for map_item in config_item["map_item_spec"]: - if type(map_item) != dict: - raise ModuleSpecError("map_item_spec element is not a dict") - _check_item_spec(map_item) - if 'item_format' in config_item and 'item_default' in config_item: - item_format = config_item["item_format"] - item_default = config_item["item_default"] - if not _check_format(item_default, item_format): - raise ModuleSpecError( - "Wrong format for " + str(item_default) + " in " + str(item_name)) - -def _check_format(value, format_name): - """Check if specified value and format are correct. Return True if - is is correct.""" - # TODO: should be added other format types if necessary - time_formats = { 'date-time' : "%Y-%m-%dT%H:%M:%SZ", - 'date' : "%Y-%m-%d", - 'time' : "%H:%M:%S" } - for fmt in time_formats: - if format_name == fmt: - try: - time.strptime(value, time_formats[fmt]) - return True - except (ValueError, TypeError): - break - return False - -class ModuleCCSessionError(Exception): - pass - -class DataNotFoundError(Exception): - pass - -class ConfigData: - def __init__(self, specification): - self.specification = specification - - def get_value(self, identifier): - """Returns a tuple where the first item is the value at the - given identifier, and the second item is absolutely False - even if the value is an unset default or not. Raises an - DataNotFoundError if the identifier is not found in the - specification file. - *** NOTE *** - There are some differences from the original method. This - method never handles local settings like the original - method. But these different behaviors aren't so big issues - for a mock-up method of stats_httpd because stats_httpd - calls this method at only first.""" - for config_map in self.get_module_spec().get_config_spec(): - if config_map['item_name'] == identifier: - if 'item_default' in config_map: - return config_map['item_default'], False - raise DataNotFoundError("item_name %s is not found in the specfile" % identifier) - - def get_module_spec(self): - return self.specification - -class ModuleCCSession(ConfigData): - def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None): - module_spec = module_spec_from_file(spec_file_name) - ConfigData.__init__(self, module_spec) - self._module_name = module_spec.get_module_name() - self.set_config_handler(config_handler) - self.set_command_handler(command_handler) - if not cc_session: - self._session = Session(verbose=True) - else: - self._session = cc_session - - def start(self): - pass - - def close(self): - self._session.close() - - def check_command(self, nonblock=True): - msg, env = self._session.group_recvmsg(nonblock) - if not msg or 'result' in msg: - return - cmd, arg = parse_command(msg) - answer = None - if cmd == COMMAND_CONFIG_UPDATE and self._config_handler: - answer = self._config_handler(arg) - elif env['group'] == self._module_name and self._command_handler: - answer = self._command_handler(cmd, arg) - if answer: - self._session.group_reply(env, answer) - - def set_config_handler(self, config_handler): - self._config_handler = config_handler - # should we run this right now since we've changed the handler? - - def set_command_handler(self, command_handler): - self._command_handler = command_handler - - def get_module_spec(self): - return self.specification - - def get_socket(self): - return self._session._socket - diff --git a/src/bin/stats/tests/isc/log/Makefile.am b/src/bin/stats/tests/isc/log/Makefile.am deleted file mode 100644 index 457b9de1c2..0000000000 --- a/src/bin/stats/tests/isc/log/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -EXTRA_DIST = __init__.py -CLEANFILES = __init__.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/log/__init__.py b/src/bin/stats/tests/isc/log/__init__.py deleted file mode 100644 index 641cf790c1..0000000000 --- a/src/bin/stats/tests/isc/log/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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 file is not installed. The log.so is installed into the right place. -# It is only to find it in the .libs directory when we run as a test or -# from the build directory. -# But as nobody gives us the builddir explicitly (and we can't use generation -# from .in file, as it would put us into the builddir and we wouldn't be found) -# we guess from current directory. Any idea for something better? This should -# be enough for the tests, but would it work for B10_FROM_SOURCE as well? -# Should we look there? Or define something in bind10_config? - -import os -import sys - -for base in sys.path[:]: - loglibdir = os.path.join(base, 'isc/log/.libs') - if os.path.exists(loglibdir): - sys.path.insert(0, loglibdir) - -from log import * diff --git a/src/bin/stats/tests/isc/util/Makefile.am b/src/bin/stats/tests/isc/util/Makefile.am deleted file mode 100644 index 9c74354ca3..0000000000 --- a/src/bin/stats/tests/isc/util/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -EXTRA_DIST = __init__.py process.py -CLEANFILES = __init__.pyc process.pyc - -CLEANDIRS = __pycache__ - -clean-local: - rm -rf $(CLEANDIRS) diff --git a/src/bin/stats/tests/isc/util/__init__.py b/src/bin/stats/tests/isc/util/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/bin/stats/tests/isc/util/process.py b/src/bin/stats/tests/isc/util/process.py deleted file mode 100644 index 0f764c1872..0000000000 --- a/src/bin/stats/tests/isc/util/process.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (C) 2010 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 dummy function of isc.util.process.rename() -""" - -def rename(name=None): - pass diff --git a/src/bin/stats/tests/testdata/Makefile.am b/src/bin/stats/tests/testdata/Makefile.am deleted file mode 100644 index 1b8df6d736..0000000000 --- a/src/bin/stats/tests/testdata/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -EXTRA_DIST = stats_test.spec diff --git a/src/bin/stats/tests/testdata/stats_test.spec b/src/bin/stats/tests/testdata/stats_test.spec deleted file mode 100644 index 8136756440..0000000000 --- a/src/bin/stats/tests/testdata/stats_test.spec +++ /dev/null @@ -1,19 +0,0 @@ -{ - "module_spec": { - "module_name": "Stats", - "module_description": "Stats daemon", - "config_data": [], - "commands": [ - { - "command_name": "status", - "command_description": "identify whether stats module is alive or not", - "command_args": [] - }, - { - "command_name": "the_dummy", - "command_description": "this is for testing", - "command_args": [] - } - ] - } -} From 9bbc77b6b8381c9a6d831e490a7715ba84b9356f Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:00:30 +0900 Subject: [PATCH 883/974] [trac930] add utilities and mock-up modules for unittests of statistics modules and change some environ variables (PYTHONPATH, CONFIG_TESTDATA_PATH) in Makefile test_utilies.py internally calls msgq, cfgmgr and some mock modules with threads for as real situation as possible. --- src/bin/stats/tests/Makefile.am | 8 +- src/bin/stats/tests/test_utils.py | 293 ++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 src/bin/stats/tests/test_utils.py diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index ee79de2f0d..0ed7766b09 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -1,8 +1,7 @@ -SUBDIRS = isc http testdata PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ PYTESTS = b10-stats_test.py b10-stats-httpd_test.py -EXTRA_DIST = $(PYTESTS) fake_time.py fake_socket.py fake_select.py -CLEANFILES = fake_time.pyc fake_socket.pyc fake_select.pyc +EXTRA_DIST = $(PYTESTS) test_utils.py +CLEANFILES = test_utils.pyc # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. @@ -21,8 +20,9 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ - PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \ + PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests:$(abs_top_builddir)/src/bin/msgq:$(abs_top_builddir)/src/lib/python/isc/config \ B10_FROM_SOURCE=$(abs_top_srcdir) \ + CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/tests/testdata \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py new file mode 100644 index 0000000000..bd23182d2c --- /dev/null +++ b/src/bin/stats/tests/test_utils.py @@ -0,0 +1,293 @@ +""" +Utilities and mock modules for unittests of statistics modules + +""" +import os +import io +import time +import sys +import threading +import tempfile + +import msgq +import isc.config.cfgmgr +import stats +import stats_httpd + +# TODO: consider appropriate timeout seconds +TIMEOUT_SEC = 0.01 + +def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC*2): + if not session: + cc_session = isc.cc.Session() + else: + cc_session = session + orig_timeout = cc_session.get_timeout() + cc_session.set_timeout(timeout * 1000) + command = isc.config.ccsession.create_command(command_name, params) + seq = cc_session.group_sendmsg(command, module_name) + try: + (answer, env) = cc_session.group_recvmsg(nonblock, seq) + if answer: + return isc.config.ccsession.parse_answer(answer) + except isc.cc.SessionTimeout: + pass + finally: + if not session: + cc_session.close() + else: + cc_session.set_timeout(orig_timeout) + +def send_shutdown(module_name): + return send_command("shutdown", module_name) + +class ThreadingServerManager: + def __init__(self, server_class, verbose): + self.server_class = server_class + self.server_class_name = server_class.__name__ + self.verbose = verbose + self.server = self.server_class(self.verbose) + self.server._thread = threading.Thread( + name=self.server_class_name, target=self.server.run) + self.server._thread.daemon = True + + def run(self): + self.server._thread.start() + self.server._started.wait() + + def shutdown(self): + self.server.shutdown() + self.server._thread.join(TIMEOUT_SEC) + +class MockMsgq: + def __init__(self, verbose): + self.verbose = verbose + self._started = threading.Event() + self.msgq = msgq.MsgQ(None, verbose) + result = self.msgq.setup() + if result: + sys.exit("Error on Msgq startup: %s" % result) + + def run(self): + self._started.set() + try: + self.msgq.run() + except Exception: + pass + finally: + self.shutdown() + + def shutdown(self): + self.msgq.shutdown() + +class MockCfgmgr: + def __init__(self, verbose): + self._started = threading.Event() + self.cfgmgr = isc.config.cfgmgr.ConfigManager( + os.environ['CONFIG_TESTDATA_PATH'], "b10-config.db") + self.cfgmgr.read_config() + + def run(self): + self._started.set() + try: + self.cfgmgr.run() + finally: + self.shutdown() + + def shutdown(self): + self.cfgmgr.running = False + +class MockBoss: + spec_str = """\ +{ + "module_spec": { + "module_name": "Boss", + "module_description": "Mock Master process", + "config_data": [], + "commands": [ + { + "command_name": "sendstats", + "command_description": "Send data to a statistics module at once", + "command_args": [] + } + ], + "statistics": [ + { + "item_name": "boot_time", + "item_type": "string", + "item_optional": false, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Boot time", + "item_description": "A date time when bind10 process starts initially", + "item_format": "date-time" + } + ] + } +} +""" + _BASETIME = (2011, 6, 22, 8, 14, 8, 2, 173, 0) + + def __init__(self, verbose): + self.verbose = verbose + self._started = threading.Event() + self.running = False + self.spec_file = io.StringIO(self.spec_str) + # create ModuleCCSession object + self.mccs = isc.config.ModuleCCSession( + self.spec_file, + self.config_handler, + self.command_handler) + self.spec_file.close() + self.cc_session = self.mccs._session + self.got_command_name = '' + + def run(self): + self.mccs.start() + self.running = True + self._started.set() + while self.running: + self.mccs.check_command(False) + + def shutdown(self): + self.running = False + + def config_handler(self, new_config): + return isc.config.create_answer(0) + + def command_handler(self, command, *args, **kwargs): + self.got_command_name = command + if command == 'sendstats': + params = { "owner": "Boss", + "data": { + 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', self._BASETIME) + } + } + return send_command("set", "Stats", params=params, session=self.cc_session) + return isc.config.create_answer(1, "Unknown Command") + +class MockAuth: + spec_str = """\ +{ + "module_spec": { + "module_name": "Auth", + "module_description": "Mock Authoritative service", + "config_data": [], + "commands": [ + { + "command_name": "sendstats", + "command_description": "Send data to a statistics module at once", + "command_args": [] + } + ], + "statistics": [ + { + "item_name": "queries.tcp", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "Queries TCP ", + "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" + }, + { + "item_name": "queries.udp", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "Queries UDP", + "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially" + } + ] + } +} +""" + def __init__(self, verbose): + self.verbose = verbose + self._started = threading.Event() + self.running = False + self.spec_file = io.StringIO(self.spec_str) + # create ModuleCCSession object + self.mccs = isc.config.ModuleCCSession( + self.spec_file, + self.config_handler, + self.command_handler) + self.spec_file.close() + self.cc_session = self.mccs._session + self.got_command_name = '' + self.queries_tcp = 3 + self.queries_udp = 2 + + def run(self): + self.mccs.start() + self.running = True + self._started.set() + while self.running: + self.mccs.check_command(False) + + def shutdown(self): + self.running = False + + def config_handler(self, new_config): + return isc.config.create_answer(0) + + def command_handler(self, command, *args, **kwargs): + self.got_command_name = command + if command == 'sendstats': + params = { "owner": "Auth", + "data": { 'queries.tcp': self.queries_tcp, + 'queries.udp': self.queries_udp } } + return send_command("set", "Stats", params=params, session=self.cc_session) + return isc.config.create_answer(1, "Unknown Command") + +class MyStats(stats.Stats): + def __init__(self, verbose): + self._started = threading.Event() + stats.Stats.__init__(self, verbose) + + def run(self): + self._started.set() + stats.Stats.start(self) + + def shutdown(self): + send_shutdown("Stats") + +class MyStatsHttpd(stats_httpd.StatsHttpd): + def __init__(self, verbose): + self._started = threading.Event() + stats_httpd.StatsHttpd.__init__(self, verbose) + + def run(self): + self._started.set() + stats_httpd.StatsHttpd.start(self) + + def shutdown(self): + send_shutdown("StatsHttpd") + +class BaseModules: + def __init__(self, verbose): + self.verbose = verbose + self.class_name = BaseModules.__name__ + + # Change value of BIND10_MSGQ_SOCKET_FILE in environment variables + os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='unix_socket.') + # MockMsgq + self.msgq = ThreadingServerManager(MockMsgq, self.verbose) + self.msgq.run() + # MockCfgmgr + self.cfgmgr = ThreadingServerManager(MockCfgmgr, self.verbose) + self.cfgmgr.run() + # MockBoss + self.boss = ThreadingServerManager(MockBoss, self.verbose) + self.boss.run() + # MockAuth + self.auth = ThreadingServerManager(MockAuth, self.verbose) + self.auth.run() + + def shutdown(self): + # MockAuth + self.auth.shutdown() + # MockBoss + self.boss.shutdown() + # MockCfgmgr + self.cfgmgr.shutdown() + # MockMsgq + self.msgq.shutdown() From 9354737244e0bb7c22ec684ed652c89991eca913 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:12:09 +0900 Subject: [PATCH 884/974] [trac930] remove descriptions about "stats-schema.spec" and add description about new features because stats module can be requested to show statistics data schema. --- src/bin/stats/b10-stats-httpd.8 | 6 +----- src/bin/stats/b10-stats-httpd.xml | 8 +------- src/bin/stats/b10-stats.8 | 4 ---- src/bin/stats/b10-stats.xml | 6 ------ 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/bin/stats/b10-stats-httpd.8 b/src/bin/stats/b10-stats-httpd.8 index ed4aafa6c6..1206e1d791 100644 --- a/src/bin/stats/b10-stats-httpd.8 +++ b/src/bin/stats/b10-stats-httpd.8 @@ -36,7 +36,7 @@ b10-stats-httpd \- BIND 10 HTTP server for HTTP/XML interface of statistics .PP \fBb10\-stats\-httpd\fR -is a standalone HTTP server\&. It is intended for HTTP/XML interface for statistics module\&. This server process runs as a process separated from the process of the BIND 10 Stats daemon (\fBb10\-stats\fR)\&. The server is initially executed by the BIND 10 boss process (\fBbind10\fR) and eventually exited by it\&. The server is intended to be server requests by HTTP clients like web browsers and third\-party modules\&. When the server is asked, it requests BIND 10 statistics data from +is a standalone HTTP server\&. It is intended for HTTP/XML interface for statistics module\&. This server process runs as a process separated from the process of the BIND 10 Stats daemon (\fBb10\-stats\fR)\&. The server is initially executed by the BIND 10 boss process (\fBbind10\fR) and eventually exited by it\&. The server is intended to be server requests by HTTP clients like web browsers and third\-party modules\&. When the server is asked, it requests BIND 10 statistics data or its schema from \fBb10\-stats\fR, and it sends the data back in Python dictionary format and the server converts it into XML format\&. The server sends it to the HTTP client\&. The server can send three types of document, which are XML (Extensible Markup Language), XSD (XML Schema definition) and XSL (Extensible Stylesheet Language)\&. The XML document is the statistics data of BIND 10, The XSD document is the data schema of it, and The XSL document is the style sheet to be showed for the web browsers\&. There is different URL for each document\&. But please note that you would be redirected to the URL of XML document if you request the URL of the root document\&. For example, you would be redirected to http://127\&.0\&.0\&.1:8000/bind10/statistics/xml if you request http://127\&.0\&.0\&.1:8000/\&. Please see the manual and the spec file of \fBb10\-stats\fR for more details about the items of BIND 10 statistics\&. The server uses CC session in communication with @@ -66,10 +66,6 @@ bindctl(1)\&. Please see the manual of bindctl(1) about how to configure the settings\&. .PP -/usr/local/share/bind10\-devel/stats\-schema\&.spec -\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via -bindctl(1)\&. -.PP /usr/local/share/bind10\-devel/stats\-httpd\-xml\&.tpl \(em the template file of XML document\&. diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml index 34c704f509..3636d9d543 100644 --- a/src/bin/stats/b10-stats-httpd.xml +++ b/src/bin/stats/b10-stats-httpd.xml @@ -57,7 +57,7 @@ by the BIND 10 boss process (bind10) and eventually exited by it. The server is intended to be server requests by HTTP clients like web browsers and third-party modules. When the server is - asked, it requests BIND 10 statistics data from + asked, it requests BIND 10 statistics data or its schema from b10-stats, and it sends the data back in Python dictionary format and the server converts it into XML format. The server sends it to the HTTP client. The server can send three types of document, @@ -112,12 +112,6 @@ of bindctl1 about how to configure the settings. - /usr/local/share/bind10-devel/stats-schema.spec - - — This is a spec file for data schema of - of BIND 10 statistics. This schema cannot be configured - via bindctl1. - /usr/local/share/bind10-devel/stats-httpd-xml.tpl diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8 index 98b109b374..0204ca10bc 100644 --- a/src/bin/stats/b10-stats.8 +++ b/src/bin/stats/b10-stats.8 @@ -135,10 +135,6 @@ See other manual pages for explanations for their statistics that are kept track \fBb10\-stats\fR\&. It contains commands for \fBb10\-stats\fR\&. They can be invoked via bindctl(1)\&. -.PP -/usr/local/share/bind10\-devel/stats\-schema\&.spec -\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via -bindctl(1)\&. .SH "SEE ALSO" .PP diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index 9709175278..13ada7aa4a 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -213,12 +213,6 @@ invoked via bindctl1. - /usr/local/share/bind10-devel/stats-schema.spec - - — This is a spec file for data schema of - of BIND 10 statistics. This schema cannot be configured - via bindctl1. - From 96dd4d2daf1fb91672a798fa478da0ec8a7ac737 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:13:17 +0900 Subject: [PATCH 885/974] [trac930] add a column "Owner" in the table tag --- src/bin/stats/stats-httpd-xsl.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/stats/stats-httpd-xsl.tpl b/src/bin/stats/stats-httpd-xsl.tpl index 01ffdc681b..a1f6406a5a 100644 --- a/src/bin/stats/stats-httpd-xsl.tpl +++ b/src/bin/stats/stats-httpd-xsl.tpl @@ -44,6 +44,7 @@ td.title {

BIND 10 Statistics

Owner Title Value
DummyFoo
+ From 61681dac2023240a4a029072add3a39809ccb7f0 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:18:38 +0900 Subject: [PATCH 886/974] [trac930] remove description about removing statistics data by stats module update example format in bindctl when show command of stats module is invoked --- doc/guide/bind10-guide.html | 30 ++++++++++++++++++------------ doc/guide/bind10-guide.xml | 32 ++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html index a9a4cc6223..1070a2e4a8 100644 --- a/doc/guide/bind10-guide.html +++ b/doc/guide/bind10-guide.html @@ -717,24 +717,30 @@ This may be a temporary setting until then.

- This stats daemon provides commands to identify if it is running, - show specified or all statistics data, set values, remove data, - and reset data. + This stats daemon provides commands to identify if it is + running, show specified or all statistics data, show specified + or all statistics data schema, and set specified statistics + data. For example, using bindctl:

 > Stats show
 {
-    "auth.queries.tcp": 1749,
-    "auth.queries.udp": 867868,
-    "bind10.boot_time": "2011-01-20T16:59:03Z",
-    "report_time": "2011-01-20T17:04:06Z",
-    "stats.boot_time": "2011-01-20T16:59:05Z",
-    "stats.last_update_time": "2011-01-20T17:04:05Z",
-    "stats.lname": "4d3869d9_a@jreed.example.net",
-    "stats.start_time": "2011-01-20T16:59:05Z",
-    "stats.timestamp": 1295543046.823504
+    "Auth": {
+        "queries.tcp": 1749,
+        "queries.udp": 867868
+    },
+    "Boss": {
+        "boot_time": "2011-01-20T16:59:03Z"
+    },
+    "Stats": {
+        "boot_time": "2011-01-20T16:59:05Z",
+        "last_update_time": "2011-01-20T17:04:05Z",
+        "lname": "4d3869d9_a@jreed.example.net",
+        "report_time": "2011-01-20T17:04:06Z",
+        "timestamp": 1295543046.823504
+    }
 }
        

Chapter 14. Logging

Logging configuration

diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index d34746b519..59442adddb 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1522,24 +1522,32 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - This stats daemon provides commands to identify if it is running, - show specified or all statistics data, set values, remove data, - and reset data. + + This stats daemon provides commands to identify if it is + running, show specified or all statistics data, show specified + or all statistics data schema, and set specified statistics + data. For example, using bindctl: + > Stats show { - "auth.queries.tcp": 1749, - "auth.queries.udp": 867868, - "bind10.boot_time": "2011-01-20T16:59:03Z", - "report_time": "2011-01-20T17:04:06Z", - "stats.boot_time": "2011-01-20T16:59:05Z", - "stats.last_update_time": "2011-01-20T17:04:05Z", - "stats.lname": "4d3869d9_a@jreed.example.net", - "stats.start_time": "2011-01-20T16:59:05Z", - "stats.timestamp": 1295543046.823504 + "Auth": { + "queries.tcp": 1749, + "queries.udp": 867868 + }, + "Boss": { + "boot_time": "2011-01-20T16:59:03Z" + }, + "Stats": { + "boot_time": "2011-01-20T16:59:05Z", + "last_update_time": "2011-01-20T17:04:05Z", + "lname": "4d3869d9_a@jreed.example.net", + "report_time": "2011-01-20T17:04:06Z", + "timestamp": 1295543046.823504 + } } From 36a53f41a7da580926111dca65652d6389fcd909 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:21:49 +0900 Subject: [PATCH 887/974] [trac930] update argument name and argument format of set command in auth module and boss module and also update related unittests of their modules --- src/bin/auth/statistics.cc | 7 ++++--- src/bin/auth/tests/statistics_unittest.cc | 8 +++++--- src/bin/bind10/bind10_src.py.in | 7 +++++-- src/bin/bind10/tests/bind10_test.py.in | 5 +++-- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc index 76e50074fc..444fb8b35b 100644 --- a/src/bin/auth/statistics.cc +++ b/src/bin/auth/statistics.cc @@ -67,10 +67,11 @@ AuthCountersImpl::submitStatistics() const { } std::stringstream statistics_string; statistics_string << "{\"command\": [\"set\"," - << "{ \"stats_data\": " - << "{ \"auth.queries.udp\": " + << "{ \"owner\": \"Auth\"," + << " \"data\":" + << "{ \"queries.udp\": " << counters_.at(AuthCounters::COUNTER_UDP_QUERY) - << ", \"auth.queries.tcp\": " + << ", \"queries.tcp\": " << counters_.at(AuthCounters::COUNTER_TCP_QUERY) << " }" << "}" diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc index 9a3dded837..cd2755b110 100644 --- a/src/bin/auth/tests/statistics_unittest.cc +++ b/src/bin/auth/tests/statistics_unittest.cc @@ -201,12 +201,14 @@ TEST_F(AuthCountersTest, submitStatistics) { // Command is "set". EXPECT_EQ("set", statistics_session_.sent_msg->get("command") ->get(0)->stringValue()); + EXPECT_EQ("Auth", statistics_session_.sent_msg->get("command") + ->get(1)->get("owner")->stringValue()); ConstElementPtr statistics_data = statistics_session_.sent_msg ->get("command")->get(1) - ->get("stats_data"); + ->get("data"); // UDP query counter is 2 and TCP query counter is 1. - EXPECT_EQ(2, statistics_data->get("auth.queries.udp")->intValue()); - EXPECT_EQ(1, statistics_data->get("auth.queries.tcp")->intValue()); + EXPECT_EQ(2, statistics_data->get("queries.udp")->intValue()); + EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue()); } } diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 28af8cc58f..c0e4e93728 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -85,7 +85,7 @@ isc.util.process.rename(sys.argv[0]) # number, and the overall BIND 10 version number (set in configure.ac). VERSION = "bind10 20110223 (BIND 10 @PACKAGE_VERSION@)" -# This is for bind10.boottime of stats module +# This is for boot_time of Boss _BASETIME = time.gmtime() class RestartSchedule: @@ -326,7 +326,10 @@ class BoB: elif command == "sendstats": # send statistics data to the stats daemon immediately cmd = isc.config.ccsession.create_command( - 'set', self._get_stats_data()) + 'set', { "owner": "Boss", + "data": { + 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + }}) seq = self.cc_session.group_sendmsg(cmd, 'Stats') # Consume the answer, in case it becomes a orphan message. try: diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 424a610803..48b19924e4 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -159,8 +159,9 @@ class TestBoB(unittest.TestCase): self.assertEqual(bob.cc_session.group, "Stats") self.assertEqual(bob.cc_session.msg, isc.config.ccsession.create_command( - 'set', { "stats_data": { - 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + "set", { "owner": "Boss", + "data": { + "boot_time": time.strftime("%Y-%m-%dT%H:%M:%SZ", _BASETIME) }})) # "ping" command self.assertEqual(bob.command_handler("ping", None), From 6f6a4cf9d98f2b4550e0949da1e20a7f38440610 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 16:33:59 +0900 Subject: [PATCH 888/974] [trac930] update spec file of stats module - update description of status command, shutdown command and show command - change argument of show command (Owner module name of statistics data can be specified) - change argument of set command (Owner module name of statistics data is always required) - add showschema command which shows statistics data schema of each module specified) - disabled reset command and remove command --- src/bin/stats/stats.spec | 75 +++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/bin/stats/stats.spec b/src/bin/stats/stats.spec index 635eb486a1..e716b62279 100644 --- a/src/bin/stats/stats.spec +++ b/src/bin/stats/stats.spec @@ -6,18 +6,51 @@ "commands": [ { "command_name": "status", - "command_description": "identify whether stats module is alive or not", + "command_description": "Show status of the stats daemon", + "command_args": [] + }, + { + "command_name": "shutdown", + "command_description": "Shut down the stats module", "command_args": [] }, { "command_name": "show", - "command_description": "show the specified/all statistics data", + "command_description": "Show the specified/all statistics data", "command_args": [ { - "item_name": "stats_item_name", + "item_name": "owner", "item_type": "string", "item_optional": true, - "item_default": "" + "item_default": "", + "item_description": "module name of the owner of the statistics data" + }, + { + "item_name": "name", + "item_type": "string", + "item_optional": true, + "item_default": "", + "item_description": "statistics item name of the owner" + } + ] + }, + { + "command_name": "showschema", + "command_description": "show the specified/all statistics shema", + "command_args": [ + { + "item_name": "owner", + "item_type": "string", + "item_optional": true, + "item_default": "", + "item_description": "module name of the owner of the statistics data" + }, + { + "item_name": "name", + "item_type": "string", + "item_optional": true, + "item_default": "", + "item_description": "statistics item name of the owner" } ] }, @@ -26,35 +59,21 @@ "command_description": "set the value of specified name in statistics data", "command_args": [ { - "item_name": "stats_data", + "item_name": "owner", + "item_type": "string", + "item_optional": false, + "item_default": "", + "item_description": "module name of the owner of the statistics data" + }, + { + "item_name": "data", "item_type": "map", "item_optional": false, "item_default": {}, + "item_description": "statistics data set of the owner", "map_item_spec": [] } ] - }, - { - "command_name": "remove", - "command_description": "remove the specified name from statistics data", - "command_args": [ - { - "item_name": "stats_item_name", - "item_type": "string", - "item_optional": false, - "item_default": "" - } - ] - }, - { - "command_name": "reset", - "command_description": "reset all statistics data to default values except for several constant names", - "command_args": [] - }, - { - "command_name": "shutdown", - "command_description": "Shut down the stats module", - "command_args": [] } ], "statistics": [ @@ -100,7 +119,7 @@ "item_default": "", "item_title": "Local Name", "item_description": "A localname of stats module given via CC protocol" - } + } ] } } From 493a6449b37b34ac5fe36257b266c229e34d105c Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 19:40:15 +0900 Subject: [PATCH 889/974] [trac930] modify stats_httpd.py.in - remove "stats-schema.spec" setting and getting statistics data schema via this spec file - add "version" item in DEFAULT_CONFIG - get the address family by socket.getaddrinfo function with specified server_address in advance, and create HttpServer object once, in stead of creating double HttpServer objects for IPv6 and IPv4 in the prior code (It is aimed for avoiding to fail to close the once opened sockets.) - open HTTP port in start method - avoid calling config_handler recursively in the except statement - create XML, XSD, XSL documents after getting statistics data and schema from remote stats module via CC session - definitely close once opened template file object --- src/bin/stats/stats_httpd.py.in | 227 +++++++++++++++++--------------- 1 file changed, 120 insertions(+), 107 deletions(-) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index 6be6adf26c..ef7d9f6048 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -57,7 +57,6 @@ else: BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd.spec" -SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec" XML_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xml.tpl" XSD_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsd.tpl" XSL_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsl.tpl" @@ -69,7 +68,7 @@ XSD_URL_PATH = '/bind10/statistics/xsd' XSL_URL_PATH = '/bind10/statistics/xsl' # TODO: This should be considered later. XSD_NAMESPACE = 'http://bind10.isc.org' + XSD_URL_PATH -DEFAULT_CONFIG = dict(listen_on=[('127.0.0.1', 8000)]) +DEFAULT_CONFIG = dict(version=0, listen_on=[('127.0.0.1', 8000)]) # Assign this process name isc.util.process.rename() @@ -161,8 +160,6 @@ class StatsHttpd: self.httpd = [] self.open_mccs() self.load_config() - self.load_templates() - self.open_httpd() def open_mccs(self): """Opens a ModuleCCSession object""" @@ -171,10 +168,6 @@ class StatsHttpd: self.mccs = isc.config.ModuleCCSession( SPECFILE_LOCATION, self.config_handler, self.command_handler) self.cc_session = self.mccs._session - # read spec file of stats module and subscribe 'Stats' - self.stats_module_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION) - self.stats_config_spec = self.stats_module_spec.get_config_spec() - self.stats_module_name = self.stats_module_spec.get_module_name() def close_mccs(self): """Closes a ModuleCCSession object""" @@ -208,45 +201,41 @@ class StatsHttpd: for addr in self.http_addrs: self.httpd.append(self._open_httpd(addr)) - def _open_httpd(self, server_address, address_family=None): + def _open_httpd(self, server_address): + httpd = None try: - # try IPv6 at first - if address_family is not None: - HttpServer.address_family = address_family - elif socket.has_ipv6: - HttpServer.address_family = socket.AF_INET6 + # get address family for the server_address before + # creating HttpServer object + address_family = socket.getaddrinfo(*server_address)[0][0] + HttpServer.address_family = address_family httpd = HttpServer( server_address, HttpHandler, self.xml_handler, self.xsd_handler, self.xsl_handler, self.write_log) - except (socket.gaierror, socket.error, - OverflowError, TypeError) as err: - # try IPv4 next - if HttpServer.address_family == socket.AF_INET6: - httpd = self._open_httpd(server_address, socket.AF_INET) - else: - raise HttpServerError( - "Invalid address %s, port %s: %s: %s" % - (server_address[0], server_address[1], - err.__class__.__name__, err)) - else: logger.info(STATHTTPD_STARTED, server_address[0], server_address[1]) - return httpd + return httpd + except (socket.gaierror, socket.error, + OverflowError, TypeError) as err: + if httpd: + httpd.server_close() + raise HttpServerError( + "Invalid address %s, port %s: %s: %s" % + (server_address[0], server_address[1], + err.__class__.__name__, err)) def close_httpd(self): """Closes sockets for HTTP""" - if len(self.httpd) == 0: - return - for ht in self.httpd: + while len(self.httpd)>0: + ht = self.httpd.pop() logger.info(STATHTTPD_CLOSING, ht.server_address[0], ht.server_address[1]) ht.server_close() - self.httpd = [] def start(self): """Starts StatsHttpd objects to run. Waiting for client requests by using select.select functions""" + self.open_httpd() self.mccs.start() self.running = True while self.running: @@ -310,7 +299,8 @@ class StatsHttpd: except HttpServerError as err: logger.error(STATHTTPD_SERVER_ERROR, err) # restore old config - self.config_handler(old_config) + self.load_config(old_config) + self.open_httpd() return isc.config.ccsession.create_answer( 1, "[b10-stats-httpd] %s" % err) else: @@ -341,8 +331,7 @@ class StatsHttpd: the data which obtains from it""" try: seq = self.cc_session.group_sendmsg( - isc.config.ccsession.create_command('show'), - self.stats_module_name) + isc.config.ccsession.create_command('show'), 'Stats') (answer, env) = self.cc_session.group_recvmsg(False, seq) if answer: (rcode, value) = isc.config.ccsession.parse_answer(answer) @@ -357,34 +346,82 @@ class StatsHttpd: raise StatsHttpdError("Stats module: %s" % str(value)) def get_stats_spec(self): - """Just returns spec data""" - return self.stats_config_spec + """Requests statistics data to the Stats daemon and returns + the data which obtains from it""" + try: + seq = self.cc_session.group_sendmsg( + isc.config.ccsession.create_command('showschema'), 'Stats') + (answer, env) = self.cc_session.group_recvmsg(False, seq) + if answer: + (rcode, value) = isc.config.ccsession.parse_answer(answer) + if rcode == 0: + return value + else: + raise StatsHttpdError("Stats module: %s" % str(value)) + except (isc.cc.session.SessionTimeout, + isc.cc.session.SessionError) as err: + raise StatsHttpdError("%s: %s" % + (err.__class__.__name__, err)) - def load_templates(self): - """Setup the bodies of XSD and XSL documents to be responds to - HTTP clients. Before that it also creates XML tag structures by - using xml.etree.ElementTree.Element class and substitutes - concrete strings with parameters embed in the string.Template - object.""" + def xml_handler(self): + """Handler which requests to Stats daemon to obtain statistics + data and returns the body of XML document""" + xml_list=[] + for (mod, spec) in self.get_stats_data().items(): + if not spec: continue + elem1 = xml.etree.ElementTree.Element(str(mod)) + for (k, v) in spec.items(): + elem2 = xml.etree.ElementTree.Element(str(k)) + elem2.text = str(v) + elem1.append(elem2) + # The coding conversion is tricky. xml..tostring() of Python 3.2 + # returns bytes (not string) regardless of the coding, while + # tostring() of Python 3.1 returns a string. To support both + # cases transparently, we first make sure tostring() returns + # bytes by specifying utf-8 and then convert the result to a + # plain string (code below assume it). + xml_list.append( + str(xml.etree.ElementTree.tostring(elem1, encoding='utf-8'), + encoding='us-ascii')) + xml_string = "".join(xml_list) + self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute( + xml_string=xml_string, + xsd_namespace=XSD_NAMESPACE, + xsd_url_path=XSD_URL_PATH, + xsl_url_path=XSL_URL_PATH) + assert self.xml_body is not None + return self.xml_body + + def xsd_handler(self): + """Handler which just returns the body of XSD document""" # for XSD xsd_root = xml.etree.ElementTree.Element("all") # started with "all" tag - for item in self.get_stats_spec(): - element = xml.etree.ElementTree.Element( - "element", - dict( name=item["item_name"], - type=item["item_type"] if item["item_type"].lower() != 'real' else 'float', - minOccurs="1", - maxOccurs="1" ), - ) - annotation = xml.etree.ElementTree.Element("annotation") - appinfo = xml.etree.ElementTree.Element("appinfo") - documentation = xml.etree.ElementTree.Element("documentation") - appinfo.text = item["item_title"] - documentation.text = item["item_description"] - annotation.append(appinfo) - annotation.append(documentation) - element.append(annotation) - xsd_root.append(element) + for (mod, spec) in self.get_stats_spec().items(): + if not spec: continue + alltag = xml.etree.ElementTree.Element("all") + for item in spec: + element = xml.etree.ElementTree.Element( + "element", + dict( name=item["item_name"], + type=item["item_type"] if item["item_type"].lower() != 'real' else 'float', + minOccurs="1", + maxOccurs="1" ), + ) + annotation = xml.etree.ElementTree.Element("annotation") + appinfo = xml.etree.ElementTree.Element("appinfo") + documentation = xml.etree.ElementTree.Element("documentation") + appinfo.text = item["item_title"] + documentation.text = item["item_description"] + annotation.append(appinfo) + annotation.append(documentation) + element.append(annotation) + alltag.append(element) + + complextype = xml.etree.ElementTree.Element("complexType") + complextype.append(alltag) + mod_element = xml.etree.ElementTree.Element("element", { "name" : mod }) + mod_element.append(complextype) + xsd_root.append(mod_element) # The coding conversion is tricky. xml..tostring() of Python 3.2 # returns bytes (not string) regardless of the coding, while # tostring() of Python 3.1 returns a string. To support both @@ -398,25 +435,33 @@ class StatsHttpd: xsd_namespace=XSD_NAMESPACE ) assert self.xsd_body is not None + return self.xsd_body + def xsl_handler(self): + """Handler which just returns the body of XSL document""" # for XSL xsd_root = xml.etree.ElementTree.Element( "xsl:template", dict(match="*")) # started with xml:template tag - for item in self.get_stats_spec(): - tr = xml.etree.ElementTree.Element("tr") - td1 = xml.etree.ElementTree.Element( - "td", { "class" : "title", - "title" : item["item_description"] }) - td1.text = item["item_title"] - td2 = xml.etree.ElementTree.Element("td") - xsl_valueof = xml.etree.ElementTree.Element( - "xsl:value-of", - dict(select=item["item_name"])) - td2.append(xsl_valueof) - tr.append(td1) - tr.append(td2) - xsd_root.append(tr) + for (mod, spec) in self.get_stats_spec().items(): + if not spec: continue + for item in spec: + tr = xml.etree.ElementTree.Element("tr") + td0 = xml.etree.ElementTree.Element("td") + td0.text = str(mod) + td1 = xml.etree.ElementTree.Element( + "td", { "class" : "title", + "title" : item["item_description"] }) + td1.text = item["item_title"] + td2 = xml.etree.ElementTree.Element("td") + xsl_valueof = xml.etree.ElementTree.Element( + "xsl:value-of", + dict(select=mod+'/'+item["item_name"])) + td2.append(xsl_valueof) + tr.append(td0) + tr.append(td1) + tr.append(td2) + xsd_root.append(tr) # The coding conversion is tricky. xml..tostring() of Python 3.2 # returns bytes (not string) regardless of the coding, while # tostring() of Python 3.1 returns a string. To support both @@ -429,47 +474,15 @@ class StatsHttpd: xsl_string=xsl_string, xsd_namespace=XSD_NAMESPACE) assert self.xsl_body is not None - - def xml_handler(self): - """Handler which requests to Stats daemon to obtain statistics - data and returns the body of XML document""" - xml_list=[] - for (k, v) in self.get_stats_data().items(): - (k, v) = (str(k), str(v)) - elem = xml.etree.ElementTree.Element(k) - elem.text = v - # The coding conversion is tricky. xml..tostring() of Python 3.2 - # returns bytes (not string) regardless of the coding, while - # tostring() of Python 3.1 returns a string. To support both - # cases transparently, we first make sure tostring() returns - # bytes by specifying utf-8 and then convert the result to a - # plain string (code below assume it). - xml_list.append( - str(xml.etree.ElementTree.tostring(elem, encoding='utf-8'), - encoding='us-ascii')) - xml_string = "".join(xml_list) - self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute( - xml_string=xml_string, - xsd_namespace=XSD_NAMESPACE, - xsd_url_path=XSD_URL_PATH, - xsl_url_path=XSL_URL_PATH) - assert self.xml_body is not None - return self.xml_body - - def xsd_handler(self): - """Handler which just returns the body of XSD document""" - return self.xsd_body - - def xsl_handler(self): - """Handler which just returns the body of XSL document""" return self.xsl_body def open_template(self, file_name): """It opens a template file, and it loads all lines to a string variable and returns string. Template object includes the variable. Limitation of a file size isn't needed there.""" - lines = "".join( - open(file_name, 'r').readlines()) + f = open(file_name, 'r') + lines = "".join(f.readlines()) + f.close() assert lines is not None return string.Template(lines) From ed5311a26b7b1368f28191c405ec13da907213ae Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 19:56:24 +0900 Subject: [PATCH 890/974] [trac930] modify Stats - remove unneeded subject and listener classes - add StatsError for handling errors in Stats - add some new methods (update_modules, update_statistics_data and get_statistics_data) - modify implementations of existent commands(show and set) according changes stats.spec - remove reset and remove command because stats module couldn't manage other modules' statistics data schema - add implementation of strict validation of each statistics data (If the validation is failed, it puts out the error.) - stats module shows its PID when status command invoked - add new command showschema invokable via bindctl - set command requires arguments of owner module name and statistics item name - show and showschema commands accepts arguments of owner module name and statistics item name - exits at exit code 1 if got runtime errors - has boot time in _BASETIME --- src/bin/stats/stats.py.in | 550 +++++++++++++++++--------------------- 1 file changed, 251 insertions(+), 299 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index afed54405a..95a643f0aa 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -15,16 +15,17 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +Statistics daemon in BIND 10 + +""" import sys; sys.path.append ('@@PYTHONPATH@@') import os -import signal -import select from time import time, strftime, gmtime from optparse import OptionParser, OptionValueError -from collections import defaultdict -from isc.config.ccsession import ModuleCCSession, create_answer -from isc.cc import Session, SessionError +import isc +import isc.util.process import isc.log from isc.log_messages.stats_messages import * @@ -35,183 +36,111 @@ logger = isc.log.Logger("stats") # have #1074 DBG_STATS_MESSAGING = 30 +# This is for boot_time of Stats +_BASETIME = gmtime() + # for setproctitle -import isc.util.process isc.util.process.rename() # If B10_FROM_SOURCE is set in the environment, we use data files # from a directory relative to that, otherwise we use the ones # installed on the system if "B10_FROM_SOURCE" in os.environ: - BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + os.sep + "stats.spec" else: PREFIX = "@prefix@" DATAROOTDIR = "@datarootdir@" - BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" - BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) -SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats.spec" -SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec" + SPECFILE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + os.sep + "stats.spec" + SPECFILE_LOCATION = SPECFILE_LOCATION.replace("${datarootdir}", DATAROOTDIR)\ + .replace("${prefix}", PREFIX) -class Singleton(type): +def get_timestamp(): """ - A abstract class of singleton pattern + get current timestamp """ - # Because of singleton pattern: - # At the beginning of coding, one UNIX domain socket is needed - # for config manager, another socket is needed for stats module, - # then stats module might need two sockets. So I adopted the - # singleton pattern because I avoid creating multiple sockets in - # one stats module. But in the initial version stats module - # reports only via bindctl, so just one socket is needed. To use - # the singleton pattern is not important now. :( + return time() - def __init__(self, *args, **kwargs): - type.__init__(self, *args, **kwargs) - self._instances = {} +def get_datetime(gmt=None): + """ + get current datetime + """ + if not gmt: gmt = gmtime() + return strftime("%Y-%m-%dT%H:%M:%SZ", gmt) - def __call__(self, *args, **kwargs): - if args not in self._instances: - self._instances[args]={} - kw = tuple(kwargs.items()) - if kw not in self._instances[args]: - self._instances[args][kw] = type.__call__(self, *args, **kwargs) - return self._instances[args][kw] +def parse_spec(spec): + """ + parse spec type data + """ + def _parse_spec(spec): + item_type = spec['item_type'] + if item_type == "integer": + return int(spec.get('item_default', 0)) + elif item_type == "real": + return float(spec.get('item_default', 0.0)) + elif item_type == "boolean": + return bool(spec.get('item_default', False)) + elif item_type == "string": + return str(spec.get('item_default', "")) + elif item_type == "list": + return spec.get( + "item_default", + [ _parse_spec(s) for s in spec["list_item_spec"] ]) + elif item_type == "map": + return spec.get( + "item_default", + dict([ (s["item_name"], _parse_spec(s)) for s in spec["map_item_spec"] ]) ) + else: + return spec.get("item_default", None) + return dict([ (s['item_name'], _parse_spec(s)) for s in spec ]) class Callback(): """ A Callback handler class """ - def __init__(self, name=None, callback=None, args=(), kwargs={}): - self.name = name - self.callback = callback + def __init__(self, command=None, args=(), kwargs={}): + self.command = command self.args = args self.kwargs = kwargs def __call__(self, *args, **kwargs): - if not args: - args = self.args - if not kwargs: - kwargs = self.kwargs - if self.callback: - return self.callback(*args, **kwargs) + if not args: args = self.args + if not kwargs: kwargs = self.kwargs + if self.command: return self.command(*args, **kwargs) -class Subject(): - """ - A abstract subject class of observer pattern - """ - # Because of observer pattern: - # In the initial release, I'm also sure that observer pattern - # isn't definitely needed because the interface between gathering - # and reporting statistics data is single. However in the future - # release, the interfaces may be multiple, that is, multiple - # listeners may be needed. For example, one interface, which - # stats module has, is for between ''config manager'' and stats - # module, another interface is for between ''HTTP server'' and - # stats module, and one more interface is for between ''SNMP - # server'' and stats module. So by considering that stats module - # needs multiple interfaces in the future release, I adopted the - # observer pattern in stats module. But I don't have concrete - # ideas in case of multiple listener currently. +class StatsError(Exception): + """Exception class for Stats class""" + pass +class Stats: + """ + Main class of stats module + """ def __init__(self): - self._listeners = [] - - def attach(self, listener): - if not listener in self._listeners: - self._listeners.append(listener) - - def detach(self, listener): - try: - self._listeners.remove(listener) - except ValueError: - pass - - def notify(self, event, modifier=None): - for listener in self._listeners: - if modifier != listener: - listener.update(event) - -class Listener(): - """ - A abstract listener class of observer pattern - """ - def __init__(self, subject): - self.subject = subject - self.subject.attach(self) - self.events = {} - - def update(self, name): - if name in self.events: - callback = self.events[name] - return callback() - - def add_event(self, event): - self.events[event.name]=event - -class SessionSubject(Subject, metaclass=Singleton): - """ - A concrete subject class which creates CC session object - """ - def __init__(self, session=None): - Subject.__init__(self) - self.session=session self.running = False - - def start(self): - self.running = True - self.notify('start') - - def stop(self): - self.running = False - self.notify('stop') - - def check(self): - self.notify('check') - -class CCSessionListener(Listener): - """ - A concrete listener class which creates SessionSubject object and - ModuleCCSession object - """ - def __init__(self, subject): - Listener.__init__(self, subject) - self.session = subject.session - self.boot_time = get_datetime() - # create ModuleCCSession object - self.cc_session = ModuleCCSession(SPECFILE_LOCATION, - self.config_handler, - self.command_handler, - self.session) - - self.session = self.subject.session = self.cc_session._session - - # initialize internal data - self.stats_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION).get_config_spec() - self.stats_data = self.initialize_data(self.stats_spec) - - # add event handler invoked via SessionSubject object - self.add_event(Callback('start', self.start)) - self.add_event(Callback('stop', self.stop)) - self.add_event(Callback('check', self.check)) - # don't add 'command_' suffix to the special commands in - # order to prevent executing internal command via bindctl - + self.mccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, + self.config_handler, + self.command_handler) + self.cc_session = self.mccs._session + # get module spec + self.module_name = self.mccs.get_module_spec().get_module_name() + self.modules = {} + self.statistics_data = {} # get commands spec - self.commands_spec = self.cc_session.get_module_spec().get_commands_spec() - + self.commands_spec = self.mccs.get_module_spec().get_commands_spec() # add event handler related command_handler of ModuleCCSession - # invoked via bindctl + self.callbacks = {} for cmd in self.commands_spec: + # add prefix "command_" + name = "command_" + cmd["command_name"] try: - # add prefix "command_" - name = "command_" + cmd["command_name"] callback = getattr(self, name) - kwargs = self.initialize_data(cmd["command_args"]) - self.add_event(Callback(name=name, callback=callback, args=(), kwargs=kwargs)) - except AttributeError as ae: - logger.error(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) + kwargs = parse_spec(cmd["command_args"]) + self.callbacks[name] = Callback(command=callback, kwargs=kwargs) + except AttributeError: + raise StatsError(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) + self.mccs.start() def _update_stats_data(self, args): # 'args' must be dictionary type @@ -223,38 +152,30 @@ class CCSessionListener(Listener): def start(self): """ - start the cc chanel + Start stats module """ - # set initial value - self.stats_data['stats.boot_time'] = self.boot_time - self.stats_data['stats.start_time'] = get_datetime() - self.stats_data['stats.last_update_time'] = get_datetime() - self.stats_data['stats.lname'] = self.session.lname - self.cc_session.start() + self.running = True + # TODO: should be added into new logging interface + # if self.verbose: + # sys.stdout.write("[b10-stats] starting\n") + # request Bob to send statistics data logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) - cmd = isc.config.ccsession.create_command("getstats", None) - seq = self.session.group_sendmsg(cmd, 'Boss') - try: - answer, env = self.session.group_recvmsg(False, seq) - if answer: - rcode, arg = isc.config.ccsession.parse_answer(answer) - if rcode == 0: - self._update_stats_data(arg) - except isc.cc.session.SessionTimeout: - pass + cmd = isc.config.ccsession.create_command("sendstats", None) + seq = self.cc_session.group_sendmsg(cmd, 'Boss') + self.cc_session.group_recvmsg(True, seq) - def stop(self): - """ - stop the cc chanel - """ - return self.cc_session.close() + # initialized Statistics data + errors = self.update_statistics_data( + self.module_name, + lname=self.cc_session.lname, + boot_time=get_datetime(_BASETIME) + ) + if errors: + raise StatsError("stats spec file is incorrect") - def check(self): - """ - check the cc chanel - """ - return self.cc_session.check_command(False) + while self.running: + self.mccs.check_command(False) def config_handler(self, new_config): """ @@ -262,169 +183,200 @@ class CCSessionListener(Listener): """ logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_NEW_CONFIG, new_config) - # do nothing currently - return create_answer(0) + return isc.config.create_answer(0) - def command_handler(self, command, *args, **kwargs): + def command_handler(self, command, kwargs): """ handle commands from the cc channel """ - # add 'command_' suffix in order to executing command via bindctl name = 'command_' + command - - if name in self.events: - event = self.events[name] - return event(*args, **kwargs) + if name in self.callbacks: + callback = self.callbacks[name] + if kwargs: + return callback(**kwargs) + else: + return callback() else: - return self.command_unknown(command, args) + logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command) + return isc.config.create_answer(1, "Unknown command: '"+str(command)+"'") - def command_shutdown(self, args): + def update_modules(self): """ - handle shutdown command + update information of each module """ - logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND) - self.subject.running = False - return create_answer(0) + modules = {} + seq = self.cc_session.group_sendmsg( + isc.config.ccsession.create_command( + isc.config.ccsession.COMMAND_GET_STATISTICS_SPEC), + 'ConfigManager') + (answer, env) = self.cc_session.group_recvmsg(False, seq) + if answer: + (rcode, value) = isc.config.ccsession.parse_answer(answer) + if rcode == 0: + for mod in value: + spec = { "module_name" : mod, + "statistics" : [] } + if value[mod] and type(value[mod]) is list: + spec["statistics"] = value[mod] + modules[mod] = isc.config.module_spec.ModuleSpec(spec) + modules[self.module_name] = self.mccs.get_module_spec() + self.modules = modules - def command_set(self, args, stats_data={}): + def get_statistics_data(self, owner=None, name=None): """ - handle set command + return statistics data which stats module has of each module """ - self._update_stats_data(args) - return create_answer(0) + self.update_statistics_data() + if owner and name: + try: + return self.statistics_data[owner][name] + except KeyError: + pass + elif owner: + try: + return self.statistics_data[owner] + except KeyError: + pass + elif name: + pass + else: + return self.statistics_data - def command_remove(self, args, stats_item_name=''): + def update_statistics_data(self, owner=None, **data): """ - handle remove command + change statistics date of specified module into specified data """ + self.update_modules() + statistics_data = {} + for (name, module) in self.modules.items(): + value = parse_spec(module.get_statistics_spec()) + if module.validate_statistics(True, value): + statistics_data[name] = value + for (name, value) in self.statistics_data.items(): + if name in statistics_data: + statistics_data[name].update(value) + else: + statistics_data[name] = value + self.statistics_data = statistics_data + if owner and data: + errors = [] + try: + if self.modules[owner].validate_statistics(False, data, errors): + self.statistics_data[owner].update(data) + return + except KeyError: + errors.append('unknown module name') + return errors - # 'args' must be dictionary type - if args and args['stats_item_name'] in self.stats_data: - stats_item_name = args['stats_item_name'] - - logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_REMOVE_COMMAND, - stats_item_name) - - # just remove one item - self.stats_data.pop(stats_item_name) - - return create_answer(0) - - def command_show(self, args, stats_item_name=''): - """ - handle show command - """ - - # always overwrite 'report_time' and 'stats.timestamp' - # if "show" command invoked - self.stats_data['report_time'] = get_datetime() - self.stats_data['stats.timestamp'] = get_timestamp() - - # if with args - if args and args['stats_item_name'] in self.stats_data: - stats_item_name = args['stats_item_name'] - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_SHOW_NAME_COMMAND, - stats_item_name) - return create_answer(0, {stats_item_name: self.stats_data[stats_item_name]}) - - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_SHOW_ALL_COMMAND) - return create_answer(0, self.stats_data) - - def command_reset(self, args): - """ - handle reset command - """ - logger.debug(DBG_STATS_MESSAGING, - STATS_RECEIVED_RESET_COMMAND) - - # re-initialize internal variables - self.stats_data = self.initialize_data(self.stats_spec) - - # reset initial value - self.stats_data['stats.boot_time'] = self.boot_time - self.stats_data['stats.start_time'] = get_datetime() - self.stats_data['stats.last_update_time'] = get_datetime() - self.stats_data['stats.lname'] = self.session.lname - - return create_answer(0) - - def command_status(self, args): + def command_status(self): """ handle status command """ logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND) - # just return "I'm alive." - return create_answer(0, "I'm alive.") + return isc.config.create_answer( + 0, "Stats is up. (PID " + str(os.getpid()) + ")") - def command_unknown(self, command, args): + def command_shutdown(self): """ - handle an unknown command + handle shutdown command """ - logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command) - return create_answer(1, "Unknown command: '"+str(command)+"'") + logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND) + self.running = False + return isc.config.create_answer(0) + def command_show(self, owner=None, name=None): + """ + handle show command + """ + if (owner or name): + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOW_NAME_COMMAND, + str(owner)+", "+str(name)) + else: + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOW_ALL_COMMAND) + if owner and not name: + return isc.config.create_answer(1, "item name is not specified") + errors = self.update_statistics_data( + self.module_name, + timestamp=get_timestamp(), + report_time=get_datetime() + ) + if errors: raise StatsError("stats spec file is incorrect") + ret = self.get_statistics_data(owner, name) + if ret: + return isc.config.create_answer(0, ret) + else: + return isc.config.create_answer( + 1, "specified module name and/or item name are incorrect") - def initialize_data(self, spec): + def command_showschema(self, owner=None, name=None): """ - initialize stats data + handle show command """ - def __get_init_val(spec): - if spec['item_type'] == 'null': - return None - elif spec['item_type'] == 'boolean': - return bool(spec.get('item_default', False)) - elif spec['item_type'] == 'string': - return str(spec.get('item_default', '')) - elif spec['item_type'] in set(['number', 'integer']): - return int(spec.get('item_default', 0)) - elif spec['item_type'] in set(['float', 'double', 'real']): - return float(spec.get('item_default', 0.0)) - elif spec['item_type'] in set(['list', 'array']): - return spec.get('item_default', - [ __get_init_val(s) for s in spec['list_item_spec'] ]) - elif spec['item_type'] in set(['map', 'object']): - return spec.get('item_default', - dict([ (s['item_name'], __get_init_val(s)) for s in spec['map_item_spec'] ]) ) + # TODO: should be added into new logging interface + # if self.verbose: + # sys.stdout.write("[b10-stats] 'showschema' command received\n") + self.update_modules() + schema = {} + schema_byname = {} + for mod in self.modules: + spec = self.modules[mod].get_statistics_spec() + schema_byname[mod] = {} + if spec: + schema[mod] = spec + for item in spec: + schema_byname[mod][item['item_name']] = item + if owner: + try: + if name: + return isc.config.create_answer(0, schema_byname[owner][name]) + else: + return isc.config.create_answer(0, schema[owner]) + except KeyError: + pass + else: + if name: + return isc.config.create_answer(1, "module name is not specified") else: - return spec.get('item_default') - return dict([ (s['item_name'], __get_init_val(s)) for s in spec ]) + return isc.config.create_answer(0, schema) + return isc.config.create_answer( + 1, "specified module name and/or item name are incorrect") -def get_timestamp(): - """ - get current timestamp - """ - return time() + def command_set(self, owner, data): + """ + handle set command + """ + errors = self.update_statistics_data(owner, **data) + if errors: + return isc.config.create_answer( + 1, + "specified module name and/or statistics data are incorrect: " + + ", ".join(errors)) + errors = self.update_statistics_data( + self.module_name, last_update_time=get_datetime() ) + if errors: + raise StatsError("stats spec file is incorrect") + return isc.config.create_answer(0) -def get_datetime(): - """ - get current datetime - """ - return strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) - -def main(session=None): +if __name__ == "__main__": try: parser = OptionParser() - parser.add_option("-v", "--verbose", dest="verbose", action="store_true", - help="display more about what is going on") + parser.add_option( + "-v", "--verbose", dest="verbose", action="store_true", + help="display more about what is going on") (options, args) = parser.parse_args() if options.verbose: isc.log.init("b10-stats", "DEBUG", 99) - subject = SessionSubject(session=session) - listener = CCSessionListener(subject) - subject.start() - while subject.running: - subject.check() - subject.stop() - + stats = Stats() + stats.start() except OptionValueError as ove: logger.fatal(STATS_BAD_OPTION_VALUE, ove) except SessionError as se: logger.fatal(STATS_CC_SESSION_ERROR, se) + # TODO: should be added into new logging interface + except StatsError as se: + sys.exit("[b10-stats] %s" % se) except KeyboardInterrupt as kie: logger.info(STATS_STOPPED_BY_KEYBOARD) - -if __name__ == "__main__": - main() From 1a8c86ea2503bffe6dc1f2300dfc2b4efba108cc Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 20:08:22 +0900 Subject: [PATCH 891/974] [trac930] refurbish the unittests for new stats module, new stats httpd module and new mockups and utilities in test_utils.py --- src/bin/stats/tests/b10-stats-httpd_test.py | 593 ++++++---- src/bin/stats/tests/b10-stats_test.py | 1080 ++++++++----------- src/bin/stats/tests/test_utils.py | 37 +- 3 files changed, 862 insertions(+), 848 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 6d72dc2f38..ae07aa9f27 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -15,145 +15,259 @@ import unittest import os -import http.server -import string -import fake_select import imp -import sys -import fake_socket - -import isc.cc +import socket +import errno +import select +import string +import time +import threading +import http.client +import xml.etree.ElementTree +import isc import stats_httpd -stats_httpd.socket = fake_socket -stats_httpd.select = fake_select +import stats +from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC DUMMY_DATA = { - "auth.queries.tcp": 10000, - "auth.queries.udp": 12000, - "bind10.boot_time": "2011-03-04T11:59:05Z", - "report_time": "2011-03-04T11:59:19Z", - "stats.boot_time": "2011-03-04T11:59:06Z", - "stats.last_update_time": "2011-03-04T11:59:07Z", - "stats.lname": "4d70d40a_c@host", - "stats.start_time": "2011-03-04T11:59:06Z", - "stats.timestamp": 1299239959.560846 + 'Boss' : { + "boot_time": "2011-03-04T11:59:06Z" + }, + 'Auth' : { + "queries.tcp": 2, + "queries.udp": 3 + }, + 'Stats' : { + "report_time": "2011-03-04T11:59:19Z", + "boot_time": "2011-03-04T11:59:06Z", + "last_update_time": "2011-03-04T11:59:07Z", + "lname": "4d70d40a_c@host", + "timestamp": 1299239959.560846 + } } -def push_answer(stats_httpd): - stats_httpd.cc_session.group_sendmsg( - { 'result': - [ 0, DUMMY_DATA ] }, "Stats") - -def pull_query(stats_httpd): - (msg, env) = stats_httpd.cc_session.group_recvmsg() - if 'result' in msg: - (ret, arg) = isc.config.ccsession.parse_answer(msg) - else: - (ret, arg) = isc.config.ccsession.parse_command(msg) - return (ret, arg, env) - class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" def setUp(self): - self.stats_httpd = stats_httpd.StatsHttpd() - self.assertTrue(type(self.stats_httpd.httpd) is list) - self.httpd = self.stats_httpd.httpd + self.base = BaseModules() + self.stats_server = ThreadingServerManager(MyStats) + self.stats = self.stats_server.server + self.stats_server.run() + + def tearDown(self): + self.stats_server.shutdown() + self.base.shutdown() def test_do_GET(self): - for ht in self.httpd: - self._test_do_GET(ht._handler) - - def _test_do_GET(self, handler): + (address, port) = ('127.0.0.1', 65450) + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.stats_httpd = statshttpd_server.server + self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + self.assertTrue(type(self.stats_httpd.httpd) is list) + self.assertEqual(len(self.stats_httpd.httpd), 0) + statshttpd_server.run() + time.sleep(TIMEOUT_SEC*5) + client = http.client.HTTPConnection(address, port) + client._http_vsn_str = 'HTTP/1.0\n' + client.connect() # URL is '/bind10/statistics/xml' - handler.path = stats_httpd.XML_URL_PATH - push_answer(self.stats_httpd) - handler.do_GET() - (ret, arg, env) = pull_query(self.stats_httpd) - self.assertEqual(ret, "show") - self.assertIsNone(arg) - self.assertTrue('group' in env) - self.assertEqual(env['group'], 'Stats') - self.assertEqual(handler.response.code, 200) - self.assertEqual(handler.response.headers["Content-type"], "text/xml") - self.assertTrue(handler.response.headers["Content-Length"] > 0) - self.assertTrue(handler.response.wrote_headers) - self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) - self.assertTrue(handler.response.body.find(stats_httpd.XSD_URL_PATH)>0) - for (k, v) in DUMMY_DATA.items(): - self.assertTrue(handler.response.body.find(str(k))>0) - self.assertTrue(handler.response.body.find(str(v))>0) + client.putrequest('GET', stats_httpd.XML_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.getheader("Content-type"), "text/xml") + self.assertTrue(int(response.getheader("Content-Length")) > 0) + self.assertEqual(response.status, 200) + root = xml.etree.ElementTree.parse(response).getroot() + self.assertTrue(root.tag.find('stats_data') > 0) + for (k,v) in root.attrib.items(): + if k.find('schemaLocation') > 0: + self.assertEqual(v, stats_httpd.XSD_NAMESPACE + ' ' + stats_httpd.XSD_URL_PATH) + for mod in DUMMY_DATA: + for (item, value) in DUMMY_DATA[mod].items(): + self.assertIsNotNone(root.find(mod + '/' + item)) # URL is '/bind10/statitics/xsd' - handler.path = stats_httpd.XSD_URL_PATH - handler.do_GET() - self.assertEqual(handler.response.code, 200) - self.assertEqual(handler.response.headers["Content-type"], "text/xml") - self.assertTrue(handler.response.headers["Content-Length"] > 0) - self.assertTrue(handler.response.wrote_headers) - self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) - for (k, v) in DUMMY_DATA.items(): - self.assertTrue(handler.response.body.find(str(k))>0) + client.putrequest('GET', stats_httpd.XSD_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.getheader("Content-type"), "text/xml") + self.assertTrue(int(response.getheader("Content-Length")) > 0) + self.assertEqual(response.status, 200) + root = xml.etree.ElementTree.parse(response).getroot() + url_xmlschema = '{http://www.w3.org/2001/XMLSchema}' + tags = [ url_xmlschema + t for t in [ 'element', 'complexType', 'all', 'element' ] ] + xsdpath = '/'.join(tags) + self.assertTrue(root.tag.find('schema') > 0) + self.assertTrue(hasattr(root, 'attrib')) + self.assertTrue('targetNamespace' in root.attrib) + self.assertEqual(root.attrib['targetNamespace'], + stats_httpd.XSD_NAMESPACE) + for elm in root.findall(xsdpath): + self.assertIsNotNone(elm.attrib['name']) + self.assertTrue(elm.attrib['name'] in DUMMY_DATA) # URL is '/bind10/statitics/xsl' - handler.path = stats_httpd.XSL_URL_PATH - handler.do_GET() - self.assertEqual(handler.response.code, 200) - self.assertEqual(handler.response.headers["Content-type"], "text/xml") - self.assertTrue(handler.response.headers["Content-Length"] > 0) - self.assertTrue(handler.response.wrote_headers) - self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) - for (k, v) in DUMMY_DATA.items(): - self.assertTrue(handler.response.body.find(str(k))>0) + client.putrequest('GET', stats_httpd.XSL_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.getheader("Content-type"), "text/xml") + self.assertTrue(int(response.getheader("Content-Length")) > 0) + self.assertEqual(response.status, 200) + root = xml.etree.ElementTree.parse(response).getroot() + url_trans = '{http://www.w3.org/1999/XSL/Transform}' + url_xhtml = '{http://www.w3.org/1999/xhtml}' + xslpath = url_trans + 'template/' + url_xhtml + 'tr' + self.assertEqual(root.tag, url_trans + 'stylesheet') + for tr in root.findall(xslpath): + tds = tr.findall(url_xhtml + 'td') + self.assertIsNotNone(tds) + self.assertEqual(type(tds), list) + self.assertTrue(len(tds) > 2) + self.assertTrue(hasattr(tds[0], 'text')) + self.assertTrue(tds[0].text in DUMMY_DATA) + valueof = tds[2].find(url_trans + 'value-of') + self.assertIsNotNone(valueof) + self.assertTrue(hasattr(valueof, 'attrib')) + self.assertIsNotNone(valueof.attrib) + self.assertTrue('select' in valueof.attrib) + self.assertTrue(valueof.attrib['select'] in \ + [ tds[0].text+'/'+item for item in DUMMY_DATA[tds[0].text].keys() ]) # 302 redirect - handler.path = '/' - handler.headers = {'Host': 'my.host.domain'} - handler.do_GET() - self.assertEqual(handler.response.code, 302) - self.assertEqual(handler.response.headers["Location"], - "http://my.host.domain%s" % stats_httpd.XML_URL_PATH) + client._http_vsn_str = 'HTTP/1.1' + client.putrequest('GET', '/') + client.putheader('Host', address) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 302) + self.assertEqual(response.getheader('Location'), + "http://%s:%d%s" % (address, port, stats_httpd.XML_URL_PATH)) - # 404 NotFound - handler.path = '/path/to/foo/bar' - handler.headers = {} - handler.do_GET() - self.assertEqual(handler.response.code, 404) + # # 404 NotFound + client._http_vsn_str = 'HTTP/1.0' + client.putrequest('GET', '/path/to/foo/bar') + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 404) + client.close() + statshttpd_server.shutdown() + + def test_do_GET_failed1(self): # failure case(connection with Stats is down) - handler.path = stats_httpd.XML_URL_PATH - push_answer(self.stats_httpd) - self.assertFalse(self.stats_httpd.cc_session._socket._closed) - self.stats_httpd.cc_session._socket._closed = True - handler.do_GET() - self.stats_httpd.cc_session._socket._closed = False - self.assertEqual(handler.response.code, 500) - self.stats_httpd.cc_session._clear_queues() + (address, port) = ('127.0.0.1', 65451) + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + statshttpd = statshttpd_server.server + statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + statshttpd_server.run() + self.assertTrue(self.stats_server.server.running) + self.stats_server.shutdown() + time.sleep(TIMEOUT_SEC*2) + self.assertFalse(self.stats_server.server.running) + statshttpd.cc_session.set_timeout(milliseconds=TIMEOUT_SEC/1000) + client = http.client.HTTPConnection(address, port) + client.connect() - # failure case(Stats module returns err) - handler.path = stats_httpd.XML_URL_PATH - self.stats_httpd.cc_session.group_sendmsg( - { 'result': [ 1, "I have an error." ] }, "Stats") - self.assertFalse(self.stats_httpd.cc_session._socket._closed) - self.stats_httpd.cc_session._socket._closed = False - handler.do_GET() - self.assertEqual(handler.response.code, 500) - self.stats_httpd.cc_session._clear_queues() + # request XML + client.putrequest('GET', stats_httpd.XML_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + # request XSD + client.putrequest('GET', stats_httpd.XSD_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + # request XSL + client.putrequest('GET', stats_httpd.XSL_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + client.close() + statshttpd_server.shutdown() + + def test_do_GET_failed2(self): + # failure case(connection with Stats is down) + (address, port) = ('127.0.0.1', 65452) + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.stats_httpd = statshttpd_server.server + self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + statshttpd_server.run() + self.stats.mccs.set_command_handler( + lambda cmd, args: \ + isc.config.ccsession.create_answer(1, "I have an error.") + ) + time.sleep(TIMEOUT_SEC*5) + client = http.client.HTTPConnection(address, port) + client.connect() + + # request XML + client.putrequest('GET', stats_httpd.XML_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + # request XSD + client.putrequest('GET', stats_httpd.XSD_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + # request XSL + client.putrequest('GET', stats_httpd.XSL_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 500) + + client.close() + statshttpd_server.shutdown() def test_do_HEAD(self): - for ht in self.httpd: - self._test_do_HEAD(ht._handler) + (address, port) = ('127.0.0.1', 65453) + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.stats_httpd = statshttpd_server.server + self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + statshttpd_server.run() + time.sleep(TIMEOUT_SEC*5) + client = http.client.HTTPConnection(address, port) + client.connect() + client.putrequest('HEAD', stats_httpd.XML_URL_PATH) + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 200) - def _test_do_HEAD(self, handler): - handler.path = '/path/to/foo/bar' - handler.do_HEAD() - self.assertEqual(handler.response.code, 404) + client.putrequest('HEAD', '/path/to/foo/bar') + client.endheaders() + response = client.getresponse() + self.assertEqual(response.status, 404) + client.close() + statshttpd_server.shutdown() + + def test_log_message(self): + class MyHttpHandler(stats_httpd.HttpHandler): + def __init__(self): + class _Dummy_class_(): pass + self.address_string = lambda : 'dummyhost' + self.log_date_time_string = lambda : \ + 'DD/MM/YYYY HH:MI:SS' + self.server = _Dummy_class_() + self.server.log_writer = self.log_writer + def log_writer(self, line): + self.logged_line = line + self.handler = MyHttpHandler() + self.handler.log_message("%s %d", 'ABCDEFG', 12345) + self.assertEqual(self.handler.logged_line, + "[b10-stats-httpd] dummyhost - - " + + "[DD/MM/YYYY HH:MI:SS] ABCDEFG 12345\n") class TestHttpServerError(unittest.TestCase): """Tests for HttpServerError exception""" - def test_raises(self): try: raise stats_httpd.HttpServerError('Nothing') @@ -162,17 +276,16 @@ class TestHttpServerError(unittest.TestCase): class TestHttpServer(unittest.TestCase): """Tests for HttpServer class""" + def setUp(self): + self.base = BaseModules() + + def tearDown(self): + self.base.shutdown() def test_httpserver(self): - self.stats_httpd = stats_httpd.StatsHttpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(ht.server_address in self.stats_httpd.http_addrs) - self.assertEqual(ht.xml_handler, self.stats_httpd.xml_handler) - self.assertEqual(ht.xsd_handler, self.stats_httpd.xsd_handler) - self.assertEqual(ht.xsl_handler, self.stats_httpd.xsl_handler) - self.assertEqual(ht.log_writer, self.stats_httpd.write_log) - self.assertTrue(isinstance(ht._handler, stats_httpd.HttpHandler)) - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + statshttpd = stats_httpd.StatsHttpd() + self.assertEqual(type(statshttpd.httpd), list) + self.assertEqual(len(statshttpd.httpd), 0) class TestStatsHttpdError(unittest.TestCase): """Tests for StatsHttpdError exception""" @@ -187,130 +300,176 @@ class TestStatsHttpd(unittest.TestCase): """Tests for StatsHttpd class""" def setUp(self): - fake_socket._CLOSED = False - fake_socket.has_ipv6 = True + self.base = BaseModules() + self.stats_server = ThreadingServerManager(MyStats) + self.stats = self.stats_server.server + self.stats_server.run() self.stats_httpd = stats_httpd.StatsHttpd() + # checking IPv6 enabled on this platform + self.ipv6_enabled = True + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.bind(("::1",8000)) + sock.close() + except socket.error: + self.ipv6_enabled = False + def tearDown(self): self.stats_httpd.stop() + self.stats_server.shutdown() + self.base.shutdown() def test_init(self): - self.assertFalse(self.stats_httpd.mccs.get_socket()._closed) - self.assertEqual(self.stats_httpd.mccs.get_socket().fileno(), - id(self.stats_httpd.mccs.get_socket())) - for ht in self.stats_httpd.httpd: - self.assertFalse(ht.socket._closed) - self.assertEqual(ht.socket.fileno(), id(ht.socket)) - fake_socket._CLOSED = True - self.assertRaises(isc.cc.session.SessionError, - stats_httpd.StatsHttpd) - fake_socket._CLOSED = False + self.assertEqual(self.stats_httpd.running, False) + self.assertEqual(self.stats_httpd.poll_intval, 0.5) + self.assertEqual(self.stats_httpd.httpd, []) + self.assertEqual(type(self.stats_httpd.mccs), isc.config.ModuleCCSession) + self.assertEqual(type(self.stats_httpd.cc_session), isc.cc.Session) + self.assertEqual(len(self.stats_httpd.config), 2) + self.assertTrue('listen_on' in self.stats_httpd.config) + self.assertEqual(len(self.stats_httpd.config['listen_on']), 1) + self.assertTrue('address' in self.stats_httpd.config['listen_on'][0]) + self.assertTrue('port' in self.stats_httpd.config['listen_on'][0]) + self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + + def test_openclose_mccs(self): + statshttpd = stats_httpd.StatsHttpd() + statshttpd.close_mccs() + self.assertEqual(statshttpd.mccs, None) + statshttpd.open_mccs() + self.assertIsNotNone(statshttpd.mccs) + statshttpd.mccs = None + self.assertEqual(statshttpd.mccs, None) + self.assertEqual(statshttpd.close_mccs(), None) def test_mccs(self): - self.stats_httpd.open_mccs() + self.assertIsNotNone(self.stats_httpd.mccs.get_socket()) self.assertTrue( - isinstance(self.stats_httpd.mccs.get_socket(), fake_socket.socket)) + isinstance(self.stats_httpd.mccs.get_socket(), socket.socket)) self.assertTrue( isinstance(self.stats_httpd.cc_session, isc.cc.session.Session)) - self.assertTrue( - isinstance(self.stats_httpd.stats_module_spec, isc.config.ModuleSpec)) - for cfg in self.stats_httpd.stats_config_spec: - self.assertTrue('item_name' in cfg) - self.assertTrue(cfg['item_name'] in DUMMY_DATA) - self.assertTrue(len(self.stats_httpd.stats_config_spec), len(DUMMY_DATA)) - - def test_load_config(self): - self.stats_httpd.load_config() - self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + self.statistics_spec = self.stats_httpd.get_stats_spec() + for mod in DUMMY_DATA: + self.assertTrue(mod in self.statistics_spec) + for cfg in self.statistics_spec[mod]: + self.assertTrue('item_name' in cfg) + self.assertTrue(cfg['item_name'] in DUMMY_DATA[mod]) + self.assertTrue(len(self.statistics_spec[mod]), len(DUMMY_DATA[mod])) + self.stats_httpd.close_mccs() + self.assertIsNone(self.stats_httpd.mccs) def test_httpd(self): # dual stack (addresses is ipv4 and ipv6) - fake_socket.has_ipv6 = True - self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) - self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ] - self.assertTrue( - stats_httpd.HttpServer.address_family in set([fake_socket.AF_INET, fake_socket.AF_INET6])) - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) - self.stats_httpd.close_httpd() + if self.ipv6_enabled: + self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ] + self.assertTrue( + stats_httpd.HttpServer.address_family in set([socket.AF_INET, socket.AF_INET6])) + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.close_httpd() # dual stack (address is ipv6) - fake_socket.has_ipv6 = True - self.stats_httpd.http_addrs = [ ('::1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) - self.stats_httpd.close_httpd() - + if self.ipv6_enabled: + self.stats_httpd.http_addrs = [ ('::1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.close_httpd() + # dual stack (address is ipv4) - fake_socket.has_ipv6 = True - self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) - self.stats_httpd.close_httpd() + if self.ipv6_enabled: + self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.close_httpd() # only-ipv4 single stack - fake_socket.has_ipv6 = False - self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) - self.stats_httpd.close_httpd() - + if not self.ipv6_enabled: + self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.close_httpd() + # only-ipv4 single stack (force set ipv6 ) - fake_socket.has_ipv6 = False - self.stats_httpd.http_addrs = [ ('::1', 8000) ] - self.assertRaises(stats_httpd.HttpServerError, - self.stats_httpd.open_httpd) - + if not self.ipv6_enabled: + self.stats_httpd.http_addrs = [ ('::1', 8000) ] + self.assertRaises(stats_httpd.HttpServerError, + self.stats_httpd.open_httpd) + # hostname self.stats_httpd.http_addrs = [ ('localhost', 8000) ] self.stats_httpd.open_httpd() for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.close_httpd() - + self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ] - self.stats_httpd.open_httpd() - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + self.assertEqual(type(self.stats_httpd.httpd), list) + self.assertEqual(len(self.stats_httpd.httpd), 0) self.stats_httpd.close_httpd() # over flow of port number self.stats_httpd.http_addrs = [ ('', 80000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + # negative self.stats_httpd.http_addrs = [ ('', -8000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + # alphabet self.stats_httpd.http_addrs = [ ('', 'ABCDE') ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - def test_start(self): - self.stats_httpd.cc_session.group_sendmsg( - { 'command': [ "shutdown" ] }, "StatsHttpd") - self.stats_httpd.start() - self.stats_httpd = stats_httpd.StatsHttpd() - self.assertRaises( - fake_select.error, self.stats_httpd.start) + # Address already in use + self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.statshttpd_server.server.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) + self.statshttpd_server.run() + time.sleep(TIMEOUT_SEC) + self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) + self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + self.statshttpd_server.shutdown() - def test_stop(self): - # success case - fake_socket._CLOSED = False - self.stats_httpd.stop() + def test_running(self): self.assertFalse(self.stats_httpd.running) - self.assertIsNone(self.stats_httpd.mccs) - for ht in self.stats_httpd.httpd: - self.assertTrue(ht.socket._closed) - self.assertTrue(self.stats_httpd.cc_session._socket._closed) + self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) + self.stats_httpd = self.statshttpd_server.server + self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65455 }]}) + self.statshttpd_server.run() + time.sleep(TIMEOUT_SEC*2) + self.assertTrue(self.stats_httpd.running) + self.statshttpd_server.shutdown() + self.assertFalse(self.stats_httpd.running) + # failure case - self.stats_httpd.cc_session._socket._closed = False - self.stats_httpd.open_mccs() - self.stats_httpd.cc_session._socket._closed = True - self.stats_httpd.stop() # No excetion raises - self.stats_httpd.cc_session._socket._closed = False + self.stats_httpd = stats_httpd.StatsHttpd() + self.stats_httpd.cc_session.close() + self.assertRaises( + isc.cc.session.SessionError, self.stats_httpd.start) + + def test_select_failure(self): + def raise_select_except(*args): + raise select.error('dummy error') + def raise_select_except_with_errno(*args): + raise select.error(errno.EINTR) + (address, port) = ('127.0.0.1', 65456) + stats_httpd.select.select = raise_select_except + statshttpd = stats_httpd.StatsHttpd() + statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + self.assertRaises(select.error, statshttpd.start) + statshttpd.stop() + stats_httpd.select.select = raise_select_except_with_errno + statshttpd_server = ThreadingServerManager(MyStatsHttpd) + statshttpd = statshttpd_server.server + statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) + statshttpd_server.run() + time.sleep(TIMEOUT_SEC*2) + statshttpd_server.shutdown() def test_open_template(self): # successful conditions @@ -363,38 +522,40 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual( self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)), isc.config.ccsession.create_answer( - 1, "Unknown known config: _UNKNOWN_KEY_")) + 1, "Unknown known config: _UNKNOWN_KEY_")) + self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="::2",port=8000)])), + dict(listen_on=[dict(address="127.0.0.2",port=8000)])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "::2") + self.assertTrue(addr["address"] == "127.0.0.2") self.assertTrue(addr["port"] == 8000) - self.assertEqual( - self.stats_httpd.config_handler( - dict(listen_on=[dict(address="::1",port=80)])), - isc.config.ccsession.create_answer(0)) - self.assertTrue("listen_on" in self.stats_httpd.config) - for addr in self.stats_httpd.config["listen_on"]: - self.assertTrue("address" in addr) - self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "::1") - self.assertTrue(addr["port"] == 80) + if self.ipv6_enabled: + self.assertEqual( + self.stats_httpd.config_handler( + dict(listen_on=[dict(address="::1",port=8000)])), + isc.config.ccsession.create_answer(0)) + self.assertTrue("listen_on" in self.stats_httpd.config) + for addr in self.stats_httpd.config["listen_on"]: + self.assertTrue("address" in addr) + self.assertTrue("port" in addr) + self.assertTrue(addr["address"] == "::1") + self.assertTrue(addr["port"] == 8000) self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="1.2.3.4",port=54321)])), + dict(listen_on=[dict(address="127.0.0.1",port=54321)])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "1.2.3.4") + self.assertTrue(addr["address"] == "127.0.0.1") self.assertTrue(addr["port"] == 54321) (ret, arg) = isc.config.ccsession.parse_answer( self.stats_httpd.config_handler( @@ -500,8 +661,6 @@ class TestStatsHttpd(unittest.TestCase): imp.reload(stats_httpd) os.environ["B10_FROM_SOURCE"] = tmppath imp.reload(stats_httpd) - stats_httpd.socket = fake_socket - stats_httpd.select = fake_select if __name__ == "__main__": unittest.main() diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 2fb4ab5e50..b013c7a8bc 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -13,633 +13,496 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -# Tests for the stats module -# -import os -import sys -import time import unittest +import os +import threading +import io +import time import imp -from isc.cc.session import Session, SessionError -from isc.config.ccsession import ModuleCCSession, ModuleCCSessionError -from fake_time import time, strftime, gmtime -import stats -stats.time = time -stats.strftime = strftime -stats.gmtime = gmtime -from stats import SessionSubject, CCSessionListener, get_timestamp, get_datetime -from fake_time import _TEST_TIME_SECS, _TEST_TIME_STRF -if "B10_FROM_SOURCE" in os.environ: - TEST_SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] +\ - "/src/bin/stats/tests/testdata/stats_test.spec" -else: - TEST_SPECFILE_LOCATION = "./testdata/stats_test.spec" +import stats +import isc.cc.session +from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, TIMEOUT_SEC + +class TestUtilties(unittest.TestCase): + items = [ + { 'item_name': 'test_int1', 'item_type': 'integer', 'item_default': 12345 }, + { 'item_name': 'test_real1', 'item_type': 'real', 'item_default': 12345.6789 }, + { 'item_name': 'test_bool1', 'item_type': 'boolean', 'item_default': True }, + { 'item_name': 'test_str1', 'item_type': 'string', 'item_default': 'ABCD' }, + { 'item_name': 'test_list1', 'item_type': 'list', 'item_default': [1,2,3], + 'list_item_spec' : [ { 'item_name': 'one', 'item_type': 'integer' }, + { 'item_name': 'two', 'item_type': 'integer' }, + { 'item_name': 'three', 'item_type': 'integer' } ] }, + { 'item_name': 'test_map1', 'item_type': 'map', 'item_default': {'a':1,'b':2,'c':3}, + 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'integer'}, + { 'item_name': 'b', 'item_type': 'integer'}, + { 'item_name': 'c', 'item_type': 'integer'} ] }, + { 'item_name': 'test_int2', 'item_type': 'integer' }, + { 'item_name': 'test_real2', 'item_type': 'real' }, + { 'item_name': 'test_bool2', 'item_type': 'boolean' }, + { 'item_name': 'test_str2', 'item_type': 'string' }, + { 'item_name': 'test_list2', 'item_type': 'list', + 'list_item_spec' : [ { 'item_name': 'one', 'item_type': 'integer' }, + { 'item_name': 'two', 'item_type': 'integer' }, + { 'item_name': 'three', 'item_type': 'integer' } ] }, + { 'item_name': 'test_map2', 'item_type': 'map', + 'map_item_spec' : [ { 'item_name': 'A', 'item_type': 'integer'}, + { 'item_name': 'B', 'item_type': 'integer'}, + { 'item_name': 'C', 'item_type': 'integer'} ] }, + { 'item_name': 'test_none', 'item_type': 'none' } + ] + + def test_parse_spec(self): + self.assertEqual( + stats.parse_spec(self.items), { + 'test_int1' : 12345 , + 'test_real1' : 12345.6789 , + 'test_bool1' : True , + 'test_str1' : 'ABCD' , + 'test_list1' : [1,2,3] , + 'test_map1' : {'a':1,'b':2,'c':3}, + 'test_int2' : 0 , + 'test_real2' : 0.0, + 'test_bool2' : False, + 'test_str2' : "", + 'test_list2' : [0,0,0], + 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 }, + 'test_none' : None }) + self.assertRaises(TypeError, stats.parse_spec, None) + self.assertRaises(KeyError, stats.parse_spec, [{'item_name':'Foo'}]) + + def test_get_timestamp(self): + self.assertEqual(stats.get_timestamp(), 1308730448.965706) + + def test_get_datetime(self): + stats.time = lambda : 1308730448.965706 + stats.gmtime = lambda : (2011, 6, 22, 8, 14, 8, 2, 173, 0) + self.assertEqual(stats.get_datetime(), '2011-06-22T08:14:08Z') + self.assertNotEqual(stats.get_datetime( + (2011, 6, 22, 8, 23, 40, 2, 173, 0)), '2011-06-22T08:14:08Z') + +class TestCallback(unittest.TestCase): + def setUp(self): + self.dummy_func = lambda *x, **y : (x, y) + self.dummy_args = (1,2,3) + self.dummy_kwargs = {'a':1,'b':2,'c':3} + self.cback1 = stats.Callback( + command=self.dummy_func, + args=self.dummy_args, + kwargs=self.dummy_kwargs + ) + self.cback2 = stats.Callback( + args=self.dummy_args, + kwargs=self.dummy_kwargs + ) + self.cback3 = stats.Callback( + command=self.dummy_func, + kwargs=self.dummy_kwargs + ) + self.cback4 = stats.Callback( + command=self.dummy_func, + args=self.dummy_args + ) + + def tearDown(self): + pass + + def test_init(self): + self.assertEqual((self.cback1.command, self.cback1.args, self.cback1.kwargs), + (self.dummy_func, self.dummy_args, self.dummy_kwargs)) + self.assertEqual((self.cback2.command, self.cback2.args, self.cback2.kwargs), + (None, self.dummy_args, self.dummy_kwargs)) + self.assertEqual((self.cback3.command, self.cback3.args, self.cback3.kwargs), + (self.dummy_func, (), self.dummy_kwargs)) + self.assertEqual((self.cback4.command, self.cback4.args, self.cback4.kwargs), + (self.dummy_func, self.dummy_args, {})) + + def test_call(self): + self.assertEqual(self.cback1(), (self.dummy_args, self.dummy_kwargs)) + self.assertEqual(self.cback1(100, 200), ((100, 200), self.dummy_kwargs)) + self.assertEqual(self.cback1(a=100, b=200), (self.dummy_args, {'a':100, 'b':200})) + self.assertEqual(self.cback2(), None) + self.assertEqual(self.cback3(), ((), self.dummy_kwargs)) + self.assertEqual(self.cback3(100, 200), ((100, 200), self.dummy_kwargs)) + self.assertEqual(self.cback3(a=100, b=200), ((), {'a':100, 'b':200})) + self.assertEqual(self.cback4(), (self.dummy_args, {})) + self.assertEqual(self.cback4(100, 200), ((100, 200), {})) + self.assertEqual(self.cback4(a=100, b=200), (self.dummy_args, {'a':100, 'b':200})) class TestStats(unittest.TestCase): - def setUp(self): - self.session = Session() - self.subject = SessionSubject(session=self.session) - self.listener = CCSessionListener(self.subject) - self.stats_spec = self.listener.cc_session.get_module_spec().get_config_spec() - self.module_name = self.listener.cc_session.get_module_spec().get_module_name() - self.stats_data = { - 'report_time' : get_datetime(), - 'bind10.boot_time' : "1970-01-01T00:00:00Z", - 'stats.timestamp' : get_timestamp(), - 'stats.lname' : self.session.lname, - 'auth.queries.tcp': 0, - 'auth.queries.udp': 0, - "stats.boot_time": get_datetime(), - "stats.start_time": get_datetime(), - "stats.last_update_time": get_datetime() - } - # check starting - self.assertFalse(self.subject.running) - self.subject.start() - self.assertEqual(len(self.session.old_message_queue), 1) - self.assertTrue(self.subject.running) - self.assertEqual(len(self.session.message_queue), 0) - self.assertEqual(self.module_name, 'Stats') + self.base = BaseModules() + self.stats = stats.Stats() + self.assertTrue("B10_FROM_SOURCE" in os.environ) + self.assertEqual(stats.SPECFILE_LOCATION, \ + os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + \ + os.sep + "stats.spec") def tearDown(self): - # check closing - self.subject.stop() - self.assertFalse(self.subject.running) - self.subject.detach(self.listener) - self.listener.stop() - self.session.close() + self.base.shutdown() - def test_local_func(self): - """ - Test for local function - - """ - # test for result_ok - self.assertEqual(type(result_ok()), dict) - self.assertEqual(result_ok(), {'result': [0]}) - self.assertEqual(result_ok(1), {'result': [1]}) - self.assertEqual(result_ok(0,'OK'), {'result': [0, 'OK']}) - self.assertEqual(result_ok(1,'Not good'), {'result': [1, 'Not good']}) - self.assertEqual(result_ok(None,"It's None"), {'result': [None, "It's None"]}) - self.assertNotEqual(result_ok(), {'RESULT': [0]}) + def test_init(self): + self.assertEqual(self.stats.module_name, 'Stats') + self.assertFalse(self.stats.running) + self.assertTrue('command_show' in self.stats.callbacks) + self.assertTrue('command_status' in self.stats.callbacks) + self.assertTrue('command_shutdown' in self.stats.callbacks) + self.assertTrue('command_show' in self.stats.callbacks) + self.assertTrue('command_showschema' in self.stats.callbacks) + self.assertTrue('command_set' in self.stats.callbacks) - # test for get_timestamp - self.assertEqual(get_timestamp(), _TEST_TIME_SECS) + def test_init_undefcmd(self): + spec_str = """\ +{ + "module_spec": { + "module_name": "Stats", + "module_description": "Stats daemon", + "config_data": [], + "commands": [ + { + "command_name": "_undef_command_", + "command_description": "a undefined command in stats", + "command_args": [] + } + ], + "statistics": [] + } +} +""" + orig_spec_location = stats.SPECFILE_LOCATION + stats.SPECFILE_LOCATION = io.StringIO(spec_str) + self.assertRaises(stats.StatsError, stats.Stats) + stats.SPECFILE_LOCATION = orig_spec_location - # test for get_datetime - self.assertEqual(get_datetime(), _TEST_TIME_STRF) + def test_start(self): + statsserver = ThreadingServerManager(MyStats) + stats = statsserver.server + self.assertFalse(stats.running) + statsserver.run() + time.sleep(TIMEOUT_SEC) + self.assertTrue(stats.running) + statsserver.shutdown() + self.assertFalse(stats.running) - def test_show_command(self): - """ - Test for show command - - """ - # test show command without arg - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - # ignore under 0.9 seconds - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) + def test_start_with_err(self): + statsd = stats.Stats() + statsd.update_statistics_data = lambda x,**y: [1] + self.assertRaises(stats.StatsError, statsd.start) - # test show command with arg - self.session.group_sendmsg({"command": [ "show", {"stats_item_name": "stats.lname"}]}, "Stats") - self.assertEqual(len(self.subject.session.message_queue), 1) - self.subject.check() - result_data = self.subject.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'stats.lname': self.stats_data['stats.lname']}), - result_data) - self.assertEqual(len(self.subject.session.message_queue), 0) + def test_config_handler(self): + self.assertEqual(self.stats.config_handler({'foo':'bar'}), + isc.config.create_answer(0)) - # test show command with arg which has wrong name - self.session.group_sendmsg({"command": [ "show", {"stats_item_name": "stats.dummy"}]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - # ignore under 0.9 seconds - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_set_command(self): - """ - Test for set command - - """ - # test set command - self.stats_data['auth.queries.udp'] = 54321 - self.assertEqual(self.stats_data['auth.queries.udp'], 54321) - self.assertEqual(self.stats_data['auth.queries.tcp'], 0) - self.session.group_sendmsg({ "command": [ - "set", { - 'stats_data': {'auth.queries.udp': 54321 } - } ] }, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 2 - self.stats_data['auth.queries.udp'] = 0 - self.assertEqual(self.stats_data['auth.queries.udp'], 0) - self.assertEqual(self.stats_data['auth.queries.tcp'], 0) - self.session.group_sendmsg({ "command": [ "set", {'stats_data': {'auth.queries.udp': 0}} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command 2 - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 3 - self.stats_data['auth.queries.tcp'] = 54322 - self.assertEqual(self.stats_data['auth.queries.udp'], 0) - self.assertEqual(self.stats_data['auth.queries.tcp'], 54322) - self.session.group_sendmsg({ "command": [ - "set", { - 'stats_data': {'auth.queries.tcp': 54322 } - } ] }, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command 3 - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_remove_command(self): - """ - Test for remove command - - """ - self.session.group_sendmsg({"command": - [ "remove", {"stats_item_name": 'bind10.boot_time' }]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - self.assertEqual(self.stats_data.pop('bind10.boot_time'), "1970-01-01T00:00:00Z") - self.assertFalse('bind10.boot_time' in self.stats_data) - - # test show command with arg - self.session.group_sendmsg({"command": - [ "show", {"stats_item_name": 'bind10.boot_time'}]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertFalse('bind10.boot_time' in result_data['result'][1]) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_reset_command(self): - """ - Test for reset command - - """ - self.session.group_sendmsg({"command": [ "reset" ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command - self.session.group_sendmsg({"command": [ "show" ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_status_command(self): - """ - Test for status command - - """ - self.session.group_sendmsg({"command": [ "status" ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(0, "I'm alive."), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - def test_unknown_command(self): - """ - Test for unknown command - - """ - self.session.group_sendmsg({"command": [ "hoge", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(1, "Unknown command: 'hoge'"), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - def test_shutdown_command(self): - """ - Test for shutdown command - - """ - self.session.group_sendmsg({"command": [ "shutdown", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.assertTrue(self.subject.running) - self.subject.check() - self.assertFalse(self.subject.running) - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - - def test_some_commands(self): - """ - Test for some commands in a row - - """ - # test set command - self.stats_data['bind10.boot_time'] = '2010-08-02T14:47:56Z' - self.assertEqual(self.stats_data['bind10.boot_time'], '2010-08-02T14:47:56Z') - self.session.group_sendmsg({ "command": [ - "set", { - 'stats_data': {'bind10.boot_time': '2010-08-02T14:47:56Z' } - }]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ - "show", { 'stats_item_name': 'bind10.boot_time' } - ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'bind10.boot_time': '2010-08-02T14:47:56Z'}), - result_data) - self.assertEqual(result_ok(0, {'bind10.boot_time': self.stats_data['bind10.boot_time']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 2nd - self.stats_data['auth.queries.udp'] = 98765 - self.assertEqual(self.stats_data['auth.queries.udp'], 98765) - self.session.group_sendmsg({ "command": [ - "set", { 'stats_data': { - 'auth.queries.udp': - self.stats_data['auth.queries.udp'] - } } - ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({"command": [ - "show", {'stats_item_name': 'auth.queries.udp'} - ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'auth.queries.udp': 98765}), - result_data) - self.assertEqual(result_ok(0, {'auth.queries.udp': self.stats_data['auth.queries.udp']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 3 - self.stats_data['auth.queries.tcp'] = 4321 - self.session.group_sendmsg({"command": [ - "set", - {'stats_data': {'auth.queries.tcp': 4321 }} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check value - self.session.group_sendmsg({"command": [ "show", {'stats_item_name': 'auth.queries.tcp'} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'auth.queries.tcp': 4321}), - result_data) - self.assertEqual(result_ok(0, {'auth.queries.tcp': self.stats_data['auth.queries.tcp']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - self.session.group_sendmsg({"command": [ "show", {'stats_item_name': 'auth.queries.udp'} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'auth.queries.udp': 98765}), - result_data) - self.assertEqual(result_ok(0, {'auth.queries.udp': self.stats_data['auth.queries.udp']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set command 4 - self.stats_data['auth.queries.tcp'] = 67890 - self.session.group_sendmsg({"command": [ - "set", {'stats_data': {'auth.queries.tcp': 67890 }} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # test show command for all values - self.session.group_sendmsg({"command": [ "show", None ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, self.stats_data), result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_some_commands2(self): - """ - Test for some commands in a row using list-type value - - """ - self.stats_data['listtype'] = [1, 2, 3] - self.assertEqual(self.stats_data['listtype'], [1, 2, 3]) - self.session.group_sendmsg({ "command": [ - "set", {'stats_data': {'listtype': [1, 2, 3] }} - ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ - "show", { 'stats_item_name': 'listtype'} - ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'listtype': [1, 2, 3]}), - result_data) - self.assertEqual(result_ok(0, {'listtype': self.stats_data['listtype']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set list-type value - self.assertEqual(self.stats_data['listtype'], [1, 2, 3]) - self.session.group_sendmsg({"command": [ - "set", {'stats_data': {'listtype': [3, 2, 1, 0] }} - ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ - "show", { 'stats_item_name': 'listtype' } - ] }, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'listtype': [3, 2, 1, 0]}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_some_commands3(self): - """ - Test for some commands in a row using dictionary-type value - - """ - self.stats_data['dicttype'] = {"a": 1, "b": 2, "c": 3} - self.assertEqual(self.stats_data['dicttype'], {"a": 1, "b": 2, "c": 3}) - self.session.group_sendmsg({ "command": [ - "set", { - 'stats_data': {'dicttype': {"a": 1, "b": 2, "c": 3} } - }]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ "show", { 'stats_item_name': 'dicttype' } ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'dicttype': {"a": 1, "b": 2, "c": 3}}), - result_data) - self.assertEqual(result_ok(0, {'dicttype': self.stats_data['dicttype']}), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - # test set list-type value - self.assertEqual(self.stats_data['dicttype'], {"a": 1, "b": 2, "c": 3}) - self.session.group_sendmsg({"command": [ - "set", {'stats_data': {'dicttype': {"a": 3, "b": 2, "c": 1, "d": 0} }} ]}, - "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - self.assertEqual(len(self.session.message_queue), 0) - - # check its value - self.session.group_sendmsg({ "command": [ "show", { 'stats_item_name': 'dicttype' }]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - result_data = self.session.get_message("Stats", None) - self.assertEqual(result_ok(0, {'dicttype': {"a": 3, "b": 2, "c": 1, "d": 0} }), - result_data) - self.assertEqual(len(self.session.message_queue), 0) - - def test_config_update(self): - """ - Test for config update - - """ - # test show command without arg - self.session.group_sendmsg({"command": [ "config_update", {"x-version":999} ]}, "Stats") - self.assertEqual(len(self.session.message_queue), 1) - self.subject.check() - self.assertEqual(result_ok(), - self.session.get_message("Stats", None)) - - def test_for_boss(self): - last_queue = self.session.old_message_queue.pop() + def test_command_handler(self): + statsserver = ThreadingServerManager(MyStats) + statsserver.run() + time.sleep(TIMEOUT_SEC*4) + self.base.boss.server._started.wait() self.assertEqual( - last_queue.msg, {'command': ['getstats']}) + send_command( + 'show', 'Stats', + params={ 'owner' : 'Boss', + 'name' : 'boot_time' }), + (0, '2011-06-22T08:14:08Z')) self.assertEqual( - last_queue.env['group'], 'Boss') + send_command( + 'set', 'Stats', + params={ 'owner' : 'Boss', + 'data' : { 'boot_time' : '2012-06-22T18:24:08Z' } }), + (0, None)) + self.assertEqual( + send_command( + 'show', 'Stats', + params={ 'owner' : 'Boss', + 'name' : 'boot_time' }), + (0, '2012-06-22T18:24:08Z')) + self.assertEqual( + send_command('status', 'Stats'), + (0, "Stats is up. (PID " + str(os.getpid()) + ")")) -class TestStats2(unittest.TestCase): - - def setUp(self): - self.session = Session() - self.subject = SessionSubject(session=self.session) - self.listener = CCSessionListener(self.subject) - self.module_name = self.listener.cc_session.get_module_spec().get_module_name() - # check starting - self.assertFalse(self.subject.running) - self.subject.start() - self.assertTrue(self.subject.running) - self.assertEqual(len(self.session.message_queue), 0) - self.assertEqual(self.module_name, 'Stats') - - def tearDown(self): - # check closing - self.subject.stop() - self.assertFalse(self.subject.running) - self.subject.detach(self.listener) - self.listener.stop() - - def test_specfile(self): - """ - Test for specfile + (rcode, value) = send_command('show', 'Stats') + self.assertEqual(rcode, 0) + self.assertEqual(len(value), 3) + self.assertTrue('Boss' in value) + self.assertTrue('Stats' in value) + self.assertTrue('Auth' in value) + self.assertEqual(len(value['Stats']), 5) + self.assertEqual(len(value['Boss']), 1) + self.assertTrue('boot_time' in value['Boss']) + self.assertEqual(value['Boss']['boot_time'], '2012-06-22T18:24:08Z') + self.assertTrue('report_time' in value['Stats']) + self.assertTrue('boot_time' in value['Stats']) + self.assertTrue('last_update_time' in value['Stats']) + self.assertTrue('timestamp' in value['Stats']) + self.assertTrue('lname' in value['Stats']) + (rcode, value) = send_command('showschema', 'Stats') + self.assertEqual(rcode, 0) + self.assertEqual(len(value), 3) + self.assertTrue('Boss' in value) + self.assertTrue('Stats' in value) + self.assertTrue('Auth' in value) + self.assertEqual(len(value['Stats']), 5) + self.assertEqual(len(value['Boss']), 1) + for item in value['Boss']: + self.assertTrue(len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + self.assertTrue('item_format' in item) + for item in value['Stats']: + self.assertTrue(len(item) == 6 or len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + if len(item) == 7: + self.assertTrue('item_format' in item) - """ - if "B10_FROM_SOURCE" in os.environ: - self.assertEqual(stats.SPECFILE_LOCATION, - os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + \ - os.sep + "stats.spec") - self.assertEqual(stats.SCHEMA_SPECFILE_LOCATION, - os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + \ - os.sep + "stats-schema.spec") - imp.reload(stats) - # change path of SPECFILE_LOCATION - stats.SPECFILE_LOCATION = TEST_SPECFILE_LOCATION - stats.SCHEMA_SPECFILE_LOCATION = TEST_SPECFILE_LOCATION - self.assertEqual(stats.SPECFILE_LOCATION, TEST_SPECFILE_LOCATION) - self.subject = stats.SessionSubject(session=self.session) - self.session = self.subject.session - self.listener = stats.CCSessionListener(self.subject) + self.assertEqual( + send_command('__UNKNOWN__', 'Stats'), + (1, "Unknown command: '__UNKNOWN__'")) - self.assertEqual(self.listener.stats_spec, []) - self.assertEqual(self.listener.stats_data, {}) + statsserver.shutdown() - self.assertEqual(self.listener.commands_spec, [ - { - "command_name": "status", - "command_description": "identify whether stats module is alive or not", - "command_args": [] - }, - { - "command_name": "the_dummy", - "command_description": "this is for testing", - "command_args": [] - }]) + def test_update_modules(self): + self.assertEqual(len(self.stats.modules), 0) + self.stats.update_modules() + self.assertTrue('Stats' in self.stats.modules) + self.assertTrue('Boss' in self.stats.modules) + self.assertFalse('Dummy' in self.stats.modules) + my_statistics_data = stats.parse_spec(self.stats.modules['Stats'].get_statistics_spec()) + self.assertTrue('report_time' in my_statistics_data) + self.assertTrue('boot_time' in my_statistics_data) + self.assertTrue('last_update_time' in my_statistics_data) + self.assertTrue('timestamp' in my_statistics_data) + self.assertTrue('lname' in my_statistics_data) + self.assertEqual(my_statistics_data['report_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['last_update_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['timestamp'], 0.0) + self.assertEqual(my_statistics_data['lname'], "") + my_statistics_data = stats.parse_spec(self.stats.modules['Boss'].get_statistics_spec()) + self.assertTrue('boot_time' in my_statistics_data) + self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") - def test_func_initialize_data(self): - """ - Test for initialize_data function + def test_get_statistics_data(self): + my_statistics_data = self.stats.get_statistics_data() + self.assertTrue('Stats' in my_statistics_data) + self.assertTrue('Boss' in my_statistics_data) + my_statistics_data = self.stats.get_statistics_data(owner='Stats') + self.assertTrue('report_time' in my_statistics_data) + self.assertTrue('boot_time' in my_statistics_data) + self.assertTrue('last_update_time' in my_statistics_data) + self.assertTrue('timestamp' in my_statistics_data) + self.assertTrue('lname' in my_statistics_data) + self.assertIsNone(self.stats.get_statistics_data(owner='Foo')) + my_statistics_data = self.stats.get_statistics_data(owner='Stats') + self.assertTrue('boot_time' in my_statistics_data) + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='report_time') + self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='boot_time') + self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='last_update_time') + self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='timestamp') + self.assertEqual(my_statistics_data, 0.0) + my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='lname') + self.assertEqual(my_statistics_data, '') + self.assertIsNone(self.stats.get_statistics_data(owner='Stats', name='Bar')) + self.assertIsNone(self.stats.get_statistics_data(owner='Foo', name='Bar')) + self.assertEqual(self.stats.get_statistics_data(name='Bar'), None) + + def test_update_statistics_data(self): + self.stats.update_statistics_data(owner='Stats', lname='foo@bar') + self.assertTrue('Stats' in self.stats.statistics_data) + my_statistics_data = self.stats.statistics_data['Stats'] + self.assertEqual(my_statistics_data['lname'], 'foo@bar') + self.stats.update_statistics_data(owner='Stats', last_update_time='2000-01-01T10:10:10Z') + self.assertTrue('Stats' in self.stats.statistics_data) + my_statistics_data = self.stats.statistics_data['Stats'] + self.assertEqual(my_statistics_data['last_update_time'], '2000-01-01T10:10:10Z') + self.assertEqual(self.stats.update_statistics_data(owner='Stats', lname=0.0), + ['0.0 should be a string']) + self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), + ['unknown module name']) + + def test_command_status(self): + self.assertEqual(self.stats.command_status(), + isc.config.create_answer( + 0, "Stats is up. (PID " + str(os.getpid()) + ")")) - """ - # prepare for sample data set - stats_spec = [ - { - "item_name": "none_sample", - "item_type": "null", - "item_default": "None" - }, - { - "item_name": "boolean_sample", - "item_type": "boolean", - "item_default": True - }, - { - "item_name": "string_sample", - "item_type": "string", - "item_default": "A something" - }, - { - "item_name": "int_sample", - "item_type": "integer", - "item_default": 9999999 - }, - { - "item_name": "real_sample", - "item_type": "real", - "item_default": 0.0009 - }, - { - "item_name": "list_sample", - "item_type": "list", - "item_default": [0, 1, 2, 3, 4], - "list_item_spec": [] - }, - { - "item_name": "map_sample", - "item_type": "map", - "item_default": {'name':'value'}, - "map_item_spec": [] - }, - { - "item_name": "other_sample", - "item_type": "__unknown__", - "item_default": "__unknown__" - } - ] - # data for comparison - stats_data = { - 'none_sample': None, - 'boolean_sample': True, - 'string_sample': 'A something', - 'int_sample': 9999999, - 'real_sample': 0.0009, - 'list_sample': [0, 1, 2, 3, 4], - 'map_sample': {'name':'value'}, - 'other_sample': '__unknown__' - } - self.assertEqual(self.listener.initialize_data(stats_spec), stats_data) + def test_command_shutdown(self): + self.stats.running = True + self.assertEqual(self.stats.command_shutdown(), + isc.config.create_answer(0)) + self.assertFalse(self.stats.running) + + def test_command_show(self): + self.assertEqual(self.stats.command_show(owner='Foo', name=None), + isc.config.create_answer(1, "item name is not specified")) + self.assertEqual(self.stats.command_show(owner='Foo', name='_bar_'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + self.assertEqual(self.stats.command_show(owner='Foo', name='bar'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + orig_get_timestamp = stats.get_timestamp + orig_get_datetime = stats.get_datetime + stats.get_timestamp = lambda : 1308730448.965706 + stats.get_datetime = lambda : '2011-06-22T08:14:08Z' + self.assertEqual(stats.get_timestamp(), 1308730448.965706) + self.assertEqual(stats.get_datetime(), '2011-06-22T08:14:08Z') + self.assertEqual(self.stats.command_show(owner='Stats', name='report_time'), \ + isc.config.create_answer(0, '2011-06-22T08:14:08Z')) + self.assertEqual(self.stats.statistics_data['Stats']['timestamp'], 1308730448.965706) + self.assertEqual(self.stats.statistics_data['Stats']['boot_time'], '1970-01-01T00:00:00Z') + stats.get_timestamp = orig_get_timestamp + stats.get_datetime = orig_get_datetime + self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( + { "module_name": self.stats.module_name, + "statistics": [] } ) + self.assertRaises( + stats.StatsError, self.stats.command_show, owner='Foo', name='bar') + + def test_command_showchema(self): + (rcode, value) = isc.config.ccsession.parse_answer( + self.stats.command_showschema()) + self.assertEqual(rcode, 0) + self.assertEqual(len(value), 3) + self.assertTrue('Stats' in value) + self.assertTrue('Boss' in value) + self.assertTrue('Auth' in value) + self.assertFalse('__Dummy__' in value) + schema = value['Stats'] + self.assertEqual(len(schema), 5) + for item in schema: + self.assertTrue(len(item) == 6 or len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + if len(item) == 7: + self.assertTrue('item_format' in item) - def test_func_main(self): - # explicitly make failed - self.session.close() - stats.main(session=self.session) + schema = value['Boss'] + self.assertEqual(len(schema), 1) + for item in schema: + self.assertTrue(len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + self.assertTrue('item_format' in item) + + schema = value['Auth'] + self.assertEqual(len(schema), 2) + for item in schema: + self.assertTrue(len(item) == 6) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + + (rcode, value) = isc.config.ccsession.parse_answer( + self.stats.command_showschema(owner='Stats')) + self.assertEqual(rcode, 0) + self.assertFalse('Stats' in value) + self.assertFalse('Boss' in value) + self.assertFalse('Auth' in value) + for item in value: + self.assertTrue(len(item) == 6 or len(item) == 7) + self.assertTrue('item_name' in item) + self.assertTrue('item_type' in item) + self.assertTrue('item_optional' in item) + self.assertTrue('item_default' in item) + self.assertTrue('item_title' in item) + self.assertTrue('item_description' in item) + if len(item) == 7: + self.assertTrue('item_format' in item) + + (rcode, value) = isc.config.ccsession.parse_answer( + self.stats.command_showschema(owner='Stats', name='report_time')) + self.assertEqual(rcode, 0) + self.assertFalse('Stats' in value) + self.assertFalse('Boss' in value) + self.assertFalse('Auth' in value) + self.assertTrue(len(value) == 7) + self.assertTrue('item_name' in value) + self.assertTrue('item_type' in value) + self.assertTrue('item_optional' in value) + self.assertTrue('item_default' in value) + self.assertTrue('item_title' in value) + self.assertTrue('item_description' in value) + self.assertTrue('item_format' in value) + self.assertEqual(value['item_name'], 'report_time') + self.assertEqual(value['item_format'], 'date-time') + + self.assertEqual(self.stats.command_showschema(owner='Foo'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + self.assertEqual(self.stats.command_showschema(owner='Foo', name='bar'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + self.assertEqual(self.stats.command_showschema(owner='Stats', name='bar'), + isc.config.create_answer( + 1, "specified module name and/or item name are incorrect")) + self.assertEqual(self.stats.command_showschema(name='bar'), + isc.config.create_answer( + 1, "module name is not specified")) + + def test_command_set(self): + orig_get_datetime = stats.get_datetime + stats.get_datetime = lambda : '2011-06-22T06:12:38Z' + (rcode, value) = isc.config.ccsession.parse_answer( + self.stats.command_set(owner='Boss', + data={ 'boot_time' : '2011-06-22T13:15:04Z' })) + stats.get_datetime = orig_get_datetime + self.assertEqual(rcode, 0) + self.assertTrue(value is None) + self.assertEqual(self.stats.statistics_data['Boss']['boot_time'], + '2011-06-22T13:15:04Z') + self.assertEqual(self.stats.statistics_data['Stats']['last_update_time'], + '2011-06-22T06:12:38Z') + self.assertEqual(self.stats.command_set(owner='Stats', + data={ 'lname' : 'foo@bar' }), + isc.config.create_answer(0, None)) + self.stats.statistics_data['Stats'] = {} + self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( + { "module_name": self.stats.module_name, + "statistics": [] } ) + self.assertEqual(self.stats.command_set(owner='Stats', + data={ 'lname' : '_foo_@_bar_' }), + isc.config.create_answer( + 1, + "specified module name and/or statistics data are incorrect:" + + " No statistics specification")) + self.stats.statistics_data['Stats'] = {} + self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( + { "module_name": self.stats.module_name, + "statistics": [ + { + "item_name": "dummy", + "item_type": "string", + "item_optional": False, + "item_default": "", + "item_title": "Local Name", + "item_description": "brabra" + } ] } ) + self.assertRaises(stats.StatsError, + self.stats.command_set, owner='Stats', data={ 'dummy' : '_xxxx_yyyy_zzz_' }) def test_osenv(self): """ @@ -652,11 +515,8 @@ class TestStats2(unittest.TestCase): os.environ["B10_FROM_SOURCE"] = path imp.reload(stats) -def result_ok(*args): - if args: - return { 'result': list(args) } - else: - return { 'result': [ 0 ] } +def test_main(): + unittest.main() if __name__ == "__main__": - unittest.main() + test_main() diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index bd23182d2c..cfffc15a35 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -42,11 +42,10 @@ def send_shutdown(module_name): return send_command("shutdown", module_name) class ThreadingServerManager: - def __init__(self, server_class, verbose): + def __init__(self, server_class): self.server_class = server_class self.server_class_name = server_class.__name__ - self.verbose = verbose - self.server = self.server_class(self.verbose) + self.server = self.server_class() self.server._thread = threading.Thread( name=self.server_class_name, target=self.server.run) self.server._thread.daemon = True @@ -60,10 +59,9 @@ class ThreadingServerManager: self.server._thread.join(TIMEOUT_SEC) class MockMsgq: - def __init__(self, verbose): - self.verbose = verbose + def __init__(self): self._started = threading.Event() - self.msgq = msgq.MsgQ(None, verbose) + self.msgq = msgq.MsgQ(None) result = self.msgq.setup() if result: sys.exit("Error on Msgq startup: %s" % result) @@ -81,7 +79,7 @@ class MockMsgq: self.msgq.shutdown() class MockCfgmgr: - def __init__(self, verbose): + def __init__(self): self._started = threading.Event() self.cfgmgr = isc.config.cfgmgr.ConfigManager( os.environ['CONFIG_TESTDATA_PATH'], "b10-config.db") @@ -127,8 +125,7 @@ class MockBoss: """ _BASETIME = (2011, 6, 22, 8, 14, 8, 2, 173, 0) - def __init__(self, verbose): - self.verbose = verbose + def __init__(self): self._started = threading.Event() self.running = False self.spec_file = io.StringIO(self.spec_str) @@ -200,8 +197,7 @@ class MockAuth: } } """ - def __init__(self, verbose): - self.verbose = verbose + def __init__(self): self._started = threading.Event() self.running = False self.spec_file = io.StringIO(self.spec_str) @@ -239,9 +235,9 @@ class MockAuth: return isc.config.create_answer(1, "Unknown Command") class MyStats(stats.Stats): - def __init__(self, verbose): + def __init__(self): self._started = threading.Event() - stats.Stats.__init__(self, verbose) + stats.Stats.__init__(self) def run(self): self._started.set() @@ -251,9 +247,9 @@ class MyStats(stats.Stats): send_shutdown("Stats") class MyStatsHttpd(stats_httpd.StatsHttpd): - def __init__(self, verbose): + def __init__(self): self._started = threading.Event() - stats_httpd.StatsHttpd.__init__(self, verbose) + stats_httpd.StatsHttpd.__init__(self) def run(self): self._started.set() @@ -263,23 +259,22 @@ class MyStatsHttpd(stats_httpd.StatsHttpd): send_shutdown("StatsHttpd") class BaseModules: - def __init__(self, verbose): - self.verbose = verbose + def __init__(self): self.class_name = BaseModules.__name__ # Change value of BIND10_MSGQ_SOCKET_FILE in environment variables os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='unix_socket.') # MockMsgq - self.msgq = ThreadingServerManager(MockMsgq, self.verbose) + self.msgq = ThreadingServerManager(MockMsgq) self.msgq.run() # MockCfgmgr - self.cfgmgr = ThreadingServerManager(MockCfgmgr, self.verbose) + self.cfgmgr = ThreadingServerManager(MockCfgmgr) self.cfgmgr.run() # MockBoss - self.boss = ThreadingServerManager(MockBoss, self.verbose) + self.boss = ThreadingServerManager(MockBoss) self.boss.run() # MockAuth - self.auth = ThreadingServerManager(MockAuth, self.verbose) + self.auth = ThreadingServerManager(MockAuth) self.auth.run() def shutdown(self): From e54bc83c4e8a66fd9ab1ae9f27899d70ef82a066 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 8 Jul 2011 21:22:34 +0900 Subject: [PATCH 892/974] [trac930] remove unneeded empty TODO comments --- doc/guide/bind10-guide.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 59442adddb..00ffee60b5 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1522,7 +1522,6 @@ then change those defaults with config set Resolver/forward_addresses[0]/address - This stats daemon provides commands to identify if it is running, show specified or all statistics data, show specified or all statistics data schema, and set specified statistics @@ -1530,7 +1529,6 @@ then change those defaults with config set Resolver/forward_addresses[0]/address For example, using bindctl: - > Stats show { From 84d9095c66c765cf78814323597b2e3bbef293d5 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 13 Jul 2011 20:25:54 +0900 Subject: [PATCH 893/974] [trac930] modify b10-stats-httpd_test.py - increase seconds in sleep time which is before HTTP client connects to the server - delete 'test_log_message' because of the deletion of original function --- src/bin/stats/tests/b10-stats-httpd_test.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index ae07aa9f27..fcf95ad36f 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -68,7 +68,7 @@ class TestHttpHandler(unittest.TestCase): self.assertTrue(type(self.stats_httpd.httpd) is list) self.assertEqual(len(self.stats_httpd.httpd), 0) statshttpd_server.run() - time.sleep(TIMEOUT_SEC*5) + time.sleep(TIMEOUT_SEC*8) client = http.client.HTTPConnection(address, port) client._http_vsn_str = 'HTTP/1.0\n' client.connect() @@ -249,23 +249,6 @@ class TestHttpHandler(unittest.TestCase): client.close() statshttpd_server.shutdown() - def test_log_message(self): - class MyHttpHandler(stats_httpd.HttpHandler): - def __init__(self): - class _Dummy_class_(): pass - self.address_string = lambda : 'dummyhost' - self.log_date_time_string = lambda : \ - 'DD/MM/YYYY HH:MI:SS' - self.server = _Dummy_class_() - self.server.log_writer = self.log_writer - def log_writer(self, line): - self.logged_line = line - self.handler = MyHttpHandler() - self.handler.log_message("%s %d", 'ABCDEFG', 12345) - self.assertEqual(self.handler.logged_line, - "[b10-stats-httpd] dummyhost - - " - + "[DD/MM/YYYY HH:MI:SS] ABCDEFG 12345\n") - class TestHttpServerError(unittest.TestCase): """Tests for HttpServerError exception""" def test_raises(self): From 703d5f36d0102993f311d21e662a28492d8cf7b4 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 20 Jul 2011 10:00:29 +0900 Subject: [PATCH 894/974] [trac930] add statistics validation for bob --- src/bin/bind10/bind10_src.py.in | 22 +++++++++++----------- src/bin/bind10/tests/bind10_test.py.in | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index c0e4e93728..b8cd284b9e 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -325,18 +325,18 @@ class BoB: answer = isc.config.ccsession.create_answer(0, self._get_stats_data()) elif command == "sendstats": # send statistics data to the stats daemon immediately - cmd = isc.config.ccsession.create_command( + statistics_data = { + 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + } + valid = self.ccs.get_module_spec().validate_statistics( + True, statistics_data) + if valid: + cmd = isc.config.ccsession.create_command( 'set', { "owner": "Boss", - "data": { - 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) - }}) - seq = self.cc_session.group_sendmsg(cmd, 'Stats') - # Consume the answer, in case it becomes a orphan message. - try: - self.cc_session.group_recvmsg(False, seq) - except isc.cc.session.SessionTimeout: - pass - answer = isc.config.ccsession.create_answer(0) + "data": statistics_data }) + seq = self.cc_session.group_sendmsg(cmd, 'Stats') + self.cc_session.group_recvmsg(True, seq) + answer = isc.config.ccsession.create_answer(0) elif command == "ping": answer = isc.config.ccsession.create_answer(0, "pong") elif command == "show_processes": diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 48b19924e4..e002087052 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -137,9 +137,27 @@ class TestBoB(unittest.TestCase): def group_sendmsg(self, msg, group): (self.msg, self.group) = (msg, group) def group_recvmsg(self, nonblock, seq): pass + class DummyModuleCCSession(): + module_spec = isc.config.module_spec.ModuleSpec({ + "module_name": "Boss", + "statistics": [ + { + "item_name": "boot_time", + "item_type": "string", + "item_optional": False, + "item_default": "1970-01-01T00:00:00Z", + "item_title": "Boot time", + "item_description": "A date time when bind10 process starts initially", + "item_format": "date-time" + } + ] + }) + def get_module_spec(self): + return self.module_spec bob = BoB() bob.verbose = True bob.cc_session = DummySession() + bob.ccs = DummyModuleCCSession() # a bad command self.assertEqual(bob.command_handler(-1, None), isc.config.ccsession.create_answer(1, "bad command")) From 9687077033661cf07b6ea2e966299e837a501612 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:28:40 +0900 Subject: [PATCH 895/974] [trac930] add new messages into the message file of Auth and Boss when validation of statistics data to send to statistics module is failed. --- src/bin/auth/auth_messages.mes | 3 +++ src/bin/bind10/bind10_messages.mes | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes index 9f04b76264..1ffa6871ea 100644 --- a/src/bin/auth/auth_messages.mes +++ b/src/bin/auth/auth_messages.mes @@ -257,4 +257,7 @@ request. The zone manager component has been informed of the request, but has returned an error response (which is included in the message). The NOTIFY request will not be honored. +% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified +An error was encountered when the authoritiative server specified +statistics data which is invalid for the auth specification file. diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 4bac069098..4debcdb3ec 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -198,3 +198,7 @@ the message channel. % BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited An unknown child process has exited. The PID is printed, but no further action will be taken by the boss process. + +% BIND10_INVALID_STATISTICS_DATA invalid specification of statistics data specified +An error was encountered when the boss module specified +statistics data which is invalid for the boss specification file. From 62bd7736311e166aea3604b8e486b58c1315f82f Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:32:22 +0900 Subject: [PATCH 896/974] [trac930] add the helper functions which are used around the registration of the function to validate the statistics data. --- src/bin/auth/auth_srv.cc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index 5a3144283a..c9dac88e99 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -125,6 +125,10 @@ public: /// The TSIG keyring const shared_ptr* keyring_; + + /// Bind the ModuleSpec object in config_session_ with + /// isc:config::ModuleSpec::validateStatistics. + void registerStatisticsValidator(); private: std::string db_file_; @@ -139,6 +143,9 @@ private: /// Increment query counter void incCounter(const int protocol); + + // validateStatistics + bool validateStatistics(isc::data::ConstElementPtr data) const; }; AuthSrvImpl::AuthSrvImpl(const bool use_cache, @@ -317,6 +324,7 @@ AuthSrv::setXfrinSession(AbstractSession* xfrin_session) { void AuthSrv::setConfigSession(ModuleCCSession* config_session) { impl_->config_session_ = config_session; + impl_->registerStatisticsValidator(); } void @@ -670,6 +678,22 @@ AuthSrvImpl::incCounter(const int protocol) { } } +void +AuthSrvImpl::registerStatisticsValidator() { + counters_.registerStatisticsValidator( + boost::bind(&AuthSrvImpl::validateStatistics, this, _1)); +} + +bool +AuthSrvImpl::validateStatistics(isc::data::ConstElementPtr data) const { + if (config_session_ == NULL) { + return (false); + } + return ( + config_session_->getModuleSpec().validateStatistics( + data, true)); +} + ConstElementPtr AuthSrvImpl::setDbFile(ConstElementPtr config) { ConstElementPtr answer = isc::config::createAnswer(); From e60ecc91ad65087c3cff3af479cc455abccbe020 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:37:22 +0900 Subject: [PATCH 897/974] [trac930] modify statistics.cc - Add implementation to validate statistics data -- When validation is success, it sends data to statistics module. But when it fails, it doesn't send and logs the message. - Add the function to register the validation function into the class --- src/bin/auth/statistics.cc | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc index 444fb8b35b..e62719f7e2 100644 --- a/src/bin/auth/statistics.cc +++ b/src/bin/auth/statistics.cc @@ -37,11 +37,14 @@ public: void inc(const AuthCounters::CounterType type); bool submitStatistics() const; void setStatisticsSession(isc::cc::AbstractSession* statistics_session); + void registerStatisticsValidator + (AuthCounters::validator_type validator); // Currently for testing purpose only uint64_t getCounter(const AuthCounters::CounterType type) const; private: std::vector counters_; isc::cc::AbstractSession* statistics_session_; + AuthCounters::validator_type validator_; }; AuthCountersImpl::AuthCountersImpl() : @@ -78,6 +81,14 @@ AuthCountersImpl::submitStatistics() const { << "]}"; isc::data::ConstElementPtr statistics_element = isc::data::Element::fromJSON(statistics_string); + // validate the statistics data before send + if (validator_) { + if (!validator_( + statistics_element->get("command")->get(1)->get("data"))) { + LOG_ERROR(auth_logger, AUTH_INVALID_STATISTICS_DATA); + return (false); + } + } try { // group_{send,recv}msg() can throw an exception when encountering // an error, and group_recvmsg() will throw an exception on timeout. @@ -106,6 +117,13 @@ AuthCountersImpl::setStatisticsSession statistics_session_ = statistics_session; } +void +AuthCountersImpl::registerStatisticsValidator + (AuthCounters::validator_type validator) +{ + validator_ = validator; +} + // Currently for testing purpose only uint64_t AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const { @@ -140,3 +158,10 @@ uint64_t AuthCounters::getCounter(const AuthCounters::CounterType type) const { return (impl_->getCounter(type)); } + +void +AuthCounters::registerStatisticsValidator + (AuthCounters::validator_type validator) const +{ + return (impl_->registerStatisticsValidator(validator)); +} From 7f5702a379516cee041129c03dd37d67f26d49c1 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:41:34 +0900 Subject: [PATCH 898/974] [trac930] Add prototypes of validator_typea and registerStatisticsValidator - validator_type -- a type of statistics validation function - registerStatisticsValidator -- the function to register the validation function --- src/bin/auth/statistics.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h index 5bf643656d..c930414c65 100644 --- a/src/bin/auth/statistics.h +++ b/src/bin/auth/statistics.h @@ -131,6 +131,26 @@ public: /// \return the value of the counter specified by \a type. /// uint64_t getCounter(const AuthCounters::CounterType type) const; + + /// \brief A type of validation function for the specification in + /// isc::config::ModuleSpec. + /// + /// This type might be useful for not only statistics + /// specificatoin but also for config_data specification and for + /// commnad. + /// + typedef boost::function + validator_type; + + /// \brief Register a function type of the statistics validation + /// function for AuthCounters. + /// + /// This method never throws an exception. + /// + /// \param validator A function type of the validation of + /// statistics specification. + /// + void registerStatisticsValidator(AuthCounters::validator_type validator) const; }; #endif // __STATISTICS_H From 3e9189a483c0f53eba4f05092c90f7955123f52c Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:43:26 +0900 Subject: [PATCH 899/974] [trac930] Add unittests to test sumitStatistics with the validation of statistics data and add mock ModuleSpec class --- src/bin/auth/tests/statistics_unittest.cc | 66 ++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc index cd2755b110..98e573b495 100644 --- a/src/bin/auth/tests/statistics_unittest.cc +++ b/src/bin/auth/tests/statistics_unittest.cc @@ -16,6 +16,8 @@ #include +#include + #include #include @@ -76,6 +78,13 @@ protected: } MockSession statistics_session_; AuthCounters counters; + // no need to be inherited from the original class here. + class MockModuleSpec { + public: + bool validateStatistics(ConstElementPtr, const bool valid) const + { return (valid); } + }; + MockModuleSpec module_spec_; }; void @@ -181,7 +190,7 @@ TEST_F(AuthCountersTest, submitStatisticsWithException) { statistics_session_.setThrowSessionTimeout(false); } -TEST_F(AuthCountersTest, submitStatistics) { +TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) { // Submit statistics data. // Validate if it submits correct data. @@ -211,4 +220,59 @@ TEST_F(AuthCountersTest, submitStatistics) { EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue()); } +TEST_F(AuthCountersTest, submitStatisticsWithValidator) { + + //a validator for the unittest + AuthCounters::validator_type validator; + ConstElementPtr el; + + // Submit statistics data with correct statistics validator. + validator = boost::bind( + &AuthCountersTest::MockModuleSpec::validateStatistics, + &module_spec_, _1, true); + + EXPECT_TRUE(validator(el)); + + // register validator to AuthCounters + counters.registerStatisticsValidator(validator); + + // Counters should be initialized to 0. + EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY)); + EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY)); + + // UDP query counter is set to 2. + counters.inc(AuthCounters::COUNTER_UDP_QUERY); + counters.inc(AuthCounters::COUNTER_UDP_QUERY); + // TCP query counter is set to 1. + counters.inc(AuthCounters::COUNTER_TCP_QUERY); + + // checks the value returned by submitStatistics + EXPECT_TRUE(counters.submitStatistics()); + + // Destination is "Stats". + EXPECT_EQ("Stats", statistics_session_.msg_destination); + // Command is "set". + EXPECT_EQ("set", statistics_session_.sent_msg->get("command") + ->get(0)->stringValue()); + EXPECT_EQ("Auth", statistics_session_.sent_msg->get("command") + ->get(1)->get("owner")->stringValue()); + ConstElementPtr statistics_data = statistics_session_.sent_msg + ->get("command")->get(1) + ->get("data"); + // UDP query counter is 2 and TCP query counter is 1. + EXPECT_EQ(2, statistics_data->get("queries.udp")->intValue()); + EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue()); + + // Submit statistics data with incorrect statistics validator. + validator = boost::bind( + &AuthCountersTest::MockModuleSpec::validateStatistics, + &module_spec_, _1, false); + + EXPECT_FALSE(validator(el)); + + counters.registerStatisticsValidator(validator); + + // checks the value returned by submitStatistics + EXPECT_FALSE(counters.submitStatistics()); +} } From 6556a2ffdd7bdb5370c2f1b3d8c9e8799ef82140 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:45:19 +0900 Subject: [PATCH 900/974] [trac930] add the logging when the validation of statistics data fails --- src/bin/bind10/bind10_src.py.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index b8cd284b9e..da7fa0ec8a 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -337,6 +337,10 @@ class BoB: seq = self.cc_session.group_sendmsg(cmd, 'Stats') self.cc_session.group_recvmsg(True, seq) answer = isc.config.ccsession.create_answer(0) + else: + logger.fatal(BIND10_INVALID_STATISTICS_DATA); + answer = isc.config.ccsession.create_answer( + 1, "specified statistics data is invalid") elif command == "ping": answer = isc.config.ccsession.create_answer(0, "pong") elif command == "show_processes": From b743e6ba98c8cbb53c45e1c0f59e5a78ba62f5d4 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 18:50:41 +0900 Subject: [PATCH 901/974] [trac930] add changes because query counter names described in the specfile are changed. --- tests/system/bindctl/tests.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/system/bindctl/tests.sh b/tests/system/bindctl/tests.sh index 6923c4167c..49ef0f17b0 100755 --- a/tests/system/bindctl/tests.sh +++ b/tests/system/bindctl/tests.sh @@ -24,6 +24,10 @@ SYSTEMTESTTOP=.. status=0 n=0 +# TODO: consider consistency with statistics definition in auth.spec +auth_queries_tcp="\" +auth_queries_udp="\" + echo "I:Checking b10-auth is working by default ($n)" $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1 # perform a simple check on the output (digcomp would be too much for this) @@ -40,8 +44,8 @@ echo 'Stats show --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # the server should have received 1 UDP and 1 TCP queries (TCP query was # sent from the server startup script) -grep "\"auth.queries.tcp\": 1," bindctl.out.$n > /dev/null || status=1 -grep "\"auth.queries.udp\": 1," bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_tcp".*\<1\>" bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_udp".*\<1\>" bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` @@ -73,8 +77,8 @@ echo 'Stats show ' | $RUN_BINDCTL \ --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # The statistics counters should have been reset while stop/start. -grep "\"auth.queries.tcp\": 0," bindctl.out.$n > /dev/null || status=1 -grep "\"auth.queries.udp\": 1," bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_tcp".*\<0\>" bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_udp".*\<1\>" bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` @@ -97,8 +101,8 @@ echo 'Stats show ' | $RUN_BINDCTL \ --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1 # The statistics counters shouldn't be reset due to hot-swapping datasource. -grep "\"auth.queries.tcp\": 0," bindctl.out.$n > /dev/null || status=1 -grep "\"auth.queries.udp\": 2," bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_tcp".*\<0\>" bindctl.out.$n > /dev/null || status=1 +grep $auth_queries_udp".*\<2\>" bindctl.out.$n > /dev/null || status=1 if [ $status != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` From 8750dc3ab772e29d7374d779cefb3c8b8c61d2d1 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 22 Jul 2011 21:40:07 +0900 Subject: [PATCH 902/974] [trac930] fix conflicts with trac1021 --- src/bin/stats/tests/b10-stats-httpd_test.py | 89 ++++++++++++--------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index fcf95ad36f..2cc78ddc32 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -548,10 +548,11 @@ class TestStatsHttpd(unittest.TestCase): def test_xml_handler(self): orig_get_stats_data = stats_httpd.StatsHttpd.get_stats_data - stats_httpd.StatsHttpd.get_stats_data = lambda x: {'foo':'bar'} + stats_httpd.StatsHttpd.get_stats_data = lambda x: \ + { 'Dummy' : { 'foo':'bar' } } xml_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XML_TEMPLATE_LOCATION).substitute( - xml_string='bar', + xml_string='bar', xsd_namespace=stats_httpd.XSD_NAMESPACE, xsd_url_path=stats_httpd.XSD_URL_PATH, xsl_url_path=stats_httpd.XSL_URL_PATH) @@ -559,7 +560,8 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual(type(xml_body1), str) self.assertEqual(type(xml_body2), str) self.assertEqual(xml_body1, xml_body2) - stats_httpd.StatsHttpd.get_stats_data = lambda x: {'bar':'foo'} + stats_httpd.StatsHttpd.get_stats_data = lambda x: \ + { 'Dummy' : {'bar':'foo'} } xml_body2 = stats_httpd.StatsHttpd().xml_handler() self.assertNotEqual(xml_body1, xml_body2) stats_httpd.StatsHttpd.get_stats_data = orig_get_stats_data @@ -567,35 +569,41 @@ class TestStatsHttpd(unittest.TestCase): def test_xsd_handler(self): orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - [{ - "item_name": "foo", - "item_type": "string", - "item_optional": False, - "item_default": "bar", - "item_description": "foo is bar", - "item_title": "Foo" - }] + { "Dummy" : + [{ + "item_name": "foo", + "item_type": "string", + "item_optional": False, + "item_default": "bar", + "item_description": "foo is bar", + "item_title": "Foo" + }] + } xsd_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XSD_TEMPLATE_LOCATION).substitute( - xsd_string='' \ + xsd_string=\ + '' \ + '' \ + 'Foo' \ + 'foo is bar' \ - + '', + + '' \ + + '', xsd_namespace=stats_httpd.XSD_NAMESPACE) xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() self.assertEqual(type(xsd_body1), str) self.assertEqual(type(xsd_body2), str) self.assertEqual(xsd_body1, xsd_body2) stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - [{ - "item_name": "bar", - "item_type": "string", - "item_optional": False, - "item_default": "foo", - "item_description": "bar is foo", - "item_title": "bar" - }] + { "Dummy" : + [{ + "item_name": "bar", + "item_type": "string", + "item_optional": False, + "item_default": "foo", + "item_description": "bar is foo", + "item_title": "bar" + }] + } xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() self.assertNotEqual(xsd_body1, xsd_body2) stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec @@ -603,19 +611,22 @@ class TestStatsHttpd(unittest.TestCase): def test_xsl_handler(self): orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - [{ - "item_name": "foo", - "item_type": "string", - "item_optional": False, - "item_default": "bar", - "item_description": "foo is bar", - "item_title": "Foo" - }] + { "Dummy" : + [{ + "item_name": "foo", + "item_type": "string", + "item_optional": False, + "item_default": "bar", + "item_description": "foo is bar", + "item_title": "Foo" + }] + } xsl_body1 = stats_httpd.StatsHttpd().open_template( stats_httpd.XSL_TEMPLATE_LOCATION).substitute( xsl_string='

' \ + + '' \ + '' \ - + '' \ + + '' \ + '', xsd_namespace=stats_httpd.XSD_NAMESPACE) xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() @@ -623,14 +634,16 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual(type(xsl_body2), str) self.assertEqual(xsl_body1, xsl_body2) stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ - [{ - "item_name": "bar", - "item_type": "string", - "item_optional": False, - "item_default": "foo", - "item_description": "bar is foo", - "item_title": "bar" - }] + { "Dummy" : + [{ + "item_name": "bar", + "item_type": "string", + "item_optional": False, + "item_default": "foo", + "item_description": "bar is foo", + "item_title": "bar" + }] + } xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() self.assertNotEqual(xsl_body1, xsl_body2) stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec From cebd7e3562312ade50d972af49239cee7f10d057 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 10:14:57 +0900 Subject: [PATCH 903/974] [trac930] modify parse_spec function returns empty dict if list-type is not specified in the argument --- src/bin/stats/stats.py.in | 1 + src/bin/stats/tests/b10-stats_test.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 95a643f0aa..82c1da9bb1 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -72,6 +72,7 @@ def parse_spec(spec): """ parse spec type data """ + if type(spec) is not list: return {} def _parse_spec(spec): item_type = spec['item_type'] if item_type == "integer": diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index b013c7a8bc..b0f87f4377 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -69,7 +69,7 @@ class TestUtilties(unittest.TestCase): 'test_list2' : [0,0,0], 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 }, 'test_none' : None }) - self.assertRaises(TypeError, stats.parse_spec, None) + self.assertEqual(stats.parse_spec(None), {}) self.assertRaises(KeyError, stats.parse_spec, [{'item_name':'Foo'}]) def test_get_timestamp(self): From a0bb482b46bd05f8c8774bacdd26dc891cb3bef7 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 10:18:07 +0900 Subject: [PATCH 904/974] [trac930] add a test pattern which the set command with a non-existent item name is sent --- src/bin/stats/tests/b10-stats_test.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index b0f87f4377..6ddd39b18a 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -483,6 +483,15 @@ class TestStats(unittest.TestCase): self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( { "module_name": self.stats.module_name, "statistics": [] } ) + self.assertEqual(self.stats.command_set(owner='Stats', + data={ 'lname' : '_foo_@_bar_' }), + isc.config.create_answer( + 1, + "specified module name and/or statistics data are incorrect:" + + " unknown item lname")) + self.stats.statistics_data['Stats'] = {} + self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( + { "module_name": self.stats.module_name } ) self.assertEqual(self.stats.command_set(owner='Stats', data={ 'lname' : '_foo_@_bar_' }), isc.config.create_answer( From fdf02d580f2bb1fbc6fa85ee0edd81a07404d1de Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 16:42:54 +0900 Subject: [PATCH 905/974] [trac930] remove unnecessary a white space --- src/bin/stats/tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index cfffc15a35..b3e20b5f76 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -182,7 +182,7 @@ class MockAuth: "item_type": "integer", "item_optional": false, "item_default": 0, - "item_title": "Queries TCP ", + "item_title": "Queries TCP", "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially" }, { From 611d0300fb8bb2e87d787023cb5c6030ee07d8d2 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 16:49:21 +0900 Subject: [PATCH 906/974] [trac930] modify stats.py and b10-stats_test.py - correct error messages in bindctl it prints together with arguments. - modify the command_show function it reports statistics data of the module even if name is not specified. - add/modify unittests depending on the changes of error messages --- src/bin/stats/stats.py.in | 17 ++++---- src/bin/stats/tests/b10-stats_test.py | 56 +++++++++++++++++++++------ 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 82c1da9bb1..aa03e19f34 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -267,7 +267,7 @@ class Stats: self.statistics_data[owner].update(data) return except KeyError: - errors.append('unknown module name') + errors.append("unknown module name: " + str(owner)) return errors def command_status(self): @@ -297,8 +297,6 @@ class Stats: else: logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_SHOW_ALL_COMMAND) - if owner and not name: - return isc.config.create_answer(1, "item name is not specified") errors = self.update_statistics_data( self.module_name, timestamp=get_timestamp(), @@ -306,11 +304,12 @@ class Stats: ) if errors: raise StatsError("stats spec file is incorrect") ret = self.get_statistics_data(owner, name) - if ret: + if ret is not None: return isc.config.create_answer(0, ret) else: return isc.config.create_answer( - 1, "specified module name and/or item name are incorrect") + 1, "specified arguments are incorrect: " \ + + "owner: " + str(owner) + ", name: " + str(name)) def command_showschema(self, owner=None, name=None): """ @@ -343,7 +342,8 @@ class Stats: else: return isc.config.create_answer(0, schema) return isc.config.create_answer( - 1, "specified module name and/or item name are incorrect") + 1, "specified arguments are incorrect: " \ + + "owner: " + str(owner) + ", name: " + str(name)) def command_set(self, owner, data): """ @@ -352,9 +352,8 @@ class Stats: errors = self.update_statistics_data(owner, **data) if errors: return isc.config.create_answer( - 1, - "specified module name and/or statistics data are incorrect: " - + ", ".join(errors)) + 1, "errors while setting statistics data: " \ + + ", ".join(errors)) errors = self.update_statistics_data( self.module_name, last_update_time=get_datetime() ) if errors: diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 6ddd39b18a..640b796fad 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -331,7 +331,7 @@ class TestStats(unittest.TestCase): self.assertEqual(self.stats.update_statistics_data(owner='Stats', lname=0.0), ['0.0 should be a string']) self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), - ['unknown module name']) + ['unknown module name: Dummy']) def test_command_status(self): self.assertEqual(self.stats.command_status(), @@ -346,13 +346,20 @@ class TestStats(unittest.TestCase): def test_command_show(self): self.assertEqual(self.stats.command_show(owner='Foo', name=None), - isc.config.create_answer(1, "item name is not specified")) + isc.config.create_answer( + 1, "specified arguments are incorrect: owner: Foo, name: None")) self.assertEqual(self.stats.command_show(owner='Foo', name='_bar_'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Foo, name: _bar_")) self.assertEqual(self.stats.command_show(owner='Foo', name='bar'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Foo, name: bar")) + self.assertEqual(self.stats.command_show(owner='Auth'), + isc.config.create_answer( + 0, {'queries.tcp': 0, 'queries.udp': 0})) + self.assertEqual(self.stats.command_show(owner='Auth', name='queries.udp'), + isc.config.create_answer( + 0, 0)) orig_get_timestamp = stats.get_timestamp orig_get_datetime = stats.get_datetime stats.get_timestamp = lambda : 1308730448.965706 @@ -452,13 +459,42 @@ class TestStats(unittest.TestCase): self.assertEqual(self.stats.command_showschema(owner='Foo'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Foo, name: None")) self.assertEqual(self.stats.command_showschema(owner='Foo', name='bar'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Foo, name: bar")) + self.assertEqual(self.stats.command_showschema(owner='Auth'), + isc.config.create_answer( + 0, [{ + "item_default": 0, + "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially", + "item_name": "queries.tcp", + "item_optional": False, + "item_title": "Queries TCP", + "item_type": "integer" + }, + { + "item_default": 0, + "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially", + "item_name": "queries.udp", + "item_optional": False, + "item_title": "Queries UDP", + "item_type": "integer" + }])) + self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.tcp'), + isc.config.create_answer( + 0, { + "item_default": 0, + "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially", + "item_name": "queries.tcp", + "item_optional": False, + "item_title": "Queries TCP", + "item_type": "integer" + })) + self.assertEqual(self.stats.command_showschema(owner='Stats', name='bar'), isc.config.create_answer( - 1, "specified module name and/or item name are incorrect")) + 1, "specified arguments are incorrect: owner: Stats, name: bar")) self.assertEqual(self.stats.command_showschema(name='bar'), isc.config.create_answer( 1, "module name is not specified")) @@ -487,8 +523,7 @@ class TestStats(unittest.TestCase): data={ 'lname' : '_foo_@_bar_' }), isc.config.create_answer( 1, - "specified module name and/or statistics data are incorrect:" - + " unknown item lname")) + "errors while setting statistics data: unknown item lname")) self.stats.statistics_data['Stats'] = {} self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( { "module_name": self.stats.module_name } ) @@ -496,8 +531,7 @@ class TestStats(unittest.TestCase): data={ 'lname' : '_foo_@_bar_' }), isc.config.create_answer( 1, - "specified module name and/or statistics data are incorrect:" - + " No statistics specification")) + "errors while setting statistics data: No statistics specification")) self.stats.statistics_data['Stats'] = {} self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( { "module_name": self.stats.module_name, From ac06a06d1df9a1cc905b224b79921b0d0ade4c05 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 27 Jul 2011 20:45:18 +0900 Subject: [PATCH 907/974] [trac930] modify the update_modues function There is no part of statistics category in the spec file of a module which has no statistics data. --- src/bin/stats/stats.py.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index aa03e19f34..706cc43645 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -216,8 +216,7 @@ class Stats: (rcode, value) = isc.config.ccsession.parse_answer(answer) if rcode == 0: for mod in value: - spec = { "module_name" : mod, - "statistics" : [] } + spec = { "module_name" : mod } if value[mod] and type(value[mod]) is list: spec["statistics"] = value[mod] modules[mod] = isc.config.module_spec.ModuleSpec(spec) From eb4be17ddf3b26c379e3f100cf8e8b0fd4329537 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 28 Jul 2011 22:07:15 +0900 Subject: [PATCH 908/974] [trac930] modify logging add loggings and new messages for logging remove unused messages from the message file add test logging names into unittest scripts --- src/bin/stats/stats.py.in | 22 ++++++++++++--------- src/bin/stats/stats_httpd.py.in | 3 +-- src/bin/stats/stats_messages.mes | 21 ++++++++++---------- src/bin/stats/tests/b10-stats-httpd_test.py | 3 +++ src/bin/stats/tests/b10-stats_test.py | 3 +++ 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 706cc43645..15e5876bf0 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -156,9 +156,7 @@ class Stats: Start stats module """ self.running = True - # TODO: should be added into new logging interface - # if self.verbose: - # sys.stdout.write("[b10-stats] starting\n") + logger.info(STATS_STARTING) # request Bob to send statistics data logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) @@ -289,7 +287,7 @@ class Stats: """ handle show command """ - if (owner or name): + if owner or name: logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_SHOW_NAME_COMMAND, str(owner)+", "+str(name)) @@ -314,9 +312,13 @@ class Stats: """ handle show command """ - # TODO: should be added into new logging interface - # if self.verbose: - # sys.stdout.write("[b10-stats] 'showschema' command received\n") + if owner or name: + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND, + str(owner)+", "+str(name)) + else: + logger.debug(DBG_STATS_MESSAGING, + STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND) self.update_modules() schema = {} schema_byname = {} @@ -372,10 +374,12 @@ if __name__ == "__main__": stats.start() except OptionValueError as ove: logger.fatal(STATS_BAD_OPTION_VALUE, ove) + sys.exit(1) except SessionError as se: logger.fatal(STATS_CC_SESSION_ERROR, se) - # TODO: should be added into new logging interface + sys.exit(1) except StatsError as se: - sys.exit("[b10-stats] %s" % se) + logger.fatal(STATS_START_ERROR, se) + sys.exit(1) except KeyboardInterrupt as kie: logger.info(STATS_STOPPED_BY_KEYBOARD) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index ef7d9f6048..7ed55c7d4b 100755 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -301,8 +301,7 @@ class StatsHttpd: # restore old config self.load_config(old_config) self.open_httpd() - return isc.config.ccsession.create_answer( - 1, "[b10-stats-httpd] %s" % err) + return isc.config.ccsession.create_answer(1, str(err)) else: return isc.config.ccsession.create_answer(0) diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes index 9ad07cf493..cfffb3adb8 100644 --- a/src/bin/stats/stats_messages.mes +++ b/src/bin/stats/stats_messages.mes @@ -28,16 +28,6 @@ control bus. A likely problem is that the message bus daemon This debug message is printed when the stats module has received a configuration update from the configuration manager. -% STATS_RECEIVED_REMOVE_COMMAND received command to remove %1 -A remove command for the given name was sent to the stats module, and -the given statistics value will now be removed. It will not appear in -statistics reports until it appears in a statistics update from a -module again. - -% STATS_RECEIVED_RESET_COMMAND received command to reset all statistics -The stats module received a command to clear all collected statistics. -The data is cleared until it receives an update from the modules again. - % STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics The stats module received a command to show all statistics that it has collected. @@ -72,4 +62,15 @@ installation problem, where the specification file stats.spec is from a different version of BIND 10 than the stats module itself. Please check your installation. +% STATS_STARTING starting +The stats module will be now starting. +% STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND received command to show all statistics schema +The stats module received a command to show all statistics schemas of all modules. + +% STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND received command to show statistics schema for %1 +The stats module received a command to show the specified statistics schema of the specified module. + +% STATS_START_ERROR stats module error: %1 +An internal error occurred while starting the stats module. The stats +module will be now shutting down. diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 2cc78ddc32..89dea29501 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -30,6 +30,9 @@ import stats_httpd import stats from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC +# set test name for logger +isc.log.init("b10-stats-httpd_test") + DUMMY_DATA = { 'Boss' : { "boot_time": "2011-03-04T11:59:06Z" diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 640b796fad..4c6bde0f5d 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -24,6 +24,9 @@ import stats import isc.cc.session from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, TIMEOUT_SEC +# set test name for logger +isc.log.init("b10-stats_test") + class TestUtilties(unittest.TestCase): items = [ { 'item_name': 'test_int1', 'item_type': 'integer', 'item_default': 12345 }, From 30f4856101bf23ce155ef0f2ebd1ca6f034d2420 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 29 Jul 2011 22:11:38 +0900 Subject: [PATCH 909/974] [trac930] remove a unnecessary x bit from stats_httpd.py.in --- src/bin/stats/stats_httpd.py.in | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/bin/stats/stats_httpd.py.in diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in old mode 100755 new mode 100644 From 898485cd30084d478e8be688151cd11fb4d492a7 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 1 Aug 2011 18:21:23 +0900 Subject: [PATCH 910/974] [trac930] rename the function name - rename the name of 'parse_spec' to 'get_spec_defaults' in the result of consideration of what it is doing - modify the description of the function as docstring - fix unitttests for the stats module depending on the function name --- src/bin/stats/stats.py.in | 18 ++++++++++-------- src/bin/stats/tests/b10-stats_test.py | 12 ++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 15e5876bf0..21ca602940 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -68,12 +68,14 @@ def get_datetime(gmt=None): if not gmt: gmt = gmtime() return strftime("%Y-%m-%dT%H:%M:%SZ", gmt) -def parse_spec(spec): +def get_spec_defaults(spec): """ - parse spec type data + extracts the default values of the items from spec specified in + arg, and returns the dict-type variable which is a set of the item + names and the default values """ if type(spec) is not list: return {} - def _parse_spec(spec): + def _get_spec_defaults(spec): item_type = spec['item_type'] if item_type == "integer": return int(spec.get('item_default', 0)) @@ -86,14 +88,14 @@ def parse_spec(spec): elif item_type == "list": return spec.get( "item_default", - [ _parse_spec(s) for s in spec["list_item_spec"] ]) + [ _get_spec_defaults(s) for s in spec["list_item_spec"] ]) elif item_type == "map": return spec.get( "item_default", - dict([ (s["item_name"], _parse_spec(s)) for s in spec["map_item_spec"] ]) ) + dict([ (s["item_name"], _get_spec_defaults(s)) for s in spec["map_item_spec"] ]) ) else: return spec.get("item_default", None) - return dict([ (s['item_name'], _parse_spec(s)) for s in spec ]) + return dict([ (s['item_name'], _get_spec_defaults(s)) for s in spec ]) class Callback(): """ @@ -137,7 +139,7 @@ class Stats: name = "command_" + cmd["command_name"] try: callback = getattr(self, name) - kwargs = parse_spec(cmd["command_args"]) + kwargs = get_spec_defaults(cmd["command_args"]) self.callbacks[name] = Callback(command=callback, kwargs=kwargs) except AttributeError: raise StatsError(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) @@ -248,7 +250,7 @@ class Stats: self.update_modules() statistics_data = {} for (name, module) in self.modules.items(): - value = parse_spec(module.get_statistics_spec()) + value = get_spec_defaults(module.get_statistics_spec()) if module.validate_statistics(True, value): statistics_data[name] = value for (name, value) in self.statistics_data.items(): diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 4c6bde0f5d..011c95d9cb 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -56,9 +56,9 @@ class TestUtilties(unittest.TestCase): { 'item_name': 'test_none', 'item_type': 'none' } ] - def test_parse_spec(self): + def test_get_spec_defaults(self): self.assertEqual( - stats.parse_spec(self.items), { + stats.get_spec_defaults(self.items), { 'test_int1' : 12345 , 'test_real1' : 12345.6789 , 'test_bool1' : True , @@ -72,8 +72,8 @@ class TestUtilties(unittest.TestCase): 'test_list2' : [0,0,0], 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 }, 'test_none' : None }) - self.assertEqual(stats.parse_spec(None), {}) - self.assertRaises(KeyError, stats.parse_spec, [{'item_name':'Foo'}]) + self.assertEqual(stats.get_spec_defaults(None), {}) + self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}]) def test_get_timestamp(self): self.assertEqual(stats.get_timestamp(), 1308730448.965706) @@ -280,7 +280,7 @@ class TestStats(unittest.TestCase): self.assertTrue('Stats' in self.stats.modules) self.assertTrue('Boss' in self.stats.modules) self.assertFalse('Dummy' in self.stats.modules) - my_statistics_data = stats.parse_spec(self.stats.modules['Stats'].get_statistics_spec()) + my_statistics_data = stats.get_spec_defaults(self.stats.modules['Stats'].get_statistics_spec()) self.assertTrue('report_time' in my_statistics_data) self.assertTrue('boot_time' in my_statistics_data) self.assertTrue('last_update_time' in my_statistics_data) @@ -291,7 +291,7 @@ class TestStats(unittest.TestCase): self.assertEqual(my_statistics_data['last_update_time'], "1970-01-01T00:00:00Z") self.assertEqual(my_statistics_data['timestamp'], 0.0) self.assertEqual(my_statistics_data['lname'], "") - my_statistics_data = stats.parse_spec(self.stats.modules['Boss'].get_statistics_spec()) + my_statistics_data = stats.get_spec_defaults(self.stats.modules['Boss'].get_statistics_spec()) self.assertTrue('boot_time' in my_statistics_data) self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") From 9f8ddd6ee1b73c9403f85b6ef5c85605ca393aa7 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 1 Aug 2011 18:38:35 +0900 Subject: [PATCH 911/974] [trac930] raise StatsError including errors in the stats spec file --- src/bin/stats/stats.py.in | 10 +++++++--- src/bin/stats/tests/b10-stats_test.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 21ca602940..4492302979 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -173,7 +173,8 @@ class Stats: boot_time=get_datetime(_BASETIME) ) if errors: - raise StatsError("stats spec file is incorrect") + raise StatsError("stats spec file is incorrect: " + + ", ".join(errors)) while self.running: self.mccs.check_command(False) @@ -301,7 +302,9 @@ class Stats: timestamp=get_timestamp(), report_time=get_datetime() ) - if errors: raise StatsError("stats spec file is incorrect") + if errors: + raise StatsError("stats spec file is incorrect: " + + ", ".join(errors)) ret = self.get_statistics_data(owner, name) if ret is not None: return isc.config.create_answer(0, ret) @@ -360,7 +363,8 @@ class Stats: errors = self.update_statistics_data( self.module_name, last_update_time=get_datetime() ) if errors: - raise StatsError("stats spec file is incorrect") + raise StatsError("stats spec file is incorrect: " + + ", ".join(errors)) return isc.config.create_answer(0) if __name__ == "__main__": diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 011c95d9cb..7eb25559d8 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -191,7 +191,7 @@ class TestStats(unittest.TestCase): def test_start_with_err(self): statsd = stats.Stats() - statsd.update_statistics_data = lambda x,**y: [1] + statsd.update_statistics_data = lambda x,**y: ['an error'] self.assertRaises(stats.StatsError, statsd.start) def test_config_handler(self): From 3516ab551851273faeeb0b8696695e5f3ffc88f9 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 2 Aug 2011 19:57:58 +0900 Subject: [PATCH 912/974] [trac930] remove tailing whitespaces. --- src/bin/stats/b10-stats-httpd.xml | 2 +- src/bin/stats/stats_httpd.py.in | 2 +- src/bin/stats/tests/Makefile.am | 2 +- src/bin/stats/tests/b10-stats-httpd_test.py | 10 +++++----- src/bin/stats/tests/b10-stats_test.py | 16 ++++++++-------- src/bin/stats/tests/test_utils.py | 8 ++++---- src/bin/tests/Makefile.am | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml index 3636d9d543..c8df9b8a6e 100644 --- a/src/bin/stats/b10-stats-httpd.xml +++ b/src/bin/stats/b10-stats-httpd.xml @@ -132,7 +132,7 @@ CONFIGURATION AND COMMANDS - The configurable setting in + The configurable setting in stats-httpd.spec is: diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index 7ed55c7d4b..3d86475445 100644 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -415,7 +415,7 @@ class StatsHttpd: annotation.append(documentation) element.append(annotation) alltag.append(element) - + complextype = xml.etree.ElementTree.Element("complexType") complextype.append(alltag) mod_element = xml.etree.ElementTree.Element("element", { "name" : mod }) diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index 0ed7766b09..b5edc592e4 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -13,7 +13,7 @@ endif # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 89dea29501..5d22f72441 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -30,7 +30,7 @@ import stats_httpd import stats from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC -# set test name for logger +# set test name for logger isc.log.init("b10-stats-httpd_test") DUMMY_DATA = { @@ -364,7 +364,7 @@ class TestStatsHttpd(unittest.TestCase): for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.close_httpd() - + # dual stack (address is ipv4) if self.ipv6_enabled: self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] @@ -380,20 +380,20 @@ class TestStatsHttpd(unittest.TestCase): for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.close_httpd() - + # only-ipv4 single stack (force set ipv6 ) if not self.ipv6_enabled: self.stats_httpd.http_addrs = [ ('::1', 8000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - + # hostname self.stats_httpd.http_addrs = [ ('localhost', 8000) ] self.stats_httpd.open_httpd() for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.close_httpd() - + self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ] self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) self.assertEqual(type(self.stats_httpd.httpd), list) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 7eb25559d8..cd53a57821 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -24,7 +24,7 @@ import stats import isc.cc.session from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, TIMEOUT_SEC -# set test name for logger +# set test name for logger isc.log.init("b10-stats_test") class TestUtilties(unittest.TestCase): @@ -205,19 +205,19 @@ class TestStats(unittest.TestCase): self.base.boss.server._started.wait() self.assertEqual( send_command( - 'show', 'Stats', + 'show', 'Stats', params={ 'owner' : 'Boss', 'name' : 'boot_time' }), (0, '2011-06-22T08:14:08Z')) self.assertEqual( send_command( - 'set', 'Stats', + 'set', 'Stats', params={ 'owner' : 'Boss', 'data' : { 'boot_time' : '2012-06-22T18:24:08Z' } }), (0, None)) self.assertEqual( send_command( - 'show', 'Stats', + 'show', 'Stats', params={ 'owner' : 'Boss', 'name' : 'boot_time' }), (0, '2012-06-22T18:24:08Z')) @@ -267,7 +267,7 @@ class TestStats(unittest.TestCase): self.assertTrue('item_description' in item) if len(item) == 7: self.assertTrue('item_format' in item) - + self.assertEqual( send_command('__UNKNOWN__', 'Stats'), (1, "Unknown command: '__UNKNOWN__'")) @@ -340,13 +340,13 @@ class TestStats(unittest.TestCase): self.assertEqual(self.stats.command_status(), isc.config.create_answer( 0, "Stats is up. (PID " + str(os.getpid()) + ")")) - + def test_command_shutdown(self): self.stats.running = True self.assertEqual(self.stats.command_shutdown(), isc.config.create_answer(0)) self.assertFalse(self.stats.running) - + def test_command_show(self): self.assertEqual(self.stats.command_show(owner='Foo', name=None), isc.config.create_answer( @@ -380,7 +380,7 @@ class TestStats(unittest.TestCase): "statistics": [] } ) self.assertRaises( stats.StatsError, self.stats.command_show, owner='Foo', name='bar') - + def test_command_showchema(self): (rcode, value) = isc.config.ccsession.parse_answer( self.stats.command_showschema()) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index b3e20b5f76..a793dc69a6 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -10,7 +10,7 @@ import threading import tempfile import msgq -import isc.config.cfgmgr +import isc.config.cfgmgr import stats import stats_httpd @@ -49,7 +49,7 @@ class ThreadingServerManager: self.server._thread = threading.Thread( name=self.server_class_name, target=self.server.run) self.server._thread.daemon = True - + def run(self): self.server._thread.start() self.server._started.wait() @@ -94,7 +94,7 @@ class MockCfgmgr: def shutdown(self): self.cfgmgr.running = False - + class MockBoss: spec_str = """\ { @@ -157,7 +157,7 @@ class MockBoss: params = { "owner": "Boss", "data": { 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', self._BASETIME) - } + } } return send_command("set", "Stats", params=params, session=self.cc_session) return isc.config.create_answer(1, "Unknown Command") diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am index 0ce992d5d7..41b497fd64 100644 --- a/src/bin/tests/Makefile.am +++ b/src/bin/tests/Makefile.am @@ -14,7 +14,7 @@ endif # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif From 8ed3b760c179df435882f2ad96b6dcfad5b6e9fa Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 2 Aug 2011 20:17:28 +0900 Subject: [PATCH 913/974] [trac930] modify b10-stats_test.py - set the constant variables in the setUp method in the TestUtilties class, and compare values returned from the functions with these constants in testing methods. - remove the tearDown method which has no test case in the TestCallback class --- src/bin/stats/tests/b10-stats_test.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index cd53a57821..0c2fa3c2f3 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -56,6 +56,13 @@ class TestUtilties(unittest.TestCase): { 'item_name': 'test_none', 'item_type': 'none' } ] + def setUp(self): + self.const_timestamp = 1308730448.965706 + self.const_timetuple = (2011, 6, 22, 8, 14, 8, 2, 173, 0) + self.const_datetime = '2011-06-22T08:14:08Z' + stats.time = lambda : self.const_timestamp + stats.gmtime = lambda : self.const_timetuple + def test_get_spec_defaults(self): self.assertEqual( stats.get_spec_defaults(self.items), { @@ -76,14 +83,12 @@ class TestUtilties(unittest.TestCase): self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}]) def test_get_timestamp(self): - self.assertEqual(stats.get_timestamp(), 1308730448.965706) + self.assertEqual(stats.get_timestamp(), self.const_timestamp) def test_get_datetime(self): - stats.time = lambda : 1308730448.965706 - stats.gmtime = lambda : (2011, 6, 22, 8, 14, 8, 2, 173, 0) - self.assertEqual(stats.get_datetime(), '2011-06-22T08:14:08Z') + self.assertEqual(stats.get_datetime(), self.const_datetime) self.assertNotEqual(stats.get_datetime( - (2011, 6, 22, 8, 23, 40, 2, 173, 0)), '2011-06-22T08:14:08Z') + (2011, 6, 22, 8, 23, 40, 2, 173, 0)), self.const_datetime) class TestCallback(unittest.TestCase): def setUp(self): @@ -108,9 +113,6 @@ class TestCallback(unittest.TestCase): args=self.dummy_args ) - def tearDown(self): - pass - def test_init(self): self.assertEqual((self.cback1.command, self.cback1.args, self.cback1.kwargs), (self.dummy_func, self.dummy_args, self.dummy_kwargs)) From 1fd37ae8a4bb25a6e85ffb2158b2ae95fe8cbd04 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 2 Aug 2011 21:44:07 +0900 Subject: [PATCH 914/974] [trac930] modify stats.py - add more documentations into update_modules, get_statistics_data and update_statistics_data methods - modify two methods: "update_modules" and "get_statistics_data" methods raise StatsError instead of just returning None, when communication between stats module and config manager is failed or when it can't find specified statistics data. - also modify the unittest depending on the changes of these behaviors. --- src/bin/stats/stats.py.in | 29 ++++++++++++++++++++------- src/bin/stats/tests/b10-stats_test.py | 15 ++++++++++---- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 4492302979..a8aef4c0ec 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -205,7 +205,10 @@ class Stats: def update_modules(self): """ - update information of each module + updates information of each module. This method gets each + module's information from the config manager and sets it into + self.modules. If its getting from the config manager fails, it + raises StatsError. """ modules = {} seq = self.cc_session.group_sendmsg( @@ -221,12 +224,16 @@ class Stats: if value[mod] and type(value[mod]) is list: spec["statistics"] = value[mod] modules[mod] = isc.config.module_spec.ModuleSpec(spec) + else: + raise StatsError("Updating module spec fails: " + str(value)) modules[self.module_name] = self.mccs.get_module_spec() self.modules = modules def get_statistics_data(self, owner=None, name=None): """ - return statistics data which stats module has of each module + returns statistics data which stats module has of each + module. If it can't find specified statistics data, it raises + StatsError. """ self.update_statistics_data() if owner and name: @@ -243,10 +250,18 @@ class Stats: pass else: return self.statistics_data + raise StatsError("No statistics data found: " + + "owner: " + str(owner) + ", " + + "name: " + str(name)) def update_statistics_data(self, owner=None, **data): """ - change statistics date of specified module into specified data + change statistics date of specified module into specified + data. It updates information of each module first, and it + updates statistics data. If specified data is invalid for + statistics spec of specified owner, it returns a list of error + messeges. If there is no error or if neither owner nor data is + specified in args, it returns None. """ self.update_modules() statistics_data = {} @@ -305,10 +320,10 @@ class Stats: if errors: raise StatsError("stats spec file is incorrect: " + ", ".join(errors)) - ret = self.get_statistics_data(owner, name) - if ret is not None: - return isc.config.create_answer(0, ret) - else: + try: + return isc.config.create_answer( + 0, self.get_statistics_data(owner, name)) + except StatsError: return isc.config.create_answer( 1, "specified arguments are incorrect: " \ + "owner: " + str(owner) + ", name: " + str(name)) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 0c2fa3c2f3..acf2269c2b 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -296,6 +296,10 @@ class TestStats(unittest.TestCase): my_statistics_data = stats.get_spec_defaults(self.stats.modules['Boss'].get_statistics_spec()) self.assertTrue('boot_time' in my_statistics_data) self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") + orig_parse_answer = stats.isc.config.ccsession.parse_answer + stats.isc.config.ccsession.parse_answer = lambda x: (99, 'error') + self.assertRaises(stats.StatsError, self.stats.update_modules) + stats.isc.config.ccsession.parse_answer = orig_parse_answer def test_get_statistics_data(self): my_statistics_data = self.stats.get_statistics_data() @@ -307,7 +311,7 @@ class TestStats(unittest.TestCase): self.assertTrue('last_update_time' in my_statistics_data) self.assertTrue('timestamp' in my_statistics_data) self.assertTrue('lname' in my_statistics_data) - self.assertIsNone(self.stats.get_statistics_data(owner='Foo')) + self.assertRaises(stats.StatsError, self.stats.get_statistics_data, owner='Foo') my_statistics_data = self.stats.get_statistics_data(owner='Stats') self.assertTrue('boot_time' in my_statistics_data) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='report_time') @@ -320,9 +324,12 @@ class TestStats(unittest.TestCase): self.assertEqual(my_statistics_data, 0.0) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='lname') self.assertEqual(my_statistics_data, '') - self.assertIsNone(self.stats.get_statistics_data(owner='Stats', name='Bar')) - self.assertIsNone(self.stats.get_statistics_data(owner='Foo', name='Bar')) - self.assertEqual(self.stats.get_statistics_data(name='Bar'), None) + self.assertRaises(stats.StatsError, self.stats.get_statistics_data, + owner='Stats', name='Bar') + self.assertRaises(stats.StatsError, self.stats.get_statistics_data, + owner='Foo', name='Bar') + self.assertRaises(stats.StatsError, self.stats.get_statistics_data, + name='Bar') def test_update_statistics_data(self): self.stats.update_statistics_data(owner='Stats', lname='foo@bar') From 7581a21a7dce1dc6b92ad24293b4269a3531e6d4 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 2 Aug 2011 22:00:11 +0900 Subject: [PATCH 915/974] [trac930] add comments about abstracts of the test scripts in their headers --- src/bin/stats/tests/b10-stats-httpd_test.py | 7 +++++++ src/bin/stats/tests/b10-stats_test.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 5d22f72441..e1c05dae42 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -13,6 +13,13 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +This unittests run Msgq, Cfgmgr, Auth, Boss and Stats as mock in +background. Because the stats httpd communicates various other modules +in runtime. However this aim is not to actually simulate a whole +system running. +""" + import unittest import os import imp diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index acf2269c2b..f8830eb5ea 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -13,6 +13,13 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +This unittests run Msgq, Cfgmgr, Auth and Boss as mock in +background. Because the stats module communicates various other +modules in runtime. However this aim is not to actually simulate a +whole system running. +""" + import unittest import os import threading From fe1d6665faf06b3fcc0aaf8ec72905aa4b7ce1f7 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 3 Aug 2011 11:41:05 +0900 Subject: [PATCH 916/974] [trac930] refactor unittests - remove time.sleep from various unittests and add in the "run" method in ThreadingServerManager - adjust the sleep time (TIMEOUT_SEC) - join some small unittests (test_start_with_err, test_command_status, test_command_shutdown) --- src/bin/stats/tests/b10-stats-httpd_test.py | 7 ------- src/bin/stats/tests/b10-stats_test.py | 23 +++++++++++---------- src/bin/stats/tests/test_utils.py | 6 ++++-- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index e1c05dae42..870e6b9038 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -78,7 +78,6 @@ class TestHttpHandler(unittest.TestCase): self.assertTrue(type(self.stats_httpd.httpd) is list) self.assertEqual(len(self.stats_httpd.httpd), 0) statshttpd_server.run() - time.sleep(TIMEOUT_SEC*8) client = http.client.HTTPConnection(address, port) client._http_vsn_str = 'HTTP/1.0\n' client.connect() @@ -175,7 +174,6 @@ class TestHttpHandler(unittest.TestCase): statshttpd_server.run() self.assertTrue(self.stats_server.server.running) self.stats_server.shutdown() - time.sleep(TIMEOUT_SEC*2) self.assertFalse(self.stats_server.server.running) statshttpd.cc_session.set_timeout(milliseconds=TIMEOUT_SEC/1000) client = http.client.HTTPConnection(address, port) @@ -213,7 +211,6 @@ class TestHttpHandler(unittest.TestCase): lambda cmd, args: \ isc.config.ccsession.create_answer(1, "I have an error.") ) - time.sleep(TIMEOUT_SEC*5) client = http.client.HTTPConnection(address, port) client.connect() @@ -244,7 +241,6 @@ class TestHttpHandler(unittest.TestCase): self.stats_httpd = statshttpd_server.server self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) statshttpd_server.run() - time.sleep(TIMEOUT_SEC*5) client = http.client.HTTPConnection(address, port) client.connect() client.putrequest('HEAD', stats_httpd.XML_URL_PATH) @@ -423,7 +419,6 @@ class TestStatsHttpd(unittest.TestCase): self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) self.statshttpd_server.server.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) self.statshttpd_server.run() - time.sleep(TIMEOUT_SEC) self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) self.statshttpd_server.shutdown() @@ -434,7 +429,6 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd = self.statshttpd_server.server self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65455 }]}) self.statshttpd_server.run() - time.sleep(TIMEOUT_SEC*2) self.assertTrue(self.stats_httpd.running) self.statshttpd_server.shutdown() self.assertFalse(self.stats_httpd.running) @@ -461,7 +455,6 @@ class TestStatsHttpd(unittest.TestCase): statshttpd = statshttpd_server.server statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) statshttpd_server.run() - time.sleep(TIMEOUT_SEC*2) statshttpd_server.shutdown() def test_open_template(self): diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index f8830eb5ea..b2c1b7fdea 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -189,28 +189,28 @@ class TestStats(unittest.TestCase): stats.SPECFILE_LOCATION = orig_spec_location def test_start(self): + # start without err statsserver = ThreadingServerManager(MyStats) - stats = statsserver.server - self.assertFalse(stats.running) + statsd = statsserver.server + self.assertFalse(statsd.running) statsserver.run() - time.sleep(TIMEOUT_SEC) - self.assertTrue(stats.running) + self.assertTrue(statsd.running) statsserver.shutdown() - self.assertFalse(stats.running) + self.assertFalse(statsd.running) - def test_start_with_err(self): + # start with err statsd = stats.Stats() statsd.update_statistics_data = lambda x,**y: ['an error'] self.assertRaises(stats.StatsError, statsd.start) - def test_config_handler(self): + def test_handlers(self): + # config_handler self.assertEqual(self.stats.config_handler({'foo':'bar'}), isc.config.create_answer(0)) - def test_command_handler(self): + # command_handler statsserver = ThreadingServerManager(MyStats) statsserver.run() - time.sleep(TIMEOUT_SEC*4) self.base.boss.server._started.wait() self.assertEqual( send_command( @@ -352,12 +352,13 @@ class TestStats(unittest.TestCase): self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), ['unknown module name: Dummy']) - def test_command_status(self): + def test_commands(self): + # status self.assertEqual(self.stats.command_status(), isc.config.create_answer( 0, "Stats is up. (PID " + str(os.getpid()) + ")")) - def test_command_shutdown(self): + # shutdown self.stats.running = True self.assertEqual(self.stats.command_shutdown(), isc.config.create_answer(0)) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index a793dc69a6..f9ab969e13 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -15,9 +15,9 @@ import stats import stats_httpd # TODO: consider appropriate timeout seconds -TIMEOUT_SEC = 0.01 +TIMEOUT_SEC = 0.05 -def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC*2): +def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC): if not session: cc_session = isc.cc.Session() else: @@ -53,6 +53,8 @@ class ThreadingServerManager: def run(self): self.server._thread.start() self.server._started.wait() + # waiting for the server's being ready for listening + time.sleep(TIMEOUT_SEC) def shutdown(self): self.server.shutdown() From 863509e2dc3bf96fd38476d787abb62e0da46624 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 5 Aug 2011 14:48:27 +0900 Subject: [PATCH 917/974] [trac930] modify b10-stats-httpd_test.py, b10-stats_test.py and test_utils.py - change address for test to 127.0.0.1 due to platform 127.0.0.2 can't be assigned - remove unnecessary thread.Event.wait() - add thread.Event.clear() after thread.Event.wait() --- src/bin/stats/tests/b10-stats-httpd_test.py | 4 ++-- src/bin/stats/tests/b10-stats_test.py | 1 - src/bin/stats/tests/test_utils.py | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 870e6b9038..3677c5f1f3 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -512,13 +512,13 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="127.0.0.2",port=8000)])), + dict(listen_on=[dict(address="127.0.0.1",port=8000)])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "127.0.0.2") + self.assertTrue(addr["address"] == "127.0.0.1") self.assertTrue(addr["port"] == 8000) if self.ipv6_enabled: diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index b2c1b7fdea..2757c61ece 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -211,7 +211,6 @@ class TestStats(unittest.TestCase): # command_handler statsserver = ThreadingServerManager(MyStats) statsserver.run() - self.base.boss.server._started.wait() self.assertEqual( send_command( 'show', 'Stats', diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index f9ab969e13..e79db48951 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -53,6 +53,7 @@ class ThreadingServerManager: def run(self): self.server._thread.start() self.server._started.wait() + self.server._started.clear() # waiting for the server's being ready for listening time.sleep(TIMEOUT_SEC) From 53314ecb63f3f0f85629b66a228207658d8fd73f Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 5 Aug 2011 16:24:03 +0900 Subject: [PATCH 918/974] [trac930] modify b10-stats-httpd_test.py and b10-stats_test.py - revise header comments in each test script - replace some hard-coded time strings with the constants defined in the setUp function - merged several checks about B10_FROM_SOURCE into the TestOSEnv class --- src/bin/stats/tests/b10-stats-httpd_test.py | 9 ++- src/bin/stats/tests/b10-stats_test.py | 90 ++++++++++++--------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 3677c5f1f3..8c84277930 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -14,10 +14,11 @@ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ -This unittests run Msgq, Cfgmgr, Auth, Boss and Stats as mock in -background. Because the stats httpd communicates various other modules -in runtime. However this aim is not to actually simulate a whole -system running. +In each of these tests we start several virtual components. They are +not the real components, no external processes are started. They are +just simple mock objects running each in its own thread and pretending +to be bind10 modules. This helps testing the stats http server in a +close to real environment. """ import unittest diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 2757c61ece..7cf4f7ede0 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -14,10 +14,11 @@ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ -This unittests run Msgq, Cfgmgr, Auth and Boss as mock in -background. Because the stats module communicates various other -modules in runtime. However this aim is not to actually simulate a -whole system running. +In each of these tests we start several virtual components. They are +not the real components, no external processes are started. They are +just simple mock objects running each in its own thread and pretending +to be bind10 modules. This helps testing the stats module in a close +to real environment. """ import unittest @@ -146,11 +147,9 @@ class TestStats(unittest.TestCase): def setUp(self): self.base = BaseModules() self.stats = stats.Stats() - self.assertTrue("B10_FROM_SOURCE" in os.environ) - self.assertEqual(stats.SPECFILE_LOCATION, \ - os.environ["B10_FROM_SOURCE"] + os.sep + \ - "src" + os.sep + "bin" + os.sep + "stats" + \ - os.sep + "stats.spec") + self.const_timestamp = 1308730448.965706 + self.const_datetime = '2011-06-22T08:14:08Z' + self.const_default_datetime = '1970-01-01T00:00:00Z' def tearDown(self): self.base.shutdown() @@ -216,19 +215,19 @@ class TestStats(unittest.TestCase): 'show', 'Stats', params={ 'owner' : 'Boss', 'name' : 'boot_time' }), - (0, '2011-06-22T08:14:08Z')) + (0, self.const_datetime)) self.assertEqual( send_command( 'set', 'Stats', params={ 'owner' : 'Boss', - 'data' : { 'boot_time' : '2012-06-22T18:24:08Z' } }), + 'data' : { 'boot_time' : self.const_datetime } }), (0, None)) self.assertEqual( send_command( 'show', 'Stats', params={ 'owner' : 'Boss', 'name' : 'boot_time' }), - (0, '2012-06-22T18:24:08Z')) + (0, self.const_datetime)) self.assertEqual( send_command('status', 'Stats'), (0, "Stats is up. (PID " + str(os.getpid()) + ")")) @@ -242,7 +241,7 @@ class TestStats(unittest.TestCase): self.assertEqual(len(value['Stats']), 5) self.assertEqual(len(value['Boss']), 1) self.assertTrue('boot_time' in value['Boss']) - self.assertEqual(value['Boss']['boot_time'], '2012-06-22T18:24:08Z') + self.assertEqual(value['Boss']['boot_time'], self.const_datetime) self.assertTrue('report_time' in value['Stats']) self.assertTrue('boot_time' in value['Stats']) self.assertTrue('last_update_time' in value['Stats']) @@ -294,14 +293,14 @@ class TestStats(unittest.TestCase): self.assertTrue('last_update_time' in my_statistics_data) self.assertTrue('timestamp' in my_statistics_data) self.assertTrue('lname' in my_statistics_data) - self.assertEqual(my_statistics_data['report_time'], "1970-01-01T00:00:00Z") - self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") - self.assertEqual(my_statistics_data['last_update_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['report_time'], self.const_default_datetime) + self.assertEqual(my_statistics_data['boot_time'], self.const_default_datetime) + self.assertEqual(my_statistics_data['last_update_time'], self.const_default_datetime) self.assertEqual(my_statistics_data['timestamp'], 0.0) self.assertEqual(my_statistics_data['lname'], "") my_statistics_data = stats.get_spec_defaults(self.stats.modules['Boss'].get_statistics_spec()) self.assertTrue('boot_time' in my_statistics_data) - self.assertEqual(my_statistics_data['boot_time'], "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data['boot_time'], self.const_default_datetime) orig_parse_answer = stats.isc.config.ccsession.parse_answer stats.isc.config.ccsession.parse_answer = lambda x: (99, 'error') self.assertRaises(stats.StatsError, self.stats.update_modules) @@ -321,11 +320,11 @@ class TestStats(unittest.TestCase): my_statistics_data = self.stats.get_statistics_data(owner='Stats') self.assertTrue('boot_time' in my_statistics_data) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='report_time') - self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data, self.const_default_datetime) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='boot_time') - self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data, self.const_default_datetime) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='last_update_time') - self.assertEqual(my_statistics_data, "1970-01-01T00:00:00Z") + self.assertEqual(my_statistics_data, self.const_default_datetime) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='timestamp') self.assertEqual(my_statistics_data, 0.0) my_statistics_data = self.stats.get_statistics_data(owner='Stats', name='lname') @@ -342,10 +341,10 @@ class TestStats(unittest.TestCase): self.assertTrue('Stats' in self.stats.statistics_data) my_statistics_data = self.stats.statistics_data['Stats'] self.assertEqual(my_statistics_data['lname'], 'foo@bar') - self.stats.update_statistics_data(owner='Stats', last_update_time='2000-01-01T10:10:10Z') + self.stats.update_statistics_data(owner='Stats', last_update_time=self.const_datetime) self.assertTrue('Stats' in self.stats.statistics_data) my_statistics_data = self.stats.statistics_data['Stats'] - self.assertEqual(my_statistics_data['last_update_time'], '2000-01-01T10:10:10Z') + self.assertEqual(my_statistics_data['last_update_time'], self.const_datetime) self.assertEqual(self.stats.update_statistics_data(owner='Stats', lname=0.0), ['0.0 should be a string']) self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'), @@ -381,14 +380,14 @@ class TestStats(unittest.TestCase): 0, 0)) orig_get_timestamp = stats.get_timestamp orig_get_datetime = stats.get_datetime - stats.get_timestamp = lambda : 1308730448.965706 - stats.get_datetime = lambda : '2011-06-22T08:14:08Z' - self.assertEqual(stats.get_timestamp(), 1308730448.965706) - self.assertEqual(stats.get_datetime(), '2011-06-22T08:14:08Z') + stats.get_timestamp = lambda : self.const_timestamp + stats.get_datetime = lambda : self.const_datetime + self.assertEqual(stats.get_timestamp(), self.const_timestamp) + self.assertEqual(stats.get_datetime(), self.const_datetime) self.assertEqual(self.stats.command_show(owner='Stats', name='report_time'), \ - isc.config.create_answer(0, '2011-06-22T08:14:08Z')) - self.assertEqual(self.stats.statistics_data['Stats']['timestamp'], 1308730448.965706) - self.assertEqual(self.stats.statistics_data['Stats']['boot_time'], '1970-01-01T00:00:00Z') + isc.config.create_answer(0, self.const_datetime)) + self.assertEqual(self.stats.statistics_data['Stats']['timestamp'], self.const_timestamp) + self.assertEqual(self.stats.statistics_data['Stats']['boot_time'], self.const_default_datetime) stats.get_timestamp = orig_get_timestamp stats.get_datetime = orig_get_datetime self.stats.mccs.specification = isc.config.module_spec.ModuleSpec( @@ -520,17 +519,17 @@ class TestStats(unittest.TestCase): def test_command_set(self): orig_get_datetime = stats.get_datetime - stats.get_datetime = lambda : '2011-06-22T06:12:38Z' + stats.get_datetime = lambda : self.const_datetime (rcode, value) = isc.config.ccsession.parse_answer( self.stats.command_set(owner='Boss', - data={ 'boot_time' : '2011-06-22T13:15:04Z' })) + data={ 'boot_time' : self.const_datetime })) stats.get_datetime = orig_get_datetime self.assertEqual(rcode, 0) self.assertTrue(value is None) self.assertEqual(self.stats.statistics_data['Boss']['boot_time'], - '2011-06-22T13:15:04Z') + self.const_datetime) self.assertEqual(self.stats.statistics_data['Stats']['last_update_time'], - '2011-06-22T06:12:38Z') + self.const_datetime) self.assertEqual(self.stats.command_set(owner='Stats', data={ 'lname' : 'foo@bar' }), isc.config.create_answer(0, None)) @@ -566,16 +565,27 @@ class TestStats(unittest.TestCase): self.assertRaises(stats.StatsError, self.stats.command_set, owner='Stats', data={ 'dummy' : '_xxxx_yyyy_zzz_' }) +class TestOSEnv(unittest.TestCase): def test_osenv(self): """ - test for not having environ "B10_FROM_SOURCE" + test for the environ variable "B10_FROM_SOURCE" + "B10_FROM_SOURCE" is set in Makefile """ - if "B10_FROM_SOURCE" in os.environ: - path = os.environ["B10_FROM_SOURCE"] - os.environ.pop("B10_FROM_SOURCE") - imp.reload(stats) - os.environ["B10_FROM_SOURCE"] = path - imp.reload(stats) + # test case having B10_FROM_SOURCE + self.assertTrue("B10_FROM_SOURCE" in os.environ) + self.assertEqual(stats.SPECFILE_LOCATION, \ + os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + \ + os.sep + "stats.spec") + # test case not having B10_FROM_SOURCE + path = os.environ["B10_FROM_SOURCE"] + os.environ.pop("B10_FROM_SOURCE") + self.assertFalse("B10_FROM_SOURCE" in os.environ) + # import stats again + imp.reload(stats) + # revert the changes + os.environ["B10_FROM_SOURCE"] = path + imp.reload(stats) def test_main(): unittest.main() From 9b8925a4d0ecbd8a09d307dfd56fa15fb8eedcc6 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 22 Aug 2011 18:05:57 +0900 Subject: [PATCH 919/974] [1175] modify stats_httpd.py.in - don't use DEFAULT_CONFIG - move up mccs.start and open_httpd to __init__(). It takes time to do these functions, and an extra sleep is needed in unittests. - set running to False in http stopping - use validate_config in module_spec class - don't close/open http before it's opened --- src/bin/stats/stats_httpd.py.in | 45 +++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index 3d86475445..da1ee3bda6 100644 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -68,7 +68,6 @@ XSD_URL_PATH = '/bind10/statistics/xsd' XSL_URL_PATH = '/bind10/statistics/xsl' # TODO: This should be considered later. XSD_NAMESPACE = 'http://bind10.isc.org' + XSD_URL_PATH -DEFAULT_CONFIG = dict(version=0, listen_on=[('127.0.0.1', 8000)]) # Assign this process name isc.util.process.rename() @@ -159,7 +158,11 @@ class StatsHttpd: self.mccs = None self.httpd = [] self.open_mccs() + self.config = {} self.load_config() + self.http_addrs = [] + self.mccs.start() + self.open_httpd() def open_mccs(self): """Opens a ModuleCCSession object""" @@ -182,18 +185,19 @@ class StatsHttpd: """Loads configuration from spec file or new configuration from the config manager""" # load config - if len(new_config) > 0: - self.config.update(new_config) - else: - self.config = DEFAULT_CONFIG - self.config.update( - dict([ - (itm['item_name'], self.mccs.get_value(itm['item_name'])[0]) - for itm in self.mccs.get_module_spec().get_config_spec() - ]) - ) + if len(self.config) == 0: + self.config = dict([ + (itm['item_name'], self.mccs.get_value(itm['item_name'])[0]) + for itm in self.mccs.get_module_spec().get_config_spec() + ]) + self.config.update(new_config) # set addresses and ports for HTTP - self.http_addrs = [ (cf['address'], cf['port']) for cf in self.config['listen_on'] ] + addrs = [] + if 'listen_on' in self.config: + for cf in self.config['listen_on']: + if 'address' in cf and 'port' in cf: + addrs.append((cf['address'], cf['port'])) + self.http_addrs = addrs def open_httpd(self): """Opens sockets for HTTP. Iterating each HTTP address to be @@ -235,8 +239,6 @@ class StatsHttpd: def start(self): """Starts StatsHttpd objects to run. Waiting for client requests by using select.select functions""" - self.open_httpd() - self.mccs.start() self.running = True while self.running: try: @@ -269,6 +271,7 @@ class StatsHttpd: logger.info(STATHTTPD_SHUTDOWN) self.close_httpd() self.close_mccs() + self.running = False def get_sockets(self): """Returns sockets to select.select""" @@ -285,15 +288,19 @@ class StatsHttpd: addresses and ports to listen HTTP requests on.""" logger.debug(DBG_STATHTTPD_MESSAGING, STATHTTPD_HANDLE_CONFIG, new_config) - for key in new_config.keys(): - if key not in DEFAULT_CONFIG and key != "version": - logger.error(STATHTTPD_UNKNOWN_CONFIG_ITEM, key) + errors = [] + if not self.mccs.get_module_spec().\ + validate_config(False, new_config, errors): return isc.config.ccsession.create_answer( - 1, "Unknown known config: %s" % key) + 1, ", ".join(errors)) # backup old config old_config = self.config.copy() - self.close_httpd() self.load_config(new_config) + # If the http sockets aren't opened or + # if new_config doesn't have'listen_on', it returns + if len(self.httpd) == 0 or 'listen_on' not in new_config: + return isc.config.ccsession.create_answer(0) + self.close_httpd() try: self.open_httpd() except HttpServerError as err: From 290e89c515e051dad269f1acbce0b52a541d9c8c Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 22 Aug 2011 18:10:25 +0900 Subject: [PATCH 920/974] [1175] modify b10-stats-httpd_test.py and b10-stats_test.py - add function get_availaddr to get available address and port on the platform - add function is_ipv6enabled to check ipv6 enabled on the platform - add miscellaneous changes to refactor unittest --- src/bin/stats/tests/b10-stats-httpd_test.py | 442 ++++++++++---------- src/bin/stats/tests/b10-stats_test.py | 32 +- 2 files changed, 248 insertions(+), 226 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 8c84277930..79631f4c76 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -36,7 +36,7 @@ import xml.etree.ElementTree import isc import stats_httpd import stats -from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC +from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, send_shutdown # set test name for logger isc.log.init("b10-stats-httpd_test") @@ -58,35 +58,61 @@ DUMMY_DATA = { } } +def get_availaddr(address='127.0.0.1'): + """returns tuple of address and port available on the + platform. default range of port is from 65535 to 50000""" + for port in range(65535, 50000, -1): + try: + if is_ipv6_enabled(address): + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + else : + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind((address, port)) + sock.close() + return (address, port) + except socket.error: + pass + +def is_ipv6_enabled(address='::1', port=8000): + """checks IPv6 enabled on the platform""" + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.bind((address, port)) + sock.close() + return True + except socket.error: + return False + class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" - def setUp(self): self.base = BaseModules() self.stats_server = ThreadingServerManager(MyStats) self.stats = self.stats_server.server self.stats_server.run() + (self.address, self.port) = get_availaddr() + self.stats_httpd_server = ThreadingServerManager(MyStatsHttpd, (self.address, self.port)) + self.stats_httpd = self.stats_httpd_server.server + self.stats_httpd_server.run() + self.client = http.client.HTTPConnection(self.address, self.port) + self.client._http_vsn_str = 'HTTP/1.0\n' + self.client.connect() def tearDown(self): + self.client.close() + self.stats_httpd_server.shutdown() self.stats_server.shutdown() self.base.shutdown() def test_do_GET(self): - (address, port) = ('127.0.0.1', 65450) - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.stats_httpd = statshttpd_server.server - self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) self.assertTrue(type(self.stats_httpd.httpd) is list) - self.assertEqual(len(self.stats_httpd.httpd), 0) - statshttpd_server.run() - client = http.client.HTTPConnection(address, port) - client._http_vsn_str = 'HTTP/1.0\n' - client.connect() + self.assertEqual(len(self.stats_httpd.httpd), 1) + self.assertEqual((self.address, self.port), self.stats_httpd.http_addrs[0]) # URL is '/bind10/statistics/xml' - client.putrequest('GET', stats_httpd.XML_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XML_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.getheader("Content-type"), "text/xml") self.assertTrue(int(response.getheader("Content-Length")) > 0) self.assertEqual(response.status, 200) @@ -100,9 +126,9 @@ class TestHttpHandler(unittest.TestCase): self.assertIsNotNone(root.find(mod + '/' + item)) # URL is '/bind10/statitics/xsd' - client.putrequest('GET', stats_httpd.XSD_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XSD_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.getheader("Content-type"), "text/xml") self.assertTrue(int(response.getheader("Content-Length")) > 0) self.assertEqual(response.status, 200) @@ -120,9 +146,9 @@ class TestHttpHandler(unittest.TestCase): self.assertTrue(elm.attrib['name'] in DUMMY_DATA) # URL is '/bind10/statitics/xsl' - client.putrequest('GET', stats_httpd.XSL_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XSL_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.getheader("Content-type"), "text/xml") self.assertTrue(int(response.getheader("Content-Length")) > 0) self.assertEqual(response.status, 200) @@ -147,114 +173,83 @@ class TestHttpHandler(unittest.TestCase): [ tds[0].text+'/'+item for item in DUMMY_DATA[tds[0].text].keys() ]) # 302 redirect - client._http_vsn_str = 'HTTP/1.1' - client.putrequest('GET', '/') - client.putheader('Host', address) - client.endheaders() - response = client.getresponse() + self.client._http_vsn_str = 'HTTP/1.1' + self.client.putrequest('GET', '/') + self.client.putheader('Host', self.address) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 302) self.assertEqual(response.getheader('Location'), - "http://%s:%d%s" % (address, port, stats_httpd.XML_URL_PATH)) + "http://%s:%d%s" % (self.address, self.port, stats_httpd.XML_URL_PATH)) # # 404 NotFound - client._http_vsn_str = 'HTTP/1.0' - client.putrequest('GET', '/path/to/foo/bar') - client.endheaders() - response = client.getresponse() + self.client._http_vsn_str = 'HTTP/1.0' + self.client.putrequest('GET', '/path/to/foo/bar') + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 404) - client.close() - statshttpd_server.shutdown() def test_do_GET_failed1(self): - # failure case(connection with Stats is down) - (address, port) = ('127.0.0.1', 65451) - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - statshttpd = statshttpd_server.server - statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - statshttpd_server.run() - self.assertTrue(self.stats_server.server.running) - self.stats_server.shutdown() - self.assertFalse(self.stats_server.server.running) - statshttpd.cc_session.set_timeout(milliseconds=TIMEOUT_SEC/1000) - client = http.client.HTTPConnection(address, port) - client.connect() + # failure case(Stats is down) + self.assertTrue(self.stats.running) + send_shutdown("Stats") # Stats is down + self.assertFalse(self.stats.running) + self.stats_httpd.cc_session.set_timeout(milliseconds=100) # request XML - client.putrequest('GET', stats_httpd.XML_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XML_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 500) # request XSD - client.putrequest('GET', stats_httpd.XSD_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XSD_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 500) # request XSL - client.putrequest('GET', stats_httpd.XSL_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XSL_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 500) - client.close() - statshttpd_server.shutdown() - def test_do_GET_failed2(self): - # failure case(connection with Stats is down) - (address, port) = ('127.0.0.1', 65452) - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.stats_httpd = statshttpd_server.server - self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - statshttpd_server.run() + # failure case(Stats replies an error) self.stats.mccs.set_command_handler( lambda cmd, args: \ isc.config.ccsession.create_answer(1, "I have an error.") ) - client = http.client.HTTPConnection(address, port) - client.connect() # request XML - client.putrequest('GET', stats_httpd.XML_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XML_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 500) # request XSD - client.putrequest('GET', stats_httpd.XSD_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XSD_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 500) # request XSL - client.putrequest('GET', stats_httpd.XSL_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('GET', stats_httpd.XSL_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 500) - client.close() - statshttpd_server.shutdown() - def test_do_HEAD(self): - (address, port) = ('127.0.0.1', 65453) - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.stats_httpd = statshttpd_server.server - self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - statshttpd_server.run() - client = http.client.HTTPConnection(address, port) - client.connect() - client.putrequest('HEAD', stats_httpd.XML_URL_PATH) - client.endheaders() - response = client.getresponse() + self.client.putrequest('HEAD', stats_httpd.XML_URL_PATH) + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 200) - client.putrequest('HEAD', '/path/to/foo/bar') - client.endheaders() - response = client.getresponse() + self.client.putrequest('HEAD', '/path/to/foo/bar') + self.client.endheaders() + response = self.client.getresponse() self.assertEqual(response.status, 404) - client.close() - statshttpd_server.shutdown() class TestHttpServerError(unittest.TestCase): """Tests for HttpServerError exception""" @@ -273,9 +268,12 @@ class TestHttpServer(unittest.TestCase): self.base.shutdown() def test_httpserver(self): - statshttpd = stats_httpd.StatsHttpd() - self.assertEqual(type(statshttpd.httpd), list) - self.assertEqual(len(statshttpd.httpd), 0) + self.stats_httpd = MyStatsHttpd(get_availaddr()) + self.assertEqual(type(self.stats_httpd.httpd), list) + self.assertEqual(len(self.stats_httpd.httpd), 1) + for httpd in self.stats_httpd.httpd: + self.assertTrue(isinstance(httpd, stats_httpd.HttpServer)) + self.stats_httpd.stop() class TestStatsHttpdError(unittest.TestCase): """Tests for StatsHttpdError exception""" @@ -292,28 +290,20 @@ class TestStatsHttpd(unittest.TestCase): def setUp(self): self.base = BaseModules() self.stats_server = ThreadingServerManager(MyStats) - self.stats = self.stats_server.server self.stats_server.run() - self.stats_httpd = stats_httpd.StatsHttpd() - # checking IPv6 enabled on this platform - self.ipv6_enabled = True - try: - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - sock.bind(("::1",8000)) - sock.close() - except socket.error: - self.ipv6_enabled = False + self.ipv6_enabled = is_ipv6_enabled() def tearDown(self): - self.stats_httpd.stop() self.stats_server.shutdown() self.base.shutdown() def test_init(self): + server_address = get_availaddr() + self.stats_httpd = MyStatsHttpd(server_address) self.assertEqual(self.stats_httpd.running, False) self.assertEqual(self.stats_httpd.poll_intval, 0.5) - self.assertEqual(self.stats_httpd.httpd, []) + self.assertNotEqual(len(self.stats_httpd.httpd), 0) self.assertEqual(type(self.stats_httpd.mccs), isc.config.ModuleCCSession) self.assertEqual(type(self.stats_httpd.cc_session), isc.cc.Session) self.assertEqual(len(self.stats_httpd.config), 2) @@ -321,144 +311,164 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual(len(self.stats_httpd.config['listen_on']), 1) self.assertTrue('address' in self.stats_httpd.config['listen_on'][0]) self.assertTrue('port' in self.stats_httpd.config['listen_on'][0]) - self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + self.assertTrue(server_address in set(self.stats_httpd.http_addrs)) + self.stats_httpd.stop() def test_openclose_mccs(self): - statshttpd = stats_httpd.StatsHttpd() - statshttpd.close_mccs() - self.assertEqual(statshttpd.mccs, None) - statshttpd.open_mccs() - self.assertIsNotNone(statshttpd.mccs) - statshttpd.mccs = None - self.assertEqual(statshttpd.mccs, None) - self.assertEqual(statshttpd.close_mccs(), None) + self.stats_httpd = MyStatsHttpd(get_availaddr()) + self.stats_httpd.close_mccs() + self.assertEqual(self.stats_httpd.mccs, None) + self.stats_httpd.open_mccs() + self.assertIsNotNone(self.stats_httpd.mccs) + self.stats_httpd.mccs = None + self.assertEqual(self.stats_httpd.mccs, None) + self.assertEqual(self.stats_httpd.close_mccs(), None) + self.stats_httpd.stop() def test_mccs(self): + self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertIsNotNone(self.stats_httpd.mccs.get_socket()) self.assertTrue( isinstance(self.stats_httpd.mccs.get_socket(), socket.socket)) self.assertTrue( isinstance(self.stats_httpd.cc_session, isc.cc.session.Session)) - self.statistics_spec = self.stats_httpd.get_stats_spec() + statistics_spec = self.stats_httpd.get_stats_spec() for mod in DUMMY_DATA: - self.assertTrue(mod in self.statistics_spec) - for cfg in self.statistics_spec[mod]: + self.assertTrue(mod in statistics_spec) + for cfg in statistics_spec[mod]: self.assertTrue('item_name' in cfg) self.assertTrue(cfg['item_name'] in DUMMY_DATA[mod]) - self.assertTrue(len(self.statistics_spec[mod]), len(DUMMY_DATA[mod])) + self.assertTrue(len(statistics_spec[mod]), len(DUMMY_DATA[mod])) self.stats_httpd.close_mccs() self.assertIsNone(self.stats_httpd.mccs) + self.stats_httpd.stop() def test_httpd(self): # dual stack (addresses is ipv4 and ipv6) if self.ipv6_enabled: - self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) - self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ] - self.assertTrue( - stats_httpd.HttpServer.address_family in set([socket.AF_INET, socket.AF_INET6])) - self.stats_httpd.open_httpd() + server_addresses = (get_availaddr('::1'), get_availaddr()) + self.stats_httpd = MyStatsHttpd(*server_addresses) for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) + self.assertTrue(ht.address_family in set([socket.AF_INET, socket.AF_INET6])) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + self.stats_httpd.stop() # dual stack (address is ipv6) if self.ipv6_enabled: - self.stats_httpd.http_addrs = [ ('::1', 8000) ] - self.stats_httpd.open_httpd() + server_addresses = get_availaddr('::1') + self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) + self.assertEqual(ht.address_family, socket.AF_INET6) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + self.stats_httpd.stop() # dual stack (address is ipv4) if self.ipv6_enabled: - self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] - self.stats_httpd.open_httpd() + server_addresses = get_availaddr() + self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) + self.assertEqual(ht.address_family, socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + self.stats_httpd.stop() # only-ipv4 single stack if not self.ipv6_enabled: - self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] - self.stats_httpd.open_httpd() + server_addresses = get_availaddr() + self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) + self.assertEqual(ht.address_family, socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + self.stats_httpd.stop() - # only-ipv4 single stack (force set ipv6 ) - if not self.ipv6_enabled: - self.stats_httpd.http_addrs = [ ('::1', 8000) ] - self.assertRaises(stats_httpd.HttpServerError, - self.stats_httpd.open_httpd) + # any address (IPv4) + server_addresses = get_availaddr(address='0.0.0.0') + self.stats_httpd = MyStatsHttpd(server_addresses) + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) + self.assertEqual(ht.address_family,socket.AF_INET) + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.stop() + + # any address (IPv6) + if self.ipv6_enabled: + server_addresses = get_availaddr(address='::') + self.stats_httpd = MyStatsHttpd(server_addresses) + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) + self.assertEqual(ht.address_family,socket.AF_INET6) + self.assertTrue(isinstance(ht.socket, socket.socket)) + self.stats_httpd.stop() # hostname - self.stats_httpd.http_addrs = [ ('localhost', 8000) ] - self.stats_httpd.open_httpd() + server_addresses = get_availaddr(address='localhost') + self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) + self.assertTrue(ht.address_family in set([socket.AF_INET, socket.AF_INET6])) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.close_httpd() + self.stats_httpd.stop() - self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ] - self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - self.assertEqual(type(self.stats_httpd.httpd), list) - self.assertEqual(len(self.stats_httpd.httpd), 0) - self.stats_httpd.close_httpd() + # nonexistent hostname + self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('my.host.domain', 8000)) # over flow of port number - self.stats_httpd.http_addrs = [ ('', 80000) ] - self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('127.0.0.1', 80000)) # negative - self.stats_httpd.http_addrs = [ ('', -8000) ] - self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('127.0.0.1', -8000)) # alphabet - self.stats_httpd.http_addrs = [ ('', 'ABCDE') ] - self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('127.0.0.1', 'ABCDE')) # Address already in use - self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.statshttpd_server.server.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) - self.statshttpd_server.run() - self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]}) - self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) - self.statshttpd_server.shutdown() + server_addresses = get_availaddr() + self.stats_httpd_server = ThreadingServerManager(MyStatsHttpd, server_addresses) + self.stats_httpd_server.run() + self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, server_addresses) + self.stats_httpd_server.shutdown() def test_running(self): + self.stats_httpd_server = ThreadingServerManager(MyStatsHttpd, get_availaddr()) + self.stats_httpd = self.stats_httpd_server.server self.assertFalse(self.stats_httpd.running) - self.statshttpd_server = ThreadingServerManager(MyStatsHttpd) - self.stats_httpd = self.statshttpd_server.server - self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65455 }]}) - self.statshttpd_server.run() + self.stats_httpd_server.run() self.assertTrue(self.stats_httpd.running) - self.statshttpd_server.shutdown() + send_shutdown("StatsHttpd") self.assertFalse(self.stats_httpd.running) + self.stats_httpd_server.shutdown() # failure case - self.stats_httpd = stats_httpd.StatsHttpd() + self.stats_httpd = MyStatsHttpd(get_availaddr()) self.stats_httpd.cc_session.close() - self.assertRaises( - isc.cc.session.SessionError, self.stats_httpd.start) + self.assertRaises(ValueError, self.stats_httpd.start) + self.stats_httpd.stop() - def test_select_failure(self): + def test_select_failure1(self): def raise_select_except(*args): raise select.error('dummy error') - def raise_select_except_with_errno(*args): - raise select.error(errno.EINTR) - (address, port) = ('127.0.0.1', 65456) + orig_select = stats_httpd.select.select stats_httpd.select.select = raise_select_except - statshttpd = stats_httpd.StatsHttpd() - statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - self.assertRaises(select.error, statshttpd.start) - statshttpd.stop() - stats_httpd.select.select = raise_select_except_with_errno - statshttpd_server = ThreadingServerManager(MyStatsHttpd) - statshttpd = statshttpd_server.server - statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]}) - statshttpd_server.run() - statshttpd_server.shutdown() + self.stats_httpd = MyStatsHttpd(get_availaddr()) + self.assertRaises(select.error, self.stats_httpd.start) + self.stats_httpd.stop() + stats_httpd.select.select = orig_select + + def test_select_failure2(self): + def raise_select_except(*args): + raise select.error(errno.EINTR) + orig_select = stats_httpd.select.select + stats_httpd.select.select = raise_select_except + self.stats_httpd_server = ThreadingServerManager(MyStatsHttpd, get_availaddr()) + self.stats_httpd_server.run() + self.stats_httpd_server.shutdown() + stats_httpd.select.select = orig_select def test_open_template(self): + self.stats_httpd = MyStatsHttpd(get_availaddr()) # successful conditions tmpl = self.stats_httpd.open_template(stats_httpd.XML_TEMPLATE_LOCATION) self.assertTrue(isinstance(tmpl, string.Template)) @@ -490,8 +500,10 @@ class TestStatsHttpd(unittest.TestCase): self.assertRaises( IOError, self.stats_httpd.open_template, '/path/to/foo/bar') + self.stats_httpd.stop() def test_commands(self): + self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertEqual(self.stats_httpd.command_handler("status", None), isc.config.ccsession.create_answer( 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")")) @@ -504,75 +516,81 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None), isc.config.ccsession.create_answer( 1, "Unknown command: __UNKNOWN_COMMAND__")) + self.stats_httpd.stop() def test_config(self): + self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertEqual( self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)), isc.config.ccsession.create_answer( - 1, "Unknown known config: _UNKNOWN_KEY_")) + 1, "unknown item _UNKNOWN_KEY_")) + addresses = get_availaddr() self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="127.0.0.1",port=8000)])), + dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "127.0.0.1") - self.assertTrue(addr["port"] == 8000) + self.assertTrue(addr["address"] == addresses[0]) + self.assertTrue(addr["port"] == addresses[1]) if self.ipv6_enabled: + addresses = get_availaddr("::1") self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="::1",port=8000)])), + dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "::1") - self.assertTrue(addr["port"] == 8000) + self.assertTrue(addr["address"] == addresses[0]) + self.assertTrue(addr["port"] == addresses[1]) + addresses = get_availaddr() self.assertEqual( self.stats_httpd.config_handler( - dict(listen_on=[dict(address="127.0.0.1",port=54321)])), + dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) - self.assertTrue(addr["address"] == "127.0.0.1") - self.assertTrue(addr["port"] == 54321) + self.assertTrue(addr["address"] == addresses[0]) + self.assertTrue(addr["port"] == addresses[1]) (ret, arg) = isc.config.ccsession.parse_answer( self.stats_httpd.config_handler( dict(listen_on=[dict(address="1.2.3.4",port=543210)])) ) self.assertEqual(ret, 1) + self.stats_httpd.stop() def test_xml_handler(self): - orig_get_stats_data = stats_httpd.StatsHttpd.get_stats_data - stats_httpd.StatsHttpd.get_stats_data = lambda x: \ + self.stats_httpd = MyStatsHttpd(get_availaddr()) + self.stats_httpd.get_stats_data = lambda: \ { 'Dummy' : { 'foo':'bar' } } - xml_body1 = stats_httpd.StatsHttpd().open_template( + xml_body1 = self.stats_httpd.open_template( stats_httpd.XML_TEMPLATE_LOCATION).substitute( xml_string='bar', xsd_namespace=stats_httpd.XSD_NAMESPACE, xsd_url_path=stats_httpd.XSD_URL_PATH, xsl_url_path=stats_httpd.XSL_URL_PATH) - xml_body2 = stats_httpd.StatsHttpd().xml_handler() + xml_body2 = self.stats_httpd.xml_handler() self.assertEqual(type(xml_body1), str) self.assertEqual(type(xml_body2), str) self.assertEqual(xml_body1, xml_body2) - stats_httpd.StatsHttpd.get_stats_data = lambda x: \ + self.stats_httpd.get_stats_data = lambda: \ { 'Dummy' : {'bar':'foo'} } - xml_body2 = stats_httpd.StatsHttpd().xml_handler() + xml_body2 = self.stats_httpd.xml_handler() self.assertNotEqual(xml_body1, xml_body2) - stats_httpd.StatsHttpd.get_stats_data = orig_get_stats_data + self.stats_httpd.stop() def test_xsd_handler(self): - orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec - stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ + self.stats_httpd = MyStatsHttpd(get_availaddr()) + self.stats_httpd.get_stats_spec = lambda: \ { "Dummy" : [{ "item_name": "foo", @@ -583,7 +601,7 @@ class TestStatsHttpd(unittest.TestCase): "item_title": "Foo" }] } - xsd_body1 = stats_httpd.StatsHttpd().open_template( + xsd_body1 = self.stats_httpd.open_template( stats_httpd.XSD_TEMPLATE_LOCATION).substitute( xsd_string=\ '' \ @@ -593,11 +611,11 @@ class TestStatsHttpd(unittest.TestCase): + '' \ + '', xsd_namespace=stats_httpd.XSD_NAMESPACE) - xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() + xsd_body2 = self.stats_httpd.xsd_handler() self.assertEqual(type(xsd_body1), str) self.assertEqual(type(xsd_body2), str) self.assertEqual(xsd_body1, xsd_body2) - stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ + self.stats_httpd.get_stats_spec = lambda: \ { "Dummy" : [{ "item_name": "bar", @@ -608,13 +626,13 @@ class TestStatsHttpd(unittest.TestCase): "item_title": "bar" }] } - xsd_body2 = stats_httpd.StatsHttpd().xsd_handler() + xsd_body2 = self.stats_httpd.xsd_handler() self.assertNotEqual(xsd_body1, xsd_body2) - stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec + self.stats_httpd.stop() def test_xsl_handler(self): - orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec - stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ + self.stats_httpd = MyStatsHttpd(get_availaddr()) + self.stats_httpd.get_stats_spec = lambda: \ { "Dummy" : [{ "item_name": "foo", @@ -625,7 +643,7 @@ class TestStatsHttpd(unittest.TestCase): "item_title": "Foo" }] } - xsl_body1 = stats_httpd.StatsHttpd().open_template( + xsl_body1 = self.stats_httpd.open_template( stats_httpd.XSL_TEMPLATE_LOCATION).substitute( xsl_string='' \ + '' \ @@ -633,11 +651,11 @@ class TestStatsHttpd(unittest.TestCase): + '' \ + '', xsd_namespace=stats_httpd.XSD_NAMESPACE) - xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() + xsl_body2 = self.stats_httpd.xsl_handler() self.assertEqual(type(xsl_body1), str) self.assertEqual(type(xsl_body2), str) self.assertEqual(xsl_body1, xsl_body2) - stats_httpd.StatsHttpd.get_stats_spec = lambda x: \ + self.stats_httpd.get_stats_spec = lambda: \ { "Dummy" : [{ "item_name": "bar", @@ -648,9 +666,9 @@ class TestStatsHttpd(unittest.TestCase): "item_title": "bar" }] } - xsl_body2 = stats_httpd.StatsHttpd().xsl_handler() + xsl_body2 = self.stats_httpd.xsl_handler() self.assertNotEqual(xsl_body1, xsl_body2) - stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec + self.stats_httpd.stop() def test_for_without_B10_FROM_SOURCE(self): # just lets it go through the code without B10_FROM_SOURCE env diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 7cf4f7ede0..c8065e0931 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -30,7 +30,7 @@ import imp import stats import isc.cc.session -from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, TIMEOUT_SEC +from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, send_shutdown # set test name for logger isc.log.init("b10-stats_test") @@ -189,27 +189,31 @@ class TestStats(unittest.TestCase): def test_start(self): # start without err - statsserver = ThreadingServerManager(MyStats) - statsd = statsserver.server - self.assertFalse(statsd.running) - statsserver.run() - self.assertTrue(statsd.running) - statsserver.shutdown() - self.assertFalse(statsd.running) + self.stats_server = ThreadingServerManager(MyStats) + self.stats = self.stats_server.server + self.assertFalse(self.stats.running) + self.stats_server.run() + self.assertTrue(self.stats.running) + send_shutdown("Stats") + self.assertFalse(self.stats.running) + self.stats_server.shutdown() # start with err - statsd = stats.Stats() - statsd.update_statistics_data = lambda x,**y: ['an error'] - self.assertRaises(stats.StatsError, statsd.start) + self.stats = stats.Stats() + self.stats.update_statistics_data = lambda x,**y: ['an error'] + self.assertRaises(stats.StatsError, self.stats.start) def test_handlers(self): + self.stats_server = ThreadingServerManager(MyStats) + self.stats = self.stats_server.server + self.stats_server.run() # config_handler self.assertEqual(self.stats.config_handler({'foo':'bar'}), isc.config.create_answer(0)) # command_handler - statsserver = ThreadingServerManager(MyStats) - statsserver.run() + self.base.boss.server._started.wait() + self.base.boss.server._started.clear() self.assertEqual( send_command( 'show', 'Stats', @@ -279,7 +283,7 @@ class TestStats(unittest.TestCase): send_command('__UNKNOWN__', 'Stats'), (1, "Unknown command: '__UNKNOWN__'")) - statsserver.shutdown() + self.stats_server.shutdown() def test_update_modules(self): self.assertEqual(len(self.stats.modules), 0) From 5ddc441f77a34158039f0328c3ab7c2106b7b3b8 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 22 Aug 2011 18:18:47 +0900 Subject: [PATCH 921/974] [1175] modify test_utils.py - don't use time.sleep for waiting threads are starting or finishing - correct shutting down of mock modules - use _started (threading.Event) where command_handler is invoked - add implementation to changing contents of specfile of MyStatsHttpd - set "BIND10_MSGQ_SOCKET_FILE" only when it's not set yet --- src/bin/stats/tests/test_utils.py | 89 ++++++++++++++++++------------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index e79db48951..e3c8cdcdc5 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -8,22 +8,21 @@ import time import sys import threading import tempfile +import json import msgq import isc.config.cfgmgr import stats import stats_httpd -# TODO: consider appropriate timeout seconds -TIMEOUT_SEC = 0.05 - -def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=TIMEOUT_SEC): - if not session: - cc_session = isc.cc.Session() - else: +def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=None): + if session is not None: cc_session = session - orig_timeout = cc_session.get_timeout() - cc_session.set_timeout(timeout * 1000) + else: + cc_session = isc.cc.Session() + if timeout is not None: + orig_timeout = cc_session.get_timeout() + cc_session.set_timeout(timeout * 1000) command = isc.config.ccsession.create_command(command_name, params) seq = cc_session.group_sendmsg(command, module_name) try: @@ -33,38 +32,35 @@ def send_command(command_name, module_name, params=None, session=None, nonblock= except isc.cc.SessionTimeout: pass finally: - if not session: - cc_session.close() - else: + if timeout is not None: cc_session.set_timeout(orig_timeout) + if session is None: + cc_session.close() -def send_shutdown(module_name): - return send_command("shutdown", module_name) +def send_shutdown(module_name, **kwargs): + return send_command("shutdown", module_name, **kwargs) class ThreadingServerManager: - def __init__(self, server_class): - self.server_class = server_class - self.server_class_name = server_class.__name__ - self.server = self.server_class() + def __init__(self, server, *args, **kwargs): + self.server = server(*args, **kwargs) + self.server_name = server.__name__ self.server._thread = threading.Thread( - name=self.server_class_name, target=self.server.run) + name=self.server_name, target=self.server.run) self.server._thread.daemon = True def run(self): self.server._thread.start() self.server._started.wait() self.server._started.clear() - # waiting for the server's being ready for listening - time.sleep(TIMEOUT_SEC) def shutdown(self): self.server.shutdown() - self.server._thread.join(TIMEOUT_SEC) + self.server._thread.join(0) # timeout is 0 class MockMsgq: def __init__(self): self._started = threading.Event() - self.msgq = msgq.MsgQ(None) + self.msgq = msgq.MsgQ(verbose=True) result = self.msgq.setup() if result: sys.exit("Error on Msgq startup: %s" % result) @@ -72,11 +68,14 @@ class MockMsgq: def run(self): self._started.set() try: + # any message is written to /dev/null + sys.stderr = open(os.devnull, "w") self.msgq.run() + sys.stderr.close() except Exception: pass finally: - self.shutdown() + self.msgq.shutdown() def shutdown(self): self.msgq.shutdown() @@ -90,10 +89,7 @@ class MockCfgmgr: def run(self): self._started.set() - try: - self.cfgmgr.run() - finally: - self.shutdown() + self.cfgmgr.run() def shutdown(self): self.cfgmgr.running = False @@ -155,6 +151,7 @@ class MockBoss: return isc.config.create_answer(0) def command_handler(self, command, *args, **kwargs): + self._started.set() self.got_command_name = command if command == 'sendstats': params = { "owner": "Boss", @@ -244,29 +241,47 @@ class MyStats(stats.Stats): def run(self): self._started.set() - stats.Stats.start(self) + self.start() def shutdown(self): - send_shutdown("Stats") + self.command_shutdown() class MyStatsHttpd(stats_httpd.StatsHttpd): - def __init__(self): + ORIG_SPECFILE_LOCATION = stats_httpd.SPECFILE_LOCATION + def __init__(self, *server_address): self._started = threading.Event() - stats_httpd.StatsHttpd.__init__(self) + if server_address: + stats_httpd.SPECFILE_LOCATION = self.get_specfile(*server_address) + try: + stats_httpd.StatsHttpd.__init__(self) + finally: + stats_httpd.SPECFILE_LOCATION.close() + stats_httpd.SPECFILE_LOCATION = self.ORIG_SPECFILE_LOCATION + else: + stats_httpd.StatsHttpd.__init__(self) + + def get_specfile(self, *server_address): + spec = json.load(open(self.ORIG_SPECFILE_LOCATION)) + config = spec['module_spec']['config_data'] + for i in range(len(config)): + if config[i]['item_name'] == 'listen_on': + config[i]['item_default'] = \ + [ dict(address=a[0], port=a[1]) for a in server_address ] + break + return io.StringIO(json.dumps(spec)) def run(self): self._started.set() - stats_httpd.StatsHttpd.start(self) + self.start() def shutdown(self): - send_shutdown("StatsHttpd") + self.stop() class BaseModules: def __init__(self): - self.class_name = BaseModules.__name__ - # Change value of BIND10_MSGQ_SOCKET_FILE in environment variables - os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='unix_socket.') + if 'BIND10_MSGQ_SOCKET_FILE' not in os.environ: + os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='msgq_socket_') # MockMsgq self.msgq = ThreadingServerManager(MockMsgq) self.msgq.run() From d6b86a88c7a486f2e5b742fc60d374e48382320e Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 22 Aug 2011 18:20:39 +0900 Subject: [PATCH 922/974] [1175] add #1175 --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index b0b2aea21f..d99bcbea00 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +291. [func] naokikambe + Statistics items are specified by each module's spec file. + Stats module can read these through the config manager. Stats + module and stats httpd report statistics data and statistics + schema by each module via both bindctl and HTTP/XML. + (Trac #928,#929,#930,#1175, git TBD) + 290. [func] jinmei libdns++/pydnspp: added an option parameter to the "from wire" methods of the Message class. One option is defined, From f159ac66aa577889514dc170c87a92c49be5a6cc Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 23 Aug 2011 11:22:03 +0900 Subject: [PATCH 923/974] [1175] set msgq verbose off --- src/bin/stats/tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index e3c8cdcdc5..44cf7c5ec3 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -60,7 +60,7 @@ class ThreadingServerManager: class MockMsgq: def __init__(self): self._started = threading.Event() - self.msgq = msgq.MsgQ(verbose=True) + self.msgq = msgq.MsgQ(verbose=False) result = self.msgq.setup() if result: sys.exit("Error on Msgq startup: %s" % result) From 9daa2f686b3bdb03b13e9becf45a722344888cf3 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 23 Aug 2011 16:42:18 +0900 Subject: [PATCH 924/974] [1175] modify stats_httpd.py.in and b10-stats-httpd_test.py - A hostname (canonical name of host) is not acceptable in listen_on configuration. - A default port number(starting number for search) is added in args of the function get_availaddr. --- src/bin/stats/stats_httpd.py.in | 8 +++++-- src/bin/stats/tests/b10-stats-httpd_test.py | 24 ++++++++++----------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index da1ee3bda6..e099a5f4a3 100644 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -209,8 +209,12 @@ class StatsHttpd: httpd = None try: # get address family for the server_address before - # creating HttpServer object - address_family = socket.getaddrinfo(*server_address)[0][0] + # creating HttpServer object. If a specified address is + # not numerical, gaierror may be thrown. + address_family = socket.getaddrinfo( + server_address[0], server_address[1], 0, + socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST + )[0][0] HttpServer.address_family = address_family httpd = HttpServer( server_address, HttpHandler, diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 79631f4c76..de074bf475 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -58,10 +58,11 @@ DUMMY_DATA = { } } -def get_availaddr(address='127.0.0.1'): - """returns tuple of address and port available on the - platform. default range of port is from 65535 to 50000""" - for port in range(65535, 50000, -1): +def get_availaddr(address='127.0.0.1', port=8001): + """returns tuple of address and port available to listen on the + platform. Default port range is between 8001 and 65535. If port is + over flow(greater than 65535), OverflowError is thrown""" + while True: try: if is_ipv6_enabled(address): sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) @@ -71,7 +72,9 @@ def get_availaddr(address='127.0.0.1'): sock.close() return (address, port) except socket.error: - pass + # This address and port number are already in use. + # next port number is added + port = port + 1 def is_ipv6_enabled(address='::1', port=8000): """checks IPv6 enabled on the platform""" @@ -403,14 +406,9 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue(isinstance(ht.socket, socket.socket)) self.stats_httpd.stop() - # hostname - server_addresses = get_availaddr(address='localhost') - self.stats_httpd = MyStatsHttpd(server_addresses) - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) - self.assertTrue(ht.address_family in set([socket.AF_INET, socket.AF_INET6])) - self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.stop() + # existent hostname + self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, + get_availaddr(address='localhost')) # nonexistent hostname self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('my.host.domain', 8000)) From 0aa4c14ebd1eb0a68c2bcf5c617325596657ea71 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 24 Aug 2011 15:55:21 +0900 Subject: [PATCH 925/974] [1175] fix conflicts with trac519 --- src/bin/bind10/bind10_src.py.in | 23 +++++++++++++---------- src/bin/stats/stats.py.in | 29 +++++++++++++++++++---------- src/bin/stats/tests/test_utils.py | 15 +++++++++------ 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index da7fa0ec8a..d084dfb092 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -308,9 +308,11 @@ class BoB: return process_list def _get_stats_data(self): - return { "stats_data": { - 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) - }} + return { "owner": "Boss", + "data": { 'boot_time': + time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + } + } def command_handler(self, command, args): logger.debug(DBG_COMMANDS, BIND10_RECEIVED_COMMAND, command) @@ -325,17 +327,18 @@ class BoB: answer = isc.config.ccsession.create_answer(0, self._get_stats_data()) elif command == "sendstats": # send statistics data to the stats daemon immediately - statistics_data = { - 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) - } + stats_data = self._get_stats_data() valid = self.ccs.get_module_spec().validate_statistics( - True, statistics_data) + True, stats_data["data"]) if valid: cmd = isc.config.ccsession.create_command( - 'set', { "owner": "Boss", - "data": statistics_data }) + 'set', stats_data }) seq = self.cc_session.group_sendmsg(cmd, 'Stats') - self.cc_session.group_recvmsg(True, seq) + # Consume the answer, in case it becomes a orphan message. + try: + self.cc_session.group_recvmsg(False, seq) + except isc.cc.session.SessionTimeout: + pass answer = isc.config.ccsession.create_answer(0) else: logger.fatal(BIND10_INVALID_STATISTICS_DATA); diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index a8aef4c0ec..1beebf51f8 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -145,14 +145,6 @@ class Stats: raise StatsError(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"]) self.mccs.start() - def _update_stats_data(self, args): - # 'args' must be dictionary type - if isinstance(args, dict) and isinstance(args.get('stats_data'), dict): - self.stats_data.update(args['stats_data']) - - # overwrite "stats.LastUpdateTime" - self.stats_data['stats.last_update_time'] = get_datetime() - def start(self): """ Start stats module @@ -162,9 +154,26 @@ class Stats: # request Bob to send statistics data logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS) - cmd = isc.config.ccsession.create_command("sendstats", None) + cmd = isc.config.ccsession.create_command("getstats", None) seq = self.cc_session.group_sendmsg(cmd, 'Boss') - self.cc_session.group_recvmsg(True, seq) + try: + answer, env = self.cc_session.group_recvmsg(False, seq) + if answer: + rcode, args = isc.config.ccsession.parse_answer(answer) + if rcode == 0: + errors = self.update_statistics_data( + args["owner"], **args["data"]) + if errors: + raise StatsError("boss spec file is incorrect: " + + ", ".join(errors)) + errors = self.update_statistics_data( + self.module_name, + last_update_time=get_datetime()) + if errors: + raise StatsError("stats spec file is incorrect: " + + ", ".join(errors)) + except isc.cc.session.SessionTimeout: + pass # initialized Statistics data errors = self.update_statistics_data( diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index 44cf7c5ec3..747b74955f 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -153,13 +153,16 @@ class MockBoss: def command_handler(self, command, *args, **kwargs): self._started.set() self.got_command_name = command + params = { "owner": "Boss", + "data": { + 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', self._BASETIME) + } + } if command == 'sendstats': - params = { "owner": "Boss", - "data": { - 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', self._BASETIME) - } - } - return send_command("set", "Stats", params=params, session=self.cc_session) + send_command("set", "Stats", params=params, session=self.cc_session) + return isc.config.create_answer(0) + elif command == 'getstats': + return isc.config.create_answer(0, params) return isc.config.create_answer(1, "Unknown Command") class MockAuth: From 06f7bc4b3b69e8fda96f6e626a7dac5b1fbbb233 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 24 Aug 2011 16:51:11 +0900 Subject: [PATCH 926/974] [1175] deadlock will be killed afer 20 secs --- src/bin/stats/tests/b10-stats-httpd_test.py | 19 +++++++++++++++++++ src/bin/stats/tests/b10-stats_test.py | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index de074bf475..bf4c33fe18 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -32,6 +32,7 @@ import time import threading import http.client import xml.etree.ElementTree +import signal import isc import stats_httpd @@ -89,6 +90,9 @@ def is_ipv6_enabled(address='::1', port=8000): class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" def setUp(self): + # deadlock will be killed afer 20 secs + signal.signal(signal.SIGALRM, self.my_signal_handler) + signal.alarm(20) self.base = BaseModules() self.stats_server = ThreadingServerManager(MyStats) self.stats = self.stats_server.server @@ -107,6 +111,9 @@ class TestHttpHandler(unittest.TestCase): self.stats_server.shutdown() self.base.shutdown() + def my_signal_handler(self, signal, frame): + self.fail("A deadlock might be detected") + def test_do_GET(self): self.assertTrue(type(self.stats_httpd.httpd) is list) self.assertEqual(len(self.stats_httpd.httpd), 1) @@ -265,11 +272,17 @@ class TestHttpServerError(unittest.TestCase): class TestHttpServer(unittest.TestCase): """Tests for HttpServer class""" def setUp(self): + # deadlock will be killed afer 20 secs + signal.signal(signal.SIGALRM, self.my_signal_handler) + signal.alarm(20) self.base = BaseModules() def tearDown(self): self.base.shutdown() + def my_signal_handler(self, signal, frame): + self.fail("A deadlock might be detected") + def test_httpserver(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertEqual(type(self.stats_httpd.httpd), list) @@ -291,6 +304,9 @@ class TestStatsHttpd(unittest.TestCase): """Tests for StatsHttpd class""" def setUp(self): + # deadlock will be killed afer 20 secs + signal.signal(signal.SIGALRM, self.my_signal_handler) + signal.alarm(20) self.base = BaseModules() self.stats_server = ThreadingServerManager(MyStats) self.stats_server.run() @@ -301,6 +317,9 @@ class TestStatsHttpd(unittest.TestCase): self.stats_server.shutdown() self.base.shutdown() + def my_signal_handler(self, signal, frame): + self.fail("A deadlock might be detected") + def test_init(self): server_address = get_availaddr() self.stats_httpd = MyStatsHttpd(server_address) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index c8065e0931..7817d2915e 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -27,6 +27,7 @@ import threading import io import time import imp +import signal import stats import isc.cc.session @@ -145,6 +146,9 @@ class TestCallback(unittest.TestCase): class TestStats(unittest.TestCase): def setUp(self): + # deadlock will be killed afer 20 secs + signal.signal(signal.SIGALRM, self.my_signal_handler) + signal.alarm(20) self.base = BaseModules() self.stats = stats.Stats() self.const_timestamp = 1308730448.965706 @@ -154,6 +158,9 @@ class TestStats(unittest.TestCase): def tearDown(self): self.base.shutdown() + def my_signal_handler(self, signal, frame): + self.fail("A deadlock might be detected") + def test_init(self): self.assertEqual(self.stats.module_name, 'Stats') self.assertFalse(self.stats.running) From e57c5196d3e8dd56b0190799c98b56a5be55333a Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 24 Aug 2011 17:28:29 +0900 Subject: [PATCH 927/974] [1175] fix typo and correct changes from trac519 --- src/bin/bind10/bind10_src.py.in | 3 +-- src/bin/bind10/tests/bind10_test.py.in | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index d084dfb092..1687cb1655 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -331,8 +331,7 @@ class BoB: valid = self.ccs.get_module_spec().validate_statistics( True, stats_data["data"]) if valid: - cmd = isc.config.ccsession.create_command( - 'set', stats_data }) + cmd = isc.config.ccsession.create_command('set', stats_data) seq = self.cc_session.group_sendmsg(cmd, 'Stats') # Consume the answer, in case it becomes a orphan message. try: diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index e002087052..2efd940aa2 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -168,8 +168,9 @@ class TestBoB(unittest.TestCase): # "getstats" command self.assertEqual(bob.command_handler("getstats", None), isc.config.ccsession.create_answer(0, - { "stats_data": { - 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) + { "owner": "Boss", + "data": { + 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) }})) # "sendstats" command self.assertEqual(bob.command_handler("sendstats", None), From c8bbdd1d74ac313d8b57d8debe4f7b75490e5df2 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 26 Aug 2011 12:01:47 +0900 Subject: [PATCH 928/974] [1175] fix wrong list-type handling in the function get_spec_defaults and add more tests into test_get_spec_defaults --- src/bin/stats/stats.py.in | 2 +- src/bin/stats/tests/b10-stats_test.py | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 1beebf51f8..7cedfbfe07 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -88,7 +88,7 @@ def get_spec_defaults(spec): elif item_type == "list": return spec.get( "item_default", - [ _get_spec_defaults(s) for s in spec["list_item_spec"] ]) + [ _get_spec_defaults(spec["list_item_spec"]) ]) elif item_type == "map": return spec.get( "item_default", diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 7817d2915e..892032dca7 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -43,9 +43,7 @@ class TestUtilties(unittest.TestCase): { 'item_name': 'test_bool1', 'item_type': 'boolean', 'item_default': True }, { 'item_name': 'test_str1', 'item_type': 'string', 'item_default': 'ABCD' }, { 'item_name': 'test_list1', 'item_type': 'list', 'item_default': [1,2,3], - 'list_item_spec' : [ { 'item_name': 'one', 'item_type': 'integer' }, - { 'item_name': 'two', 'item_type': 'integer' }, - { 'item_name': 'three', 'item_type': 'integer' } ] }, + 'list_item_spec' : { 'item_name': 'number', 'item_type': 'integer' } }, { 'item_name': 'test_map1', 'item_type': 'map', 'item_default': {'a':1,'b':2,'c':3}, 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'integer'}, { 'item_name': 'b', 'item_type': 'integer'}, @@ -55,14 +53,18 @@ class TestUtilties(unittest.TestCase): { 'item_name': 'test_bool2', 'item_type': 'boolean' }, { 'item_name': 'test_str2', 'item_type': 'string' }, { 'item_name': 'test_list2', 'item_type': 'list', - 'list_item_spec' : [ { 'item_name': 'one', 'item_type': 'integer' }, - { 'item_name': 'two', 'item_type': 'integer' }, - { 'item_name': 'three', 'item_type': 'integer' } ] }, + 'list_item_spec' : { 'item_name': 'number', 'item_type': 'integer' } }, { 'item_name': 'test_map2', 'item_type': 'map', 'map_item_spec' : [ { 'item_name': 'A', 'item_type': 'integer'}, { 'item_name': 'B', 'item_type': 'integer'}, { 'item_name': 'C', 'item_type': 'integer'} ] }, - { 'item_name': 'test_none', 'item_type': 'none' } + { 'item_name': 'test_none', 'item_type': 'none' }, + { 'item_name': 'test_list3', 'item_type': 'list', 'item_default': ["one","two","three"], + 'list_item_spec' : { 'item_name': 'number', 'item_type': 'string' } }, + { 'item_name': 'test_map3', 'item_type': 'map', 'item_default': {'a':'one','b':'two','c':'three'}, + 'map_item_spec' : [ { 'item_name': 'a', 'item_type': 'string'}, + { 'item_name': 'b', 'item_type': 'string'}, + { 'item_name': 'c', 'item_type': 'string'} ] } ] def setUp(self): @@ -85,9 +87,11 @@ class TestUtilties(unittest.TestCase): 'test_real2' : 0.0, 'test_bool2' : False, 'test_str2' : "", - 'test_list2' : [0,0,0], + 'test_list2' : [0], 'test_map2' : { 'A' : 0, 'B' : 0, 'C' : 0 }, - 'test_none' : None }) + 'test_none' : None, + 'test_list3' : [ "one", "two", "three" ], + 'test_map3' : { 'a' : 'one', 'b' : 'two', 'c' : 'three' } }) self.assertEqual(stats.get_spec_defaults(None), {}) self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}]) From 433381e5ca62418fc90377d16f1805260b27b619 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 1 Sep 2011 04:19:53 +0000 Subject: [PATCH 929/974] [1175] modify b10-stats-httpd_test.py, b10-stats_test.py and test_utils.py - more strictly close the io object whether it's successfully opened or not - add verbosity=2 in unittest.main for debugging the failure in the buildbot - don't redict sys.stderr in MockMsgq - rename the function name to create_specfile - switch the verbose in Msgq into True --- src/bin/stats/tests/b10-stats-httpd_test.py | 35 ++++++++++++--------- src/bin/stats/tests/b10-stats_test.py | 2 +- src/bin/stats/tests/test_utils.py | 33 ++++++++++--------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index bf4c33fe18..f243ef4398 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -63,29 +63,34 @@ def get_availaddr(address='127.0.0.1', port=8001): """returns tuple of address and port available to listen on the platform. Default port range is between 8001 and 65535. If port is over flow(greater than 65535), OverflowError is thrown""" - while True: - try: - if is_ipv6_enabled(address): - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - else : - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.bind((address, port)) - sock.close() - return (address, port) - except socket.error: - # This address and port number are already in use. - # next port number is added - port = port + 1 + sock = None + if is_ipv6_enabled(address): + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + else: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + while True: + try: + sock.bind((address, port)) + return (address, port) + except socket.error: + # This address and port number are already in use. + # next port number is added + port = port + 1 + finally: + if sock: sock.close() def is_ipv6_enabled(address='::1', port=8000): """checks IPv6 enabled on the platform""" + sock = None try: sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) sock.bind((address, port)) - sock.close() return True except socket.error: return False + finally: + if sock: sock.close() class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" @@ -698,4 +703,4 @@ class TestStatsHttpd(unittest.TestCase): imp.reload(stats_httpd) if __name__ == "__main__": - unittest.main() + unittest.main(verbosity=2) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 892032dca7..919dc43bcb 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -603,7 +603,7 @@ class TestOSEnv(unittest.TestCase): imp.reload(stats) def test_main(): - unittest.main() + unittest.main(verbosity=2) if __name__ == "__main__": test_main() diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index 747b74955f..ff45440710 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -60,7 +60,7 @@ class ThreadingServerManager: class MockMsgq: def __init__(self): self._started = threading.Event() - self.msgq = msgq.MsgQ(verbose=False) + self.msgq = msgq.MsgQ(verbose=True) result = self.msgq.setup() if result: sys.exit("Error on Msgq startup: %s" % result) @@ -68,10 +68,7 @@ class MockMsgq: def run(self): self._started.set() try: - # any message is written to /dev/null - sys.stderr = open(os.devnull, "w") self.msgq.run() - sys.stderr.close() except Exception: pass finally: @@ -254,24 +251,30 @@ class MyStatsHttpd(stats_httpd.StatsHttpd): def __init__(self, *server_address): self._started = threading.Event() if server_address: - stats_httpd.SPECFILE_LOCATION = self.get_specfile(*server_address) + stats_httpd.SPECFILE_LOCATION = self.create_specfile(*server_address) try: stats_httpd.StatsHttpd.__init__(self) finally: - stats_httpd.SPECFILE_LOCATION.close() + if hasattr(stats_httpd.SPECFILE_LOCATION, "close"): + stats_httpd.SPECFILE_LOCATION.close() stats_httpd.SPECFILE_LOCATION = self.ORIG_SPECFILE_LOCATION else: stats_httpd.StatsHttpd.__init__(self) - def get_specfile(self, *server_address): - spec = json.load(open(self.ORIG_SPECFILE_LOCATION)) - config = spec['module_spec']['config_data'] - for i in range(len(config)): - if config[i]['item_name'] == 'listen_on': - config[i]['item_default'] = \ - [ dict(address=a[0], port=a[1]) for a in server_address ] - break - return io.StringIO(json.dumps(spec)) + def create_specfile(self, *server_address): + spec_io = open(self.ORIG_SPECFILE_LOCATION) + try: + spec = json.load(spec_io) + spec_io.close() + config = spec['module_spec']['config_data'] + for i in range(len(config)): + if config[i]['item_name'] == 'listen_on': + config[i]['item_default'] = \ + [ dict(address=a[0], port=a[1]) for a in server_address ] + break + return io.StringIO(json.dumps(spec)) + finally: + spec_io.close() def run(self): self._started.set() From d8cac904c7aea4a652a47afb35aceb6ca4808ce8 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 1 Sep 2011 11:34:28 +0000 Subject: [PATCH 930/974] [1175] add -v option in pycoverage for debugging the failure in buildbot. (unittest.main() with verbosity option is not supported in Python3.1.) --- src/bin/stats/tests/Makefile.am | 2 +- src/bin/stats/tests/b10-stats-httpd_test.py | 2 +- src/bin/stats/tests/b10-stats_test.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index b5edc592e4..7638f67188 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -23,7 +23,7 @@ endif PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests:$(abs_top_builddir)/src/bin/msgq:$(abs_top_builddir)/src/lib/python/isc/config \ B10_FROM_SOURCE=$(abs_top_srcdir) \ CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/tests/testdata \ - $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ + $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest -v || exit ; \ done CLEANDIRS = __pycache__ diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index f243ef4398..06ede85395 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -703,4 +703,4 @@ class TestStatsHttpd(unittest.TestCase): imp.reload(stats_httpd) if __name__ == "__main__": - unittest.main(verbosity=2) + unittest.main() diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 919dc43bcb..892032dca7 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -603,7 +603,7 @@ class TestOSEnv(unittest.TestCase): imp.reload(stats) def test_main(): - unittest.main(verbosity=2) + unittest.main() if __name__ == "__main__": test_main() From ddec42c7a23cca11903ece8f7ab614dcc7e5edd3 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 5 Sep 2011 04:46:08 +0000 Subject: [PATCH 931/974] [1175] remove -v option from pycoverage --- src/bin/stats/tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index 7638f67188..b5edc592e4 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -23,7 +23,7 @@ endif PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests:$(abs_top_builddir)/src/bin/msgq:$(abs_top_builddir)/src/lib/python/isc/config \ B10_FROM_SOURCE=$(abs_top_srcdir) \ CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/tests/testdata \ - $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest -v || exit ; \ + $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done CLEANDIRS = __pycache__ From 15f5d7895a2744376062229cf19593016a773cde Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 5 Sep 2011 04:53:11 +0000 Subject: [PATCH 932/974] [1175] modify b10-stats-httpd_test.py - remove a logging name from unittest - do stats_httpd.stop() in teadDown() instead of each test case - send 'shutdown' command to kill stats_httpd when testing address already in use --- src/bin/stats/tests/b10-stats-httpd_test.py | 27 ++++----------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 06ede85395..c76ab8a95c 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -39,9 +39,6 @@ import stats_httpd import stats from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, send_shutdown -# set test name for logger -isc.log.init("b10-stats-httpd_test") - DUMMY_DATA = { 'Boss' : { "boot_time": "2011-03-04T11:59:06Z" @@ -283,6 +280,8 @@ class TestHttpServer(unittest.TestCase): self.base = BaseModules() def tearDown(self): + if hasattr(self, "stats_httpd"): + self.stats_httpd.stop() self.base.shutdown() def my_signal_handler(self, signal, frame): @@ -294,7 +293,6 @@ class TestHttpServer(unittest.TestCase): self.assertEqual(len(self.stats_httpd.httpd), 1) for httpd in self.stats_httpd.httpd: self.assertTrue(isinstance(httpd, stats_httpd.HttpServer)) - self.stats_httpd.stop() class TestStatsHttpdError(unittest.TestCase): """Tests for StatsHttpdError exception""" @@ -319,6 +317,8 @@ class TestStatsHttpd(unittest.TestCase): self.ipv6_enabled = is_ipv6_enabled() def tearDown(self): + if hasattr(self, "stats_httpd"): + self.stats_httpd.stop() self.stats_server.shutdown() self.base.shutdown() @@ -339,7 +339,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue('address' in self.stats_httpd.config['listen_on'][0]) self.assertTrue('port' in self.stats_httpd.config['listen_on'][0]) self.assertTrue(server_address in set(self.stats_httpd.http_addrs)) - self.stats_httpd.stop() def test_openclose_mccs(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) @@ -350,7 +349,6 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd.mccs = None self.assertEqual(self.stats_httpd.mccs, None) self.assertEqual(self.stats_httpd.close_mccs(), None) - self.stats_httpd.stop() def test_mccs(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) @@ -368,7 +366,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue(len(statistics_spec[mod]), len(DUMMY_DATA[mod])) self.stats_httpd.close_mccs() self.assertIsNone(self.stats_httpd.mccs) - self.stats_httpd.stop() def test_httpd(self): # dual stack (addresses is ipv4 and ipv6) @@ -379,7 +376,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertTrue(ht.address_family in set([socket.AF_INET, socket.AF_INET6])) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.stop() # dual stack (address is ipv6) if self.ipv6_enabled: @@ -389,7 +385,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family, socket.AF_INET6) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.stop() # dual stack (address is ipv4) if self.ipv6_enabled: @@ -399,7 +394,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family, socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.stop() # only-ipv4 single stack if not self.ipv6_enabled: @@ -409,7 +403,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family, socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.stop() # any address (IPv4) server_addresses = get_availaddr(address='0.0.0.0') @@ -418,7 +411,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family,socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.stop() # any address (IPv6) if self.ipv6_enabled: @@ -428,7 +420,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family,socket.AF_INET6) self.assertTrue(isinstance(ht.socket, socket.socket)) - self.stats_httpd.stop() # existent hostname self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, @@ -451,7 +442,7 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd_server = ThreadingServerManager(MyStatsHttpd, server_addresses) self.stats_httpd_server.run() self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, server_addresses) - self.stats_httpd_server.shutdown() + send_shutdown("StatsHttpd") def test_running(self): self.stats_httpd_server = ThreadingServerManager(MyStatsHttpd, get_availaddr()) @@ -467,7 +458,6 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd = MyStatsHttpd(get_availaddr()) self.stats_httpd.cc_session.close() self.assertRaises(ValueError, self.stats_httpd.start) - self.stats_httpd.stop() def test_select_failure1(self): def raise_select_except(*args): @@ -476,7 +466,6 @@ class TestStatsHttpd(unittest.TestCase): stats_httpd.select.select = raise_select_except self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertRaises(select.error, self.stats_httpd.start) - self.stats_httpd.stop() stats_httpd.select.select = orig_select def test_select_failure2(self): @@ -522,7 +511,6 @@ class TestStatsHttpd(unittest.TestCase): self.assertRaises( IOError, self.stats_httpd.open_template, '/path/to/foo/bar') - self.stats_httpd.stop() def test_commands(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) @@ -538,7 +526,6 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None), isc.config.ccsession.create_answer( 1, "Unknown command: __UNKNOWN_COMMAND__")) - self.stats_httpd.stop() def test_config(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) @@ -588,7 +575,6 @@ class TestStatsHttpd(unittest.TestCase): dict(listen_on=[dict(address="1.2.3.4",port=543210)])) ) self.assertEqual(ret, 1) - self.stats_httpd.stop() def test_xml_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) @@ -608,7 +594,6 @@ class TestStatsHttpd(unittest.TestCase): { 'Dummy' : {'bar':'foo'} } xml_body2 = self.stats_httpd.xml_handler() self.assertNotEqual(xml_body1, xml_body2) - self.stats_httpd.stop() def test_xsd_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) @@ -650,7 +635,6 @@ class TestStatsHttpd(unittest.TestCase): } xsd_body2 = self.stats_httpd.xsd_handler() self.assertNotEqual(xsd_body1, xsd_body2) - self.stats_httpd.stop() def test_xsl_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) @@ -690,7 +674,6 @@ class TestStatsHttpd(unittest.TestCase): } xsl_body2 = self.stats_httpd.xsl_handler() self.assertNotEqual(xsl_body1, xsl_body2) - self.stats_httpd.stop() def test_for_without_B10_FROM_SOURCE(self): # just lets it go through the code without B10_FROM_SOURCE env From 7af1aeddc36a1ac1343f1af12aa29164f1028f03 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 5 Sep 2011 04:53:24 +0000 Subject: [PATCH 933/974] [1175] remove a logging name from unittest --- src/bin/stats/tests/b10-stats_test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 892032dca7..a52ce6325a 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -33,9 +33,6 @@ import stats import isc.cc.session from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, send_shutdown -# set test name for logger -isc.log.init("b10-stats_test") - class TestUtilties(unittest.TestCase): items = [ { 'item_name': 'test_int1', 'item_type': 'integer', 'item_default': 12345 }, From 9eafb04ee8dbd47022dd9a5e5c1310f88f398d2c Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 5 Sep 2011 05:01:18 +0000 Subject: [PATCH 934/974] [1175] modify test_utils.py - add 3-time retry to creating the server object when it fails in the ThreadingServerManager class - suppress outputs by Msgq, and add dummy sys module and the output methods - pass Exceptions raised while it's running with a thread --- src/bin/stats/tests/test_utils.py | 56 ++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index ff45440710..cb63abb099 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -42,7 +42,19 @@ def send_shutdown(module_name, **kwargs): class ThreadingServerManager: def __init__(self, server, *args, **kwargs): - self.server = server(*args, **kwargs) + self.server = None + n = 0 + while True: + try: + self.server = server(*args, **kwargs) + except isc.cc.session.SessionTimeout: + if self.server is not None: + self.server.shutdown() + # retrying until 3 times + if n >2: raise + n = n + 1 + continue + else: break self.server_name = server.__name__ self.server._thread = threading.Thread( name=self.server_name, target=self.server.run) @@ -57,10 +69,21 @@ class ThreadingServerManager: self.server.shutdown() self.server._thread.join(0) # timeout is 0 +def do_nothing(*args, **kwargs): pass + +class dummy_sys: + """Dummy for sys""" + class dummy_io: + write = do_nothing + stdout = stderr = dummy_io() + class MockMsgq: def __init__(self): self._started = threading.Event() - self.msgq = msgq.MsgQ(verbose=True) + # suppress output to stdout and stderr + msgq.sys = dummy_sys() + msgq.print = do_nothing + self.msgq = msgq.MsgQ(verbose=False) result = self.msgq.setup() if result: sys.exit("Error on Msgq startup: %s" % result) @@ -86,7 +109,10 @@ class MockCfgmgr: def run(self): self._started.set() - self.cfgmgr.run() + try: + self.cfgmgr.run() + except Exception: + pass def shutdown(self): self.cfgmgr.running = False @@ -138,8 +164,11 @@ class MockBoss: self.mccs.start() self.running = True self._started.set() - while self.running: - self.mccs.check_command(False) + try: + while self.running: + self.mccs.check_command(False) + except Exception: + pass def shutdown(self): self.running = False @@ -216,8 +245,11 @@ class MockAuth: self.mccs.start() self.running = True self._started.set() - while self.running: - self.mccs.check_command(False) + try: + while self.running: + self.mccs.check_command(False) + except Exception: + pass def shutdown(self): self.running = False @@ -241,7 +273,10 @@ class MyStats(stats.Stats): def run(self): self._started.set() - self.start() + try: + self.start() + except Exception: + pass def shutdown(self): self.command_shutdown() @@ -278,7 +313,10 @@ class MyStatsHttpd(stats_httpd.StatsHttpd): def run(self): self._started.set() - self.start() + try: + self.start() + except Exception: + pass def shutdown(self): self.stop() From 9accf90bb081b057023479f0a86e54017b02cdd3 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 5 Sep 2011 17:14:11 +0900 Subject: [PATCH 935/974] [1175] modify stats_httpd.py.in, b10-stats-httpd_test.py and b10-stats_test.py - The stats httpd doesn't need to return an argument when it's shutting down. - The testcase sends the 'status' command or the 'shutdown' command to the stats or the stats httpd when they started, and then their returned values are checked. --- src/bin/stats/stats_httpd.py.in | 3 +-- src/bin/stats/tests/b10-stats-httpd_test.py | 9 +++++---- src/bin/stats/tests/b10-stats_test.py | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index e099a5f4a3..e8f7f50fa1 100644 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -328,8 +328,7 @@ class StatsHttpd: logger.debug(DBG_STATHTTPD_MESSAGING, STATHTTPD_RECEIVED_SHUTDOWN_COMMAND) self.running = False - return isc.config.ccsession.create_answer( - 0, "Stats Httpd is shutting down.") + return isc.config.ccsession.create_answer(0) else: logger.debug(DBG_STATHTTPD_MESSAGING, STATHTTPD_RECEIVED_UNKNOWN_COMMAND, command) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index c76ab8a95c..526d5f2603 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -37,7 +37,7 @@ import signal import isc import stats_httpd import stats -from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, send_shutdown +from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, send_command, send_shutdown DUMMY_DATA = { 'Boss' : { @@ -449,8 +449,10 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd = self.stats_httpd_server.server self.assertFalse(self.stats_httpd.running) self.stats_httpd_server.run() + self.assertEqual(send_command("status", "StatsHttpd"), + (0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")")) self.assertTrue(self.stats_httpd.running) - send_shutdown("StatsHttpd") + self.assertEqual(send_shutdown("StatsHttpd"), (0, None)) self.assertFalse(self.stats_httpd.running) self.stats_httpd_server.shutdown() @@ -519,8 +521,7 @@ class TestStatsHttpd(unittest.TestCase): 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")")) self.stats_httpd.running = True self.assertEqual(self.stats_httpd.command_handler("shutdown", None), - isc.config.ccsession.create_answer( - 0, "Stats Httpd is shutting down.")) + isc.config.ccsession.create_answer(0)) self.assertFalse(self.stats_httpd.running) self.assertEqual( self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None), diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index a52ce6325a..5982595022 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -201,8 +201,10 @@ class TestStats(unittest.TestCase): self.stats = self.stats_server.server self.assertFalse(self.stats.running) self.stats_server.run() + self.assertEqual(send_command("status", "Stats"), + (0, "Stats is up. (PID " + str(os.getpid()) + ")")) self.assertTrue(self.stats.running) - send_shutdown("Stats") + self.assertEqual(send_shutdown("Stats"), (0, None)) self.assertFalse(self.stats.running) self.stats_server.shutdown() From bd8cb42b61666342ee8bc6c33aed2a168301ff67 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 6 Sep 2011 11:42:42 +0900 Subject: [PATCH 936/974] [1175] send the command 'status' to the stats when it started, and then send the command 'shutdown', and also check each value returned by each invoked command --- src/bin/stats/tests/b10-stats-httpd_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 526d5f2603..ccadeb20fd 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -203,9 +203,12 @@ class TestHttpHandler(unittest.TestCase): def test_do_GET_failed1(self): + # checks status + self.assertEqual(send_command("status", "Stats"), + (0, "Stats is up. (PID " + str(os.getpid()) + ")")) # failure case(Stats is down) self.assertTrue(self.stats.running) - send_shutdown("Stats") # Stats is down + self.assertEqual(send_shutdown("Stats"), (0, None)) # Stats is down self.assertFalse(self.stats.running) self.stats_httpd.cc_session.set_timeout(milliseconds=100) From d56c782197242e32ccdd23c9e3652ff520f3d58f Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 6 Sep 2011 20:17:57 +0900 Subject: [PATCH 937/974] [1175] modify b10-stats-httpd_test.py - The function get_availaddr uses socket.getaddrinfo for getting the address family. - The function is_ipv6_enabled uses 3 random ports for checking whether IPv6 is enabled. --- src/bin/stats/tests/b10-stats-httpd_test.py | 49 +++++++++++---------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index ccadeb20fd..f0a49cf1a5 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -33,6 +33,7 @@ import threading import http.client import xml.etree.ElementTree import signal +import random import isc import stats_httpd @@ -60,34 +61,36 @@ def get_availaddr(address='127.0.0.1', port=8001): """returns tuple of address and port available to listen on the platform. Default port range is between 8001 and 65535. If port is over flow(greater than 65535), OverflowError is thrown""" - sock = None - if is_ipv6_enabled(address): - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - else: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - while True: + while True: + for addr in socket.getaddrinfo( + address, port, 0, + socket.SOCK_STREAM, socket.IPPROTO_TCP): + sock = socket.socket(addr[0], socket.SOCK_STREAM) try: sock.bind((address, port)) return (address, port) except socket.error: - # This address and port number are already in use. - # next port number is added - port = port + 1 - finally: - if sock: sock.close() + continue + finally: + if sock: sock.close() + # This address and port number are already in use. + # next port number is added + port = port + 1 -def is_ipv6_enabled(address='::1', port=8000): - """checks IPv6 enabled on the platform""" - sock = None - try: - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - sock.bind((address, port)) - return True - except socket.error: - return False - finally: - if sock: sock.close() +def is_ipv6_enabled(address='::1', port=8001): + """checks IPv6 enabled on the platform. address for check is '::1' + and port for check is random number between 8001 and + 65535. Retrying is 3 times even if it fails.""" + for p in random.sample(range(port, 65535), 3): + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.bind((address, p)) + return True + except socket.error: + continue + finally: + if sock: sock.close() + raise Exception('hoge') class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" From a5387c15e93c6d1925bf4ad0eacdcfd63790c32a Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 6 Sep 2011 20:27:44 +0900 Subject: [PATCH 938/974] [1175] fix a typo --- src/bin/stats/tests/b10-stats-httpd_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index f0a49cf1a5..a066251313 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -90,7 +90,7 @@ def is_ipv6_enabled(address='::1', port=8001): continue finally: if sock: sock.close() - raise Exception('hoge') + return False class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" From 5b866ef26bd5ae980bb86c494a592ef232552b68 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Thu, 8 Sep 2011 10:52:19 +0900 Subject: [PATCH 939/974] [1175] modify test_utils.py - move up an assignment of the BIND10_MSGQ_SOCKET_FILE environment variable (BaseModules uses a constant file name during each testcase) - BaseModules checks whether msgq is ready after it started the msgq object. A SessionTimeout is raised here if not. --- src/bin/stats/tests/test_utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index cb63abb099..20b959ecb4 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -15,6 +15,10 @@ import isc.config.cfgmgr import stats import stats_httpd +# Change value of BIND10_MSGQ_SOCKET_FILE in environment variables +if 'BIND10_MSGQ_SOCKET_FILE' not in os.environ: + os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='msgq_socket_') + def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=None): if session is not None: cc_session = session @@ -323,12 +327,11 @@ class MyStatsHttpd(stats_httpd.StatsHttpd): class BaseModules: def __init__(self): - # Change value of BIND10_MSGQ_SOCKET_FILE in environment variables - if 'BIND10_MSGQ_SOCKET_FILE' not in os.environ: - os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='msgq_socket_') # MockMsgq self.msgq = ThreadingServerManager(MockMsgq) self.msgq.run() + # Check whether msgq is ready. A SessionTimeout is raised here if not. + isc.cc.session.Session().close() # MockCfgmgr self.cfgmgr = ThreadingServerManager(MockCfgmgr) self.cfgmgr.run() From 7808524aa9bbb424327ac67d7408647cb18840f5 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 13 Sep 2011 17:53:22 +0900 Subject: [PATCH 940/974] [1175] modify test_utils.py - explicitly shut down the socket of the msgq before shutting down the msgq - do nothing in the shutdown method of MockMsgq for avoiding shutting down the msgq twice - replace the stop method in the shutdown method of the MyStatsHttpd with the shutdown command of stats_httpd --- src/bin/stats/tests/test_utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index 20b959ecb4..1eaf220c21 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -99,10 +99,14 @@ class MockMsgq: except Exception: pass finally: + # explicitly shut down the socket of the msgq before + # shutting down the msgq + self.msgq.listen_socket.shutdown(msgq.socket.SHUT_RDWR) self.msgq.shutdown() def shutdown(self): - self.msgq.shutdown() + # do nothing for avoiding shutting down the msgq twice + pass class MockCfgmgr: def __init__(self): @@ -323,7 +327,7 @@ class MyStatsHttpd(stats_httpd.StatsHttpd): pass def shutdown(self): - self.stop() + self.command_handler('shutdown', None) class BaseModules: def __init__(self): From 9dedc72e89b9ca8ba2c5f3bc562ad9ccd1aa05b0 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 13 Sep 2011 18:21:55 +0900 Subject: [PATCH 941/974] [1175] add the package name for SessionError --- src/bin/stats/stats.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in index 7cedfbfe07..da00818960 100755 --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -405,7 +405,7 @@ if __name__ == "__main__": except OptionValueError as ove: logger.fatal(STATS_BAD_OPTION_VALUE, ove) sys.exit(1) - except SessionError as se: + except isc.cc.session.SessionError as se: logger.fatal(STATS_CC_SESSION_ERROR, se) sys.exit(1) except StatsError as se: From a35b62699480e149f22f4e039935bfcf41f97ac2 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 13 Sep 2011 18:22:23 +0900 Subject: [PATCH 942/974] [1175] correct the logging id --- src/bin/stats/stats_httpd.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in index e8f7f50fa1..596870a06f 100644 --- a/src/bin/stats/stats_httpd.py.in +++ b/src/bin/stats/stats_httpd.py.in @@ -513,7 +513,7 @@ if __name__ == "__main__": logger.fatal(STATHTTPD_CC_SESSION_ERROR, se) sys.exit(1) except HttpServerError as hse: - logger.fatal(STATHTTPD_START_SERVER_ERROR, hse) + logger.fatal(STATHTTPD_START_SERVER_INIT_ERROR, hse) sys.exit(1) except KeyboardInterrupt as kie: logger.info(STATHTTPD_STOPPED_BY_KEYBOARD) From 3efca5f9b7b7bfeac53044fdd44e5add61397157 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 26 Sep 2011 22:24:35 +0900 Subject: [PATCH 943/974] [1175] correct the comment of the function according to its purpose --- src/bin/stats/tests/b10-stats-httpd_test.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index a066251313..34a260d2db 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -58,9 +58,14 @@ DUMMY_DATA = { } def get_availaddr(address='127.0.0.1', port=8001): - """returns tuple of address and port available to listen on the - platform. Default port range is between 8001 and 65535. If port is - over flow(greater than 65535), OverflowError is thrown""" + """returns a tuple of address and port which is available to + listen on the platform. The first argument is a address for + search. The second argument is a port for search. If a set of + address and port is failed on the search for the availability, the + port number is increased and it goes on the next trial until the + available set of address and port is looked up. If the port number + reaches over 65535, it may stop the search and raise a + OverflowError exception.""" while True: for addr in socket.getaddrinfo( address, port, 0, From 1f967a8ffe37f6732dd628d28a13abc442541c38 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 26 Sep 2011 15:46:30 +0900 Subject: [PATCH 944/974] [1175] fix double hash signs --- src/bin/stats/tests/b10-stats-httpd_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 34a260d2db..8aa0295a81 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -202,7 +202,7 @@ class TestHttpHandler(unittest.TestCase): self.assertEqual(response.getheader('Location'), "http://%s:%d%s" % (self.address, self.port, stats_httpd.XML_URL_PATH)) - # # 404 NotFound + # 404 NotFound self.client._http_vsn_str = 'HTTP/1.0' self.client.putrequest('GET', '/path/to/foo/bar') self.client.endheaders() From fcfe5af9c22c5b666e5ecf646bbe0d9da7b655e9 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 26 Sep 2011 18:04:13 +0900 Subject: [PATCH 945/974] [1175] modify b10-stats_test.py, b10-stats-httpd_test.py and test_utils.py - exclude the setup of handler in SIGALRM and add it as an external function in the SignalHandler class in test_utils.py - define the function in that class to reset the handler in test_utils.py, and add it in tearDown() in each testcase --- src/bin/stats/tests/b10-stats-httpd_test.py | 33 ++++++++------------- src/bin/stats/tests/b10-stats_test.py | 13 ++++---- src/bin/stats/tests/test_utils.py | 19 ++++++++++++ 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 8aa0295a81..06bf9e6c74 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -32,13 +32,12 @@ import time import threading import http.client import xml.etree.ElementTree -import signal import random import isc import stats_httpd import stats -from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, send_command, send_shutdown +from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, SignalHandler, send_command, send_shutdown DUMMY_DATA = { 'Boss' : { @@ -100,9 +99,8 @@ def is_ipv6_enabled(address='::1', port=8001): class TestHttpHandler(unittest.TestCase): """Tests for HttpHandler class""" def setUp(self): - # deadlock will be killed afer 20 secs - signal.signal(signal.SIGALRM, self.my_signal_handler) - signal.alarm(20) + # set the signal handler for deadlock + self.sig_handler = SignalHandler(self.fail) self.base = BaseModules() self.stats_server = ThreadingServerManager(MyStats) self.stats = self.stats_server.server @@ -120,9 +118,8 @@ class TestHttpHandler(unittest.TestCase): self.stats_httpd_server.shutdown() self.stats_server.shutdown() self.base.shutdown() - - def my_signal_handler(self, signal, frame): - self.fail("A deadlock might be detected") + # reset the signal handler + self.sig_handler.reset() def test_do_GET(self): self.assertTrue(type(self.stats_httpd.httpd) is list) @@ -285,18 +282,16 @@ class TestHttpServerError(unittest.TestCase): class TestHttpServer(unittest.TestCase): """Tests for HttpServer class""" def setUp(self): - # deadlock will be killed afer 20 secs - signal.signal(signal.SIGALRM, self.my_signal_handler) - signal.alarm(20) + # set the signal handler for deadlock + self.sig_handler = SignalHandler(self.fail) self.base = BaseModules() def tearDown(self): if hasattr(self, "stats_httpd"): self.stats_httpd.stop() self.base.shutdown() - - def my_signal_handler(self, signal, frame): - self.fail("A deadlock might be detected") + # reset the signal handler + self.sig_handler.reset() def test_httpserver(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) @@ -318,9 +313,8 @@ class TestStatsHttpd(unittest.TestCase): """Tests for StatsHttpd class""" def setUp(self): - # deadlock will be killed afer 20 secs - signal.signal(signal.SIGALRM, self.my_signal_handler) - signal.alarm(20) + # set the signal handler for deadlock + self.sig_handler = SignalHandler(self.fail) self.base = BaseModules() self.stats_server = ThreadingServerManager(MyStats) self.stats_server.run() @@ -332,9 +326,8 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd.stop() self.stats_server.shutdown() self.base.shutdown() - - def my_signal_handler(self, signal, frame): - self.fail("A deadlock might be detected") + # reset the signal handler + self.sig_handler.reset() def test_init(self): server_address = get_availaddr() diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 5982595022..3813c7e312 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -27,11 +27,10 @@ import threading import io import time import imp -import signal import stats import isc.cc.session -from test_utils import BaseModules, ThreadingServerManager, MyStats, send_command, send_shutdown +from test_utils import BaseModules, ThreadingServerManager, MyStats, SignalHandler, send_command, send_shutdown class TestUtilties(unittest.TestCase): items = [ @@ -147,9 +146,8 @@ class TestCallback(unittest.TestCase): class TestStats(unittest.TestCase): def setUp(self): - # deadlock will be killed afer 20 secs - signal.signal(signal.SIGALRM, self.my_signal_handler) - signal.alarm(20) + # set the signal handler for deadlock + self.sig_handler = SignalHandler(self.fail) self.base = BaseModules() self.stats = stats.Stats() self.const_timestamp = 1308730448.965706 @@ -158,9 +156,8 @@ class TestStats(unittest.TestCase): def tearDown(self): self.base.shutdown() - - def my_signal_handler(self, signal, frame): - self.fail("A deadlock might be detected") + # reset the signal handler + self.sig_handler.reset() def test_init(self): self.assertEqual(self.stats.module_name, 'Stats') diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index 1eaf220c21..d3bae1086b 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -9,6 +9,7 @@ import sys import threading import tempfile import json +import signal import msgq import isc.config.cfgmgr @@ -19,6 +20,24 @@ import stats_httpd if 'BIND10_MSGQ_SOCKET_FILE' not in os.environ: os.environ['BIND10_MSGQ_SOCKET_FILE'] = tempfile.mktemp(prefix='msgq_socket_') +class SignalHandler(): + """A signal handler class for deadlock in unittest""" + def __init__(self, fail_handler, timeout=20): + """sets a schedule in SIGARM for invoking the handler via + unittest.TestCase after timeout seconds (default is 20)""" + self.fail_handler = fail_handler + self.orig_handler = signal.signal(signal.SIGALRM, self.sig_handler) + signal.alarm(timeout) + + def reset(self): + """resets the schedule in SIGALRM""" + signal.alarm(0) + signal.signal(signal.SIGALRM, self.orig_handler) + + def sig_handler(self, signal, frame): + """envokes unittest.TestCase.fail as a signal handler""" + self.fail_handler("A deadlock might be detected") + def send_command(command_name, module_name, params=None, session=None, nonblock=False, timeout=None): if session is not None: cc_session = session From 34ead9dfeff5f64af36a209cae28075fcbbb3330 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 26 Sep 2011 18:35:22 +0900 Subject: [PATCH 946/974] [1175] remove the two conditions because they do the same tests for IPv4 whether IPv6 is enabled or not --- src/bin/stats/tests/b10-stats-httpd_test.py | 24 ++++++--------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 06bf9e6c74..9e03cfa8e0 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -390,23 +390,13 @@ class TestStatsHttpd(unittest.TestCase): self.assertEqual(ht.address_family, socket.AF_INET6) self.assertTrue(isinstance(ht.socket, socket.socket)) - # dual stack (address is ipv4) - if self.ipv6_enabled: - server_addresses = get_availaddr() - self.stats_httpd = MyStatsHttpd(server_addresses) - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) - self.assertEqual(ht.address_family, socket.AF_INET) - self.assertTrue(isinstance(ht.socket, socket.socket)) - - # only-ipv4 single stack - if not self.ipv6_enabled: - server_addresses = get_availaddr() - self.stats_httpd = MyStatsHttpd(server_addresses) - for ht in self.stats_httpd.httpd: - self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) - self.assertEqual(ht.address_family, socket.AF_INET) - self.assertTrue(isinstance(ht.socket, socket.socket)) + # dual/single stack (address is ipv4) + server_addresses = get_availaddr() + self.stats_httpd = MyStatsHttpd(server_addresses) + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) + self.assertEqual(ht.address_family, socket.AF_INET) + self.assertTrue(isinstance(ht.socket, socket.socket)) # any address (IPv4) server_addresses = get_availaddr(address='0.0.0.0') From ce8b5fe9567f06f7acba34b9e9b35ad471e2ab67 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 26 Sep 2011 22:12:48 +0900 Subject: [PATCH 947/974] [1175] correct the names and comments of the functions according to their purposes --- src/bin/stats/tests/b10-stats-httpd_test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 9e03cfa8e0..22b91c1184 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -455,7 +455,9 @@ class TestStatsHttpd(unittest.TestCase): self.stats_httpd.cc_session.close() self.assertRaises(ValueError, self.stats_httpd.start) - def test_select_failure1(self): + def test_failure_with_a_select_error (self): + """checks select.error is raised if the exception except + errno.EINTR is raised while it's selecting""" def raise_select_except(*args): raise select.error('dummy error') orig_select = stats_httpd.select.select @@ -464,7 +466,9 @@ class TestStatsHttpd(unittest.TestCase): self.assertRaises(select.error, self.stats_httpd.start) stats_httpd.select.select = orig_select - def test_select_failure2(self): + def test_nofailure_with_errno_EINTR(self): + """checks no exception is raised if errno.EINTR is raised + while it's selecting""" def raise_select_except(*args): raise select.error(errno.EINTR) orig_select = stats_httpd.select.select From 434f4fd17dd3dee1d17e7b2e008f1ab1416d5799 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 26 Sep 2011 22:13:09 +0900 Subject: [PATCH 948/974] [1175] correct the comment according to the purpose of the retry --- src/bin/stats/tests/test_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index d3bae1086b..3cd394faa9 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -66,6 +66,9 @@ def send_shutdown(module_name, **kwargs): class ThreadingServerManager: def __init__(self, server, *args, **kwargs): self.server = None + # retrying to create a server object until 3 times because a + # SessionTimeout depending on some environment or timing may + # be accidentally raised n = 0 while True: try: @@ -73,7 +76,6 @@ class ThreadingServerManager: except isc.cc.session.SessionTimeout: if self.server is not None: self.server.shutdown() - # retrying until 3 times if n >2: raise n = n + 1 continue From d04acfb82c3425a638f09d2f49208ef86bc7a6b3 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 28 Sep 2011 13:45:49 +0900 Subject: [PATCH 949/974] [1175] comment the reason why socket.has_ipv6 is not used --- src/bin/stats/tests/b10-stats-httpd_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py index 22b91c1184..e8670803c7 100644 --- a/src/bin/stats/tests/b10-stats-httpd_test.py +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -84,7 +84,10 @@ def get_availaddr(address='127.0.0.1', port=8001): def is_ipv6_enabled(address='::1', port=8001): """checks IPv6 enabled on the platform. address for check is '::1' and port for check is random number between 8001 and - 65535. Retrying is 3 times even if it fails.""" + 65535. Retrying is 3 times even if it fails. The built-in socket + module provides a 'has_ipv6' parameter, but it's not used here + because there may be a situation where the value is True on an + environment where the IPv6 config is disabled.""" for p in random.sample(range(port, 65535), 3): try: sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) From 054699635affd9c9ecbe7a108d880829f3ba229e Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Wed, 28 Sep 2011 13:53:21 +0900 Subject: [PATCH 950/974] [1175] remove the retrying part because there is no clear necessity --- src/bin/stats/tests/test_utils.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py index 3cd394faa9..da0bac4427 100644 --- a/src/bin/stats/tests/test_utils.py +++ b/src/bin/stats/tests/test_utils.py @@ -65,21 +65,7 @@ def send_shutdown(module_name, **kwargs): class ThreadingServerManager: def __init__(self, server, *args, **kwargs): - self.server = None - # retrying to create a server object until 3 times because a - # SessionTimeout depending on some environment or timing may - # be accidentally raised - n = 0 - while True: - try: - self.server = server(*args, **kwargs) - except isc.cc.session.SessionTimeout: - if self.server is not None: - self.server.shutdown() - if n >2: raise - n = n + 1 - continue - else: break + self.server = server(*args, **kwargs) self.server_name = server.__name__ self.server._thread = threading.Thread( name=self.server_name, target=self.server.run) From 85e4dfa61bf440c132f4ce6bc73130bc6e91719c Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Mon, 3 Oct 2011 13:23:19 +0900 Subject: [PATCH 951/974] [master] changelog entry for #928, #929, #930 and #1175 --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d99bcbea00..82e920b64f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,7 +3,7 @@ Stats module can read these through the config manager. Stats module and stats httpd report statistics data and statistics schema by each module via both bindctl and HTTP/XML. - (Trac #928,#929,#930,#1175, git TBD) + (Trac #928,#929,#930,#1175, git 054699635affd9c9ecbe7a108d880829f3ba229e) 290. [func] jinmei libdns++/pydnspp: added an option parameter to the "from wire" From 471edfd7a86d91f04536bc7c7fb42ad7239e1731 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 3 Oct 2011 11:02:27 +0200 Subject: [PATCH 952/974] [1259] Use a constant for 100 updates --- src/lib/python/isc/xfrin/diff.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 6c0540121b..962e51c283 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -26,6 +26,17 @@ class NoSuchZone(Exception): """ pass +""" +This is the amount of changes we accumulate before calling Diff.apply +automatically. + +The number 100 is just taken from Bind9. We don't know the rationale +for exactly this amount, but we think it is just some randomly chosen +number. +""" +# If changing this, modify the tests accordingly as well. +DIFF_APPLY_TRESHOLD = 100 + class Diff: """ The class represents a diff against current state of datasource on @@ -81,7 +92,7 @@ class Diff: raise ValueError('The rrset must contain exactly 1 Rdata, but ' + 'it holds ' + str(rr.get_rdata_count())) self.__buffer.append((operation, rr)) - if len(self.__buffer) >= 100: + if len(self.__buffer) >= DIFF_APPLY_TRESHOLD: # Time to auto-apply, so the data don't accumulate too much self.apply() From 32f075fa288dc5ea049cbf72657386889144bd12 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 3 Oct 2011 11:04:07 +0200 Subject: [PATCH 953/974] [1259] Rename parameter to ds_client Because datasource could represent something bigger behind the scenes. --- src/lib/python/isc/xfrin/diff.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 962e51c283..9a286e80fe 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -51,24 +51,24 @@ class Diff: the changes to underlying data source right away, but keeps them for a while. """ - def __init__(self, datasource, zone): + def __init__(self, ds_client, zone): """ Initializes the diff to a ready state. It checks the zone exists in the datasource and if not, NoSuchZone is raised. This also creates a transaction in the data source. - The datasource is the one containing the zone. Zone is isc.dns.Name - object representing the name of the zone (its apex). + The ds_client is the datasource client containing the zone. Zone is + isc.dns.Name object representing the name of the zone (its apex). You can also expect isc.datasrc.Error or isc.datasrc.NotImplemented exceptions. """ - self.__updater = datasource.get_updater(zone, False) + self.__updater = ds_client.get_updater(zone, False) if self.__updater is None: # The no such zone case raise NoSuchZone("Zone " + str(zone) + " does not exist in the data source " + - str(datasource)) + str(ds_client)) self.__buffer = [] def __check_commited(self): From d36eda71276b43e4281ae53fd558155725f4d4eb Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 3 Oct 2011 11:19:11 +0200 Subject: [PATCH 954/974] [1259] Check the class If it corresponds to the updater. This would be a bug, but we better detect it sooner. --- src/lib/python/isc/xfrin/diff.py | 10 ++++++++++ src/lib/python/isc/xfrin/tests/diff_tests.py | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 9a286e80fe..082bdb38a4 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -91,6 +91,10 @@ class Diff: if rr.get_rdata_count() != 1: raise ValueError('The rrset must contain exactly 1 Rdata, but ' + 'it holds ' + str(rr.get_rdata_count())) + if rr.get_class() != self.__updater.get_class(): + raise ValueError("The rrset's class " + str(rr.get_class()) + + " does not match updater's " + + str(self.__updater.get_class())) self.__buffer.append((operation, rr)) if len(self.__buffer) >= DIFF_APPLY_TRESHOLD: # Time to auto-apply, so the data don't accumulate too much @@ -103,6 +107,9 @@ class Diff: The rr is of isc.dns.RRset type and it must contain only one RR. If this is not the case or if the diff was already commited, this raises the ValueError exception. + + The rr class must match the one of the datasource client. If + it does not, ValueError is raised. """ self.__data_common(rr, 'add') @@ -113,6 +120,9 @@ class Diff: The rr is of isc.dns.RRset type and it must contain only one RR. If this is not the case or if the diff was already commited, this raises the ValueError exception. + + The rr class must match the one of the datasource client. If + it does not, ValueError is raised. """ self.__data_common(rr, 'remove') diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 963b16d008..dc1aca966e 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -94,6 +94,13 @@ class DiffTest(unittest.TestCase): """ self.__data_operations.append(('remove', rrset)) + def get_class(self): + """ + This one is part of pretending to be a zone updater. It returns + the IN class. + """ + return self.__rrclass + def get_updater(self, zone_name, replace): """ This one pretends this is the data source client and serves @@ -322,6 +329,17 @@ class DiffTest(unittest.TestCase): diff.compact() check() + def test_wrong_class(self): + """ + Test a wrong class of rrset is rejected. + """ + diff = Diff(self, Name('example.org.')) + rrset = RRset(Name('a.example.org.'), RRClass.CH(), RRType.NS(), + self.__ttl) + rrset.add_rdata(Rdata(RRType.NS(), RRClass.CH(), 'ns.example.org.')) + self.assertRaises(ValueError, diff.add_data, rrset) + self.assertRaises(ValueError, diff.remove_data, rrset) + if __name__ == "__main__": isc.log.init("bind10") unittest.main() From 8fe024cd171ecf1610419abb70e5d613b94ba5a0 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 3 Oct 2011 09:29:36 +0000 Subject: [PATCH 955/974] [master] fix for solaris build failure simply a forgotten cast, reviewed on jabber --- src/lib/dns/python/message_python.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 601215326b..6de092544a 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -160,7 +160,7 @@ PyMethodDef Message_methods[] = { "If the given message is not in RENDER mode, an " "InvalidMessageOperation is raised.\n" }, - { "from_wire", Message_fromWire, METH_VARARGS, Message_fromWire_doc }, + { "from_wire", reinterpret_cast(Message_fromWire), METH_VARARGS, Message_fromWire_doc }, { NULL, NULL, 0, NULL } }; From c6babcd3e44bc42fdb090d3a4837848d8c7c149c Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 3 Oct 2011 11:40:46 +0200 Subject: [PATCH 956/974] [1259] Check the thing is not used after exception As it would not be safe anyway. --- src/lib/python/isc/xfrin/diff.py | 44 +++++++++------ src/lib/python/isc/xfrin/tests/diff_tests.py | 56 ++++++++++++++++++++ 2 files changed, 84 insertions(+), 16 deletions(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 082bdb38a4..4291883f0d 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -73,12 +73,13 @@ class Diff: def __check_commited(self): """ - This checks if the diff is already commited. If it is, it raises - ValueError. This check is for methods that need to work only on + This checks if the diff is already commited or broken. If it is, it + raises ValueError. This check is for methods that need to work only on yet uncommited diffs. """ if self.__updater is None: - raise ValueError("The diff is already commited, you come late") + raise ValueError("The diff is already commited or it has raised " + + "an exception, you come late") def __data_common(self, rr, operation): """ @@ -172,15 +173,21 @@ class Diff: self.__check_commited() # First, compact the data self.compact() - # Then pass the data inside the data source - for (operation, rrset) in self.__buffer: - if operation == 'add': - self.__updater.add_rrset(rrset) - elif operation == 'remove': - self.__updater.remove_rrset(rrset) - else: - raise ValueError('Unknown operation ' + operation) - # As everything is already in, drop the buffer + try: + # Then pass the data inside the data source + for (operation, rrset) in self.__buffer: + if operation == 'add': + self.__updater.add_rrset(rrset) + elif operation == 'remove': + self.__updater.remove_rrset(rrset) + else: + raise ValueError('Unknown operation ' + operation) + # As everything is already in, drop the buffer + except: + # If there's a problem, we can't continue. + self.__updater = None + raise + self.__buffer = [] def commit(self): @@ -195,10 +202,15 @@ class Diff: # Push the data inside the data source self.apply() # Make sure they are visible. - self.__updater.commit() - # Remove the updater. That will free some resources for one, but - # mark this object as already commited, so we can check - self.__updater = None + try: + self.__updater.commit() + finally: + # Remove the updater. That will free some resources for one, but + # mark this object as already commited, so we can check + + # We remove it even in case the commit failed, as that makes us + # unusable. + self.__updater = None def get_buffer(self): """ diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index dc1aca966e..0152d0718b 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -18,6 +18,13 @@ import unittest from isc.dns import Name, RRset, RRClass, RRType, RRTTL, Rdata from isc.xfrin.diff import Diff, NoSuchZone +class TestError(Exception): + """ + Just to have something to be raised during the tests. + Not used outside. + """ + pass + class DiffTest(unittest.TestCase): """ Tests for the isc.xfrin.diff.Diff class. @@ -37,6 +44,7 @@ class DiffTest(unittest.TestCase): self.__data_operations = [] self.__apply_called = False self.__commit_called = False + self.__broken_called = False # Some common values self.__rrclass = RRClass.IN() self.__type = RRType.A() @@ -73,6 +81,15 @@ class DiffTest(unittest.TestCase): """ self.__apply_called = True + def __broken_operation(self, *args): + """ + This can be used whenever an operation should fail. It raises TestError. + It should take whatever amount of parameters needed, so it can be put + quite anywhere. + """ + self.__broken_called = True + raise TestError("Test error") + def commit(self): """ This is part of pretending to be a zone updater. This notes the commit @@ -340,6 +357,45 @@ class DiffTest(unittest.TestCase): self.assertRaises(ValueError, diff.add_data, rrset) self.assertRaises(ValueError, diff.remove_data, rrset) + def __do_raise_test(self): + """ + Do a raise test. Expects that one of the operations is exchanged for + broken version. + """ + diff = Diff(self, Name('example.org.')) + diff.add_data(self.__rrset1) + diff.remove_data(self.__rrset2) + self.assertRaises(TestError, diff.commit) + self.assertTrue(self.__broken_called) + self.assertRaises(ValueError, diff.add_data, self.__rrset1) + self.assertRaises(ValueError, diff.remove_data, self.__rrset2) + self.assertRaises(ValueError, diff.commit) + self.assertRaises(ValueError, diff.apply) + + def test_raise_add(self): + """ + Test the exception from add_rrset is propagated and the diff can't be + used afterwards. + """ + self.add_rrset = self.__broken_operation + self.__do_raise_test() + + def test_raise_remove(self): + """ + Test the exception from remove_rrset is propagated and the diff can't be + used afterwards. + """ + self.remove_rrset = self.__broken_operation + self.__do_raise_test() + + def test_raise_commit(self): + """ + Test the exception from updater's commit gets propagated and it can't be + used afterwards. + """ + self.commit = self.__broken_operation + self.__do_raise_test() + if __name__ == "__main__": isc.log.init("bind10") unittest.main() From 519720d9c6eb354a2e31089f1c7b8fd0760053f9 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 3 Oct 2011 15:11:01 +0200 Subject: [PATCH 957/974] [1259] Log when TTLs don't match --- src/lib/python/isc/Makefile.am | 2 +- src/lib/python/isc/log_messages/Makefile.am | 2 ++ .../isc/log_messages/libxfrin_messages.py | 1 + src/lib/python/isc/xfrin/Makefile.am | 11 +++++++ src/lib/python/isc/xfrin/diff.py | 7 +++++ .../python/isc/xfrin/libxfrin_messages.mes | 22 ++++++++++++++ src/lib/python/isc/xfrin/tests/diff_tests.py | 29 +++++++++++++++++++ 7 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/lib/python/isc/log_messages/libxfrin_messages.py create mode 100644 src/lib/python/isc/xfrin/libxfrin_messages.mes diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am index a9cc0e1b73..a3e74c5ff7 100644 --- a/src/lib/python/isc/Makefile.am +++ b/src/lib/python/isc/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10 -SUBDIRS += log_messages xfrin +SUBDIRS += xfrin log_messages python_PYTHON = __init__.py diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am index b9bc4c8a8e..30f8374bf6 100644 --- a/src/lib/python/isc/log_messages/Makefile.am +++ b/src/lib/python/isc/log_messages/Makefile.am @@ -11,6 +11,7 @@ EXTRA_DIST += zonemgr_messages.py EXTRA_DIST += cfgmgr_messages.py EXTRA_DIST += config_messages.py EXTRA_DIST += notify_out_messages.py +EXTRA_DIST += libxfrin_messages.py CLEANFILES = __init__.pyc CLEANFILES += bind10_messages.pyc @@ -23,6 +24,7 @@ CLEANFILES += zonemgr_messages.pyc CLEANFILES += cfgmgr_messages.pyc CLEANFILES += config_messages.pyc CLEANFILES += notify_out_messages.pyc +CLEANFILES += libxfrin_messages.pyc CLEANDIRS = __pycache__ diff --git a/src/lib/python/isc/log_messages/libxfrin_messages.py b/src/lib/python/isc/log_messages/libxfrin_messages.py new file mode 100644 index 0000000000..74da329c68 --- /dev/null +++ b/src/lib/python/isc/log_messages/libxfrin_messages.py @@ -0,0 +1 @@ +from work.libxfrin_messages import * diff --git a/src/lib/python/isc/xfrin/Makefile.am b/src/lib/python/isc/xfrin/Makefile.am index 8b453482e6..d31acb6df6 100644 --- a/src/lib/python/isc/xfrin/Makefile.am +++ b/src/lib/python/isc/xfrin/Makefile.am @@ -1,6 +1,17 @@ SUBDIRS = . tests python_PYTHON = __init__.py diff.py +BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.py +nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.py +pylogmessagedir = $(pyexecdir)/isc/log_messages/ + +CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.py +CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.pyc + +# Define rule to build logging source files from message file +$(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.py: libxfrin_messages.mes + $(top_builddir)/src/lib/log/compiler/message \ + -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/libxfrin_messages.mes pythondir = $(pyexecdir)/isc/xfrin diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 4291883f0d..001c215ff5 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -19,6 +19,8 @@ it to the datasource. """ import isc.dns +import isc.log +from isc.log_messages.libxfrin_messages import * class NoSuchZone(Exception): """ @@ -37,6 +39,8 @@ number. # If changing this, modify the tests accordingly as well. DIFF_APPLY_TRESHOLD = 100 +logger = isc.log.Logger('libxfrin') + class Diff: """ The class represents a diff against current state of datasource on @@ -150,6 +154,9 @@ class Diff: rrset.get_class(), rrset.get_type(), rrset.get_ttl()))) + if rrset.get_ttl() != buf[-1][1].get_ttl(): + logger.warn(LIBXFRIN_DIFFERENT_TTL, rrset.get_ttl(), + buf[-1][1].get_ttl()) for rdatum in rrset.get_rdata(): buf[-1][1].add_rdata(rdatum) self.__buffer = buf diff --git a/src/lib/python/isc/xfrin/libxfrin_messages.mes b/src/lib/python/isc/xfrin/libxfrin_messages.mes new file mode 100644 index 0000000000..8bfb7b1ee5 --- /dev/null +++ b/src/lib/python/isc/xfrin/libxfrin_messages.mes @@ -0,0 +1,22 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# No namespace declaration - these constants go in the global namespace +# of the libxfrin_messages python module. + +% LIBXFRIN_DIFFERENT_TTL multiple data with different TTLs (%1, %2) on %3/%4 +The xfrin module received an update containing multiple rdata changes for the +same RRset. But the TTLs of these don't match each other. The data will be put +into the underlying data source. What happens with the TTLs is up to the +data source. diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 0152d0718b..354f29956b 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -45,6 +45,7 @@ class DiffTest(unittest.TestCase): self.__apply_called = False self.__commit_called = False self.__broken_called = False + self.__warn_called = False # Some common values self.__rrclass = RRClass.IN() self.__type = RRType.A() @@ -90,6 +91,13 @@ class DiffTest(unittest.TestCase): self.__broken_called = True raise TestError("Test error") + def warn(self, *args): + """ + This is for checking the warn function was called, we replace the logger + in the tested module. + """ + self.__warn_called = True + def commit(self): """ This is part of pretending to be a zone updater. This notes the commit @@ -396,6 +404,27 @@ class DiffTest(unittest.TestCase): self.commit = self.__broken_operation self.__do_raise_test() + def test_ttl(self): + """ + Test the TTL handling. A warn function should have been called if they + differ, but that's all, it should not crash or raise. + """ + orig_logger = isc.xfrin.diff.logger + try: + isc.xfrin.diff.logger = self + diff = Diff(self, Name('example.org.')) + diff.add_data(self.__rrset1) + rrset2 = RRset(Name('a.example.org.'), self.__rrclass, + self.__type, RRTTL(120)) + rrset2.add_rdata(Rdata(self.__type, self.__rrclass, '192.10.2.2')) + diff.add_data(rrset2) + # They should get compacted together and complain. + diff.compact() + self.assertEqual(1, len(diff.get_buffer())) + self.assertTrue(self.__warn_called) + finally: + isc.xfrin.diff.logger = orig_logger + if __name__ == "__main__": isc.log.init("bind10") unittest.main() From 9e17bd49b426ffba00312cf90ec80d178a20b964 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 3 Oct 2011 15:13:51 +0200 Subject: [PATCH 958/974] [1259] Better names --- src/lib/python/isc/xfrin/tests/diff_tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index 354f29956b..d431f4210b 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -160,7 +160,7 @@ class DiffTest(unittest.TestCase): self.assertRaises(NoSuchZone, Diff, self, Name('none.example.org.')) self.assertTrue(self.__updater_requested) - def __data_common(self, diff, method, name): + def __data_common(self, diff, method, operation): """ Common part of test for test_add and test_remove. """ @@ -169,10 +169,10 @@ class DiffTest(unittest.TestCase): self.assertRaises(ValueError, method, self.__rrset_multi) # They were not added self.assertEqual([], diff.get_buffer()) - # Add some proper data + # Put some proper data into the diff method(self.__rrset1) method(self.__rrset2) - dlist = [(name, self.__rrset1), (name, self.__rrset2)] + dlist = [(operation, self.__rrset1), (operation, self.__rrset2)] self.assertEqual(dlist, diff.get_buffer()) # Check the data are not destroyed by raising an exception because of # bad data From b29c5e5221b8e6a9ff65a0c39f14c04afaed5c44 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Mon, 3 Oct 2011 15:35:33 +0200 Subject: [PATCH 959/974] [1259] Missing EXTRA_DIST --- src/lib/python/isc/xfrin/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/python/isc/xfrin/Makefile.am b/src/lib/python/isc/xfrin/Makefile.am index d31acb6df6..5804de6cf4 100644 --- a/src/lib/python/isc/xfrin/Makefile.am +++ b/src/lib/python/isc/xfrin/Makefile.am @@ -5,6 +5,8 @@ BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.py nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.py pylogmessagedir = $(pyexecdir)/isc/log_messages/ +EXTRA_DIST = libxfrin_messages.mes + CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.py CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/libxfrin_messages.pyc From f7bb760f4d8290d52959ea83b090d1877e4ac9ee Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 3 Oct 2011 14:44:28 +0000 Subject: [PATCH 960/974] [1206] fix segfault on sunstudio --- src/lib/datasrc/factory.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index eff8891ffc..c3147c8c3f 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -39,9 +39,9 @@ public: /// implementation library class DataSourceLibrarySymbolError : public DataSourceError { public: - DataSourceLibrarySymbolError(const char* file, size_t line, - const char* what) : - DataSourceError(file, line, what) {} +DataSourceLibrarySymbolError(const char* file, size_t line, + const char* what) : + DataSourceError(file, line, what) {} }; /// \brief Raised if the given config contains bad data @@ -50,8 +50,12 @@ public: /// instance, the sqlite3 datasource needs a database file). class DataSourceConfigError : public DataSourceError { public: - DataSourceConfigError(const char* file, size_t line, const char* what) : - DataSourceError(file, line, what) {} +DataSourceConfigError(const char* file, size_t line, const char* what) : + DataSourceError(file, line, what) {} +// This exception is created in the dynamic modules. Apparently +// sunstudio can't handle it if we then automatically derive the +// destructor, so we provide it explicitely +~DataSourceConfigError() throw() {} }; typedef DataSourceClient* ds_creator(isc::data::ConstElementPtr config); From 00f4c38428153bb5ad99ba1cc40e9a204266dace Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 3 Oct 2011 18:17:22 +0200 Subject: [PATCH 961/974] [1206] new stuff makes some of the old (soon to be removed) fail cppcheck --- src/lib/datasrc/data_source.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/data_source.h b/src/lib/datasrc/data_source.h index ff695da6e8..a7a15a9242 100644 --- a/src/lib/datasrc/data_source.h +++ b/src/lib/datasrc/data_source.h @@ -184,9 +184,9 @@ public: void setClass(isc::dns::RRClass& c) { rrclass = c; } void setClass(const isc::dns::RRClass& c) { rrclass = c; } - Result init() { return (NOT_IMPLEMENTED); } - Result init(isc::data::ConstElementPtr config); - Result close() { return (NOT_IMPLEMENTED); } + virtual Result init() { return (NOT_IMPLEMENTED); } + virtual Result init(isc::data::ConstElementPtr config); + virtual Result close() { return (NOT_IMPLEMENTED); } virtual Result findRRset(const isc::dns::Name& qname, const isc::dns::RRClass& qclass, @@ -351,7 +351,7 @@ public: /// \brief Returns the best enclosing zone name found for the given // name and RR class so far. - /// + /// /// \return A pointer to the zone apex \c Name, NULL if none found yet. /// /// This method never throws an exception. @@ -413,6 +413,6 @@ private: #endif -// Local Variables: +// Local Variables: // mode: c++ -// End: +// End: From e38010819247006d20532d24de8dd6c37e0ca664 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 3 Oct 2011 18:36:19 +0200 Subject: [PATCH 962/974] [1206] somehow i lost some indentation. put it back --- src/lib/datasrc/factory.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h index c3147c8c3f..8db9ec91dd 100644 --- a/src/lib/datasrc/factory.h +++ b/src/lib/datasrc/factory.h @@ -39,9 +39,9 @@ public: /// implementation library class DataSourceLibrarySymbolError : public DataSourceError { public: -DataSourceLibrarySymbolError(const char* file, size_t line, - const char* what) : - DataSourceError(file, line, what) {} + DataSourceLibrarySymbolError(const char* file, size_t line, + const char* what) : + DataSourceError(file, line, what) {} }; /// \brief Raised if the given config contains bad data @@ -50,12 +50,12 @@ DataSourceLibrarySymbolError(const char* file, size_t line, /// instance, the sqlite3 datasource needs a database file). class DataSourceConfigError : public DataSourceError { public: -DataSourceConfigError(const char* file, size_t line, const char* what) : - DataSourceError(file, line, what) {} -// This exception is created in the dynamic modules. Apparently -// sunstudio can't handle it if we then automatically derive the -// destructor, so we provide it explicitely -~DataSourceConfigError() throw() {} + DataSourceConfigError(const char* file, size_t line, const char* what) : + DataSourceError(file, line, what) {} + // This exception is created in the dynamic modules. Apparently + // sunstudio can't handle it if we then automatically derive the + // destructor, so we provide it explicitely + ~DataSourceConfigError() throw() {} }; typedef DataSourceClient* ds_creator(isc::data::ConstElementPtr config); From ebeb5ead60c5c0d7b16478498b78a8f1ef3b71c3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 3 Oct 2011 18:06:10 +0000 Subject: [PATCH 963/974] [master] (re)fixed the recent solaris build issue without relying on reinterpret_cast. okayed on jabber. --- src/lib/dns/python/message_python.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 6de092544a..23494019c6 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -78,7 +78,7 @@ PyObject* Message_makeResponse(s_Message* self); PyObject* Message_toText(s_Message* self); PyObject* Message_str(PyObject* self); PyObject* Message_toWire(s_Message* self, PyObject* args); -PyObject* Message_fromWire(PyObject* const pyself, PyObject* args); +PyObject* Message_fromWire(PyObject* pyself, PyObject* args); // This list contains the actual set of functions we have in // python. Each entry has @@ -160,7 +160,7 @@ PyMethodDef Message_methods[] = { "If the given message is not in RENDER mode, an " "InvalidMessageOperation is raised.\n" }, - { "from_wire", reinterpret_cast(Message_fromWire), METH_VARARGS, Message_fromWire_doc }, + { "from_wire", Message_fromWire, METH_VARARGS, Message_fromWire_doc }, { NULL, NULL, 0, NULL } }; @@ -642,8 +642,8 @@ Message_toWire(s_Message* self, PyObject* args) { } PyObject* -Message_fromWire(PyObject* const pyself, PyObject* args) { - s_Message* self = static_cast(pyself); +Message_fromWire(PyObject* pyself, PyObject* args) { + s_Message* const self = static_cast(pyself); const char* b; Py_ssize_t len; unsigned int options = Message::PARSE_DEFAULT; From 85ac49c5282c231c71b8d2046889d22b0061db08 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 3 Oct 2011 12:19:36 -0700 Subject: [PATCH 964/974] [1259] a minor wording fix: s/Bind9/BIND 9/ --- src/lib/python/isc/xfrin/diff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 001c215ff5..26b2f83a7e 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -32,7 +32,7 @@ class NoSuchZone(Exception): This is the amount of changes we accumulate before calling Diff.apply automatically. -The number 100 is just taken from Bind9. We don't know the rationale +The number 100 is just taken from BIND 9. We don't know the rationale for exactly this amount, but we think it is just some randomly chosen number. """ From eb4917aea94d78ea64fa90f0c70501bbb6d48b37 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 4 Oct 2011 08:47:26 +0000 Subject: [PATCH 965/974] [master] use c-style cast for functionptr cast --- src/lib/datasrc/factory.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc index 8ccf27fd39..eddd4f41c1 100644 --- a/src/lib/datasrc/factory.cc +++ b/src/lib/datasrc/factory.cc @@ -61,10 +61,14 @@ DataSourceClientContainer::DataSourceClientContainer(const std::string& type, ConstElementPtr config) : ds_lib_(type + "_ds.so") { - ds_creator* ds_create = - reinterpret_cast(ds_lib_.getSym("createInstance")); - destructor_ = - reinterpret_cast(ds_lib_.getSym("destroyInstance")); + // We are casting from a data to a function pointer here + // Some compilers (rightfully) complain about that, but + // c-style casts are accepted the most here. If we run + // into any that also don't like this, we might need to + // use some form of union cast or memory copy to get + // from the void* to the function pointer. + ds_creator* ds_create = (ds_creator*)ds_lib_.getSym("createInstance"); + destructor_ = (ds_destructor*)ds_lib_.getSym("destroyInstance"); instance_ = ds_create(config); } From fe76209cd8ad96144f0e2fc9522f5fda1d52d9c3 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 4 Oct 2011 07:33:05 -0400 Subject: [PATCH 966/974] [1144] read instead of peek in rdata/generic/detail/ds_like.h --- src/lib/dns/rdata/generic/detail/ds_like.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h index 1061ece2f1..b5a35cd967 100644 --- a/src/lib/dns/rdata/generic/detail/ds_like.h +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -87,7 +87,7 @@ public: RRType(typeCode) << " digest type out of range"); } - peekc = iss.peek(); + iss.read(&peekc, 1); if (!iss.good() || !isspace(peekc, iss.getloc())) { isc_throw(InvalidRdataText, RRType(typeCode) << " presentation format error"); From 956e210239d46bebe4574c5ca38b3b51b1bb7c65 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Tue, 4 Oct 2011 07:33:55 -0400 Subject: [PATCH 967/974] [1144] more comparison tests in tests/rdata_ds_like_unittest.cc --- src/lib/dns/tests/rdata_ds_like_unittest.cc | 34 ++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc index 95610e1edc..db25aead80 100644 --- a/src/lib/dns/tests/rdata_ds_like_unittest.cc +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -125,11 +125,43 @@ TYPED_TEST(Rdata_DS_LIKE_Test, toWireBuffer) { rdata_ds_like.toWire(this->obuffer); } +string ds_like_txt1("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different tag +string ds_like_txt2("12893 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different algorithm +string ds_like_txt3("12892 6 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different digest type +string ds_like_txt4("12892 5 3 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different digest +string ds_like_txt5("12892 5 2 F2E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different digest length +string ds_like_txt6("12892 5 2 F2E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B555"); + TYPED_TEST(Rdata_DS_LIKE_Test, compare) { // trivial case: self equivalence EXPECT_EQ(0, TypeParam(ds_like_txt).compare(TypeParam(ds_like_txt))); - // TODO: need more tests + // non-equivalence tests + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt2)), 0); + EXPECT_GT(TypeParam(ds_like_txt2).compare(TypeParam(ds_like_txt1)), 0); + + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt3)), 0); + EXPECT_GT(TypeParam(ds_like_txt3).compare(TypeParam(ds_like_txt1)), 0); + + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt4)), 0); + EXPECT_GT(TypeParam(ds_like_txt4).compare(TypeParam(ds_like_txt1)), 0); + + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt5)), 0); + EXPECT_GT(TypeParam(ds_like_txt5).compare(TypeParam(ds_like_txt1)), 0); + + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt6)), 0); + EXPECT_GT(TypeParam(ds_like_txt6).compare(TypeParam(ds_like_txt1)), 0); } } From 33c0d21361655c08b274c75736b7bcbe99dd3d2d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 4 Oct 2011 10:30:31 -0700 Subject: [PATCH 968/974] [1144] added one more test case: comparing incompatible types of RDATAs --- src/lib/dns/tests/rdata_ds_like_unittest.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc index db25aead80..d49e22987e 100644 --- a/src/lib/dns/tests/rdata_ds_like_unittest.cc +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -162,6 +162,10 @@ TYPED_TEST(Rdata_DS_LIKE_Test, compare) { EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt6)), 0); EXPECT_GT(TypeParam(ds_like_txt6).compare(TypeParam(ds_like_txt1)), 0); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(this->rdata_ds_like.compare(*RdataTest::rdata_nomatch), + bad_cast); } } From af927e2c390b49012b276c11991a3f7ef3a592a9 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 4 Oct 2011 10:34:09 +0200 Subject: [PATCH 969/974] [1259] More TTL tweaks These are just tests and logging messages. --- src/lib/python/isc/xfrin/libxfrin_messages.mes | 7 +++---- src/lib/python/isc/xfrin/tests/diff_tests.py | 7 +++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lib/python/isc/xfrin/libxfrin_messages.mes b/src/lib/python/isc/xfrin/libxfrin_messages.mes index 8bfb7b1ee5..be943c86d6 100644 --- a/src/lib/python/isc/xfrin/libxfrin_messages.mes +++ b/src/lib/python/isc/xfrin/libxfrin_messages.mes @@ -15,8 +15,7 @@ # No namespace declaration - these constants go in the global namespace # of the libxfrin_messages python module. -% LIBXFRIN_DIFFERENT_TTL multiple data with different TTLs (%1, %2) on %3/%4 +% LIBXFRIN_DIFFERENT_TTL multiple data with different TTLs (%1, %2) on %3/%4. Adjusting %2 -> %1. The xfrin module received an update containing multiple rdata changes for the -same RRset. But the TTLs of these don't match each other. The data will be put -into the underlying data source. What happens with the TTLs is up to the -data source. +same RRset. But the TTLs of these don't match each other. As we combine them +together, the later one get's overwritten to the earlier one in the sequence. diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py index d431f4210b..9652a1a772 100644 --- a/src/lib/python/isc/xfrin/tests/diff_tests.py +++ b/src/lib/python/isc/xfrin/tests/diff_tests.py @@ -418,9 +418,16 @@ class DiffTest(unittest.TestCase): self.__type, RRTTL(120)) rrset2.add_rdata(Rdata(self.__type, self.__rrclass, '192.10.2.2')) diff.add_data(rrset2) + rrset2 = RRset(Name('a.example.org.'), self.__rrclass, + self.__type, RRTTL(6000)) + rrset2.add_rdata(Rdata(self.__type, self.__rrclass, '192.10.2.3')) + diff.add_data(rrset2) # They should get compacted together and complain. diff.compact() self.assertEqual(1, len(diff.get_buffer())) + # The TTL stays on the first value, no matter if smaller or bigger + # ones come later. + self.assertEqual(self.__ttl, diff.get_buffer()[0][1].get_ttl()) self.assertTrue(self.__warn_called) finally: isc.xfrin.diff.logger = orig_logger From 743dad9408b0a86052156e6a3d4fec1001600017 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 4 Oct 2011 19:57:02 +0200 Subject: [PATCH 970/974] [1259] Add a note about the library name --- src/lib/python/isc/xfrin/diff.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py index 26b2f83a7e..b6d824468f 100644 --- a/src/lib/python/isc/xfrin/diff.py +++ b/src/lib/python/isc/xfrin/diff.py @@ -16,6 +16,10 @@ """ This helps the XFR in process with accumulating parts of diff and applying it to the datasource. + +The name of the module is not yet fully decided. We might want to move it +under isc.datasrc or somewhere else, because we might want to reuse it with +future DDNS process. But until then, it lives here. """ import isc.dns From 7209be736accd15885ad7eafc23b36eec18c2213 Mon Sep 17 00:00:00 2001 From: Dima Volodin Date: Wed, 5 Oct 2011 07:30:39 -0400 Subject: [PATCH 971/974] [1144] ChangeLog entry --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 96aaf0be56..107679b4e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ 292. [func] dvv Implement the DLV rrtype according to RFC4431. - (Trac #1144, git TBD) + (Trac #1144, git d267c0511a07c41cd92e3b0b9ee9bf693743a7cf) 291. [func] naokikambe Statistics items are specified by each module's spec file. From f5d7359a945241edf986b7c91c0ad6c7bcf113e3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 5 Oct 2011 18:25:25 +0000 Subject: [PATCH 972/974] [master] fixed a build regression on g++(3.x)/Solaris. The original code doesn't seem to work in a templated test for this compiler. okayed on jabber. --- src/lib/dns/tests/rdata_ds_like_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc index d49e22987e..9b294460d6 100644 --- a/src/lib/dns/tests/rdata_ds_like_unittest.cc +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -164,7 +164,7 @@ TYPED_TEST(Rdata_DS_LIKE_Test, compare) { EXPECT_GT(TypeParam(ds_like_txt6).compare(TypeParam(ds_like_txt1)), 0); // comparison attempt between incompatible RR types should be rejected - EXPECT_THROW(this->rdata_ds_like.compare(*RdataTest::rdata_nomatch), + EXPECT_THROW(this->rdata_ds_like.compare(*this->rdata_nomatch), bad_cast); } From a9b140ed88b9a25f47e5649b635c8a19e81bfdee Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 5 Oct 2011 14:39:27 -0700 Subject: [PATCH 973/974] [master] use freeaddrinfo to free an addrinfo structure, not free. reported by Francis on bind10-dev. looks okay, directly pushing. --- src/lib/asiolink/tests/io_endpoint_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/asiolink/tests/io_endpoint_unittest.cc b/src/lib/asiolink/tests/io_endpoint_unittest.cc index f0279d18af..c7283ec80d 100644 --- a/src/lib/asiolink/tests/io_endpoint_unittest.cc +++ b/src/lib/asiolink/tests/io_endpoint_unittest.cc @@ -219,7 +219,7 @@ sockAddrMatch(const struct sockaddr& actual_sa, res->ai_addr->sa_len = actual_sa.sa_len; #endif EXPECT_EQ(0, memcmp(res->ai_addr, &actual_sa, res->ai_addrlen)); - free(res); + freeaddrinfo(res); } TEST(IOEndpointTest, getSockAddr) { From 85071d50cf5e1a569b447ba00e118db04293475a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 5 Oct 2011 20:34:40 -0700 Subject: [PATCH 974/974] [master] disabled factory_unittest if we use static link. this should solve buildbot failure: http://git.bind10.isc.org/~tester/builder/BIND10/20111005214500-MacOSX10.6-x86_64-Clang-Static/logs/unittests.out okayed on jabber. --- src/lib/datasrc/tests/Makefile.am | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index 10e1add99f..3183b1d4f7 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -35,7 +35,13 @@ run_unittests_SOURCES += logger_unittest.cc run_unittests_SOURCES += database_unittest.cc run_unittests_SOURCES += client_unittest.cc run_unittests_SOURCES += sqlite3_accessor_unittest.cc +if !USE_STATIC_LINK +# This test uses dynamically loadable module. It will cause various +# troubles with static link such as "missing" symbols in the static object +# for the module. As a workaround we disable this particualr test +# in this case. run_unittests_SOURCES += factory_unittest.cc +endif # for the dlopened types we have tests for, we also need to include the # sources run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
Owner Title Value
DummyFoo
Dummy