diff --git a/ChangeLog b/ChangeLog
index c59c9ec34d..6fb15b6c20 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,35 @@
+347. [bug] jelte
+ Fixed a bug where adding Zonemgr/secondary_zones without explicitely
+ setting the class value of the added zone resulted in a cryptic
+ error in bindctl ("Error: class"). It will now correctly default to
+ IN if not set. This also adds better checks on the name and class
+ values, and better errors if they are bad.
+ (Trac #1414, git 7b122af8489acf0f28f935a19eca2c5509a3677f)
+
+346. [build]* jreed
+ Renamed libdhcp to libdhcp++.
+ (Trac #1446, git d394e64f4c44f16027b1e62b4ac34e054b49221d)
+
+345. [func] tomek
+ dhcp4: Dummy DHCPv4 component implemented. Currently it does
+ nothing useful, except providing skeleton implementation that can
+ be expanded in the future.
+ (Trac #992, git d6e33479365c8f8f62ef2b9aa5548efe6b194601)
+
+344. [func] y-aharen
+ src/lib/statistics: Added statistics counter library for entire server
+ items and per zone items. Also, modified b10-auth to use it. It is
+ also intended to use in the other modules such as b10-resolver.
+ (Trac #510, git afddaf4c5718c2a0cc31f2eee79c4e0cc625499f)
+
+343. [func] jelte
+ Added IXFR-out system tests, based on the first two test sets of
+ http://bind10.isc.org/wiki/IxfrSystemTests.
+ (Trac #1314, git 1655bed624866a766311a01214597db01b4c7cec)
+
342. [bug] stephen
In the resolver, a FORMERR received from an upstream nameserver
- now rsults in a SERVFAIL being returned as a response to the original
+ now results in a SERVFAIL being returned as a response to the original
query. Additional debug messages added to distinguish between
different errors in packets received from upstream nameservers.
(Trac #1383, git 9b2b249d23576c999a65d8c338e008cabe45f0c9)
@@ -69,12 +98,12 @@
potential problems and were fixed.
(Trac #1389, git 3fdce88046bdad392bd89ea656ec4ac3c858ca2f)
-333. [bug] dvv
- Solaris needs "-z now" to force non-lazy binding and prevent g++ static
- initialization code from deadlocking.
+333. [bug] dvv
+ Solaris needs "-z now" to force non-lazy binding and prevent
+ g++ static initialization code from deadlocking.
(Trac #1439, git c789138250b33b6b08262425a08a2a0469d90433)
-332. [bug] vorner
+332. [bug] vorner
C++ exceptions in the isc.dns.Rdata wrapper are now converted
to python ones instead of just aborting the interpretter.
(Trac #1407, git 5b64e839be2906b8950f5b1e42a3fadd72fca033)
@@ -109,7 +138,7 @@ bind10-devel-20111128 released on November 28, 2011
always respond to IXFR requests according to RFC1995).
(Trac #1371 and #1372, git 80c131f5b0763753d199b0fb9b51f10990bcd92b)
-326. [build]* jinmei
+326. [build]* jinmei
Added a check script for the SQLite3 schema version. It will be
run at the beginning of 'make install', and if it detects an old
version of schema, installation will stop. You'll then need to
diff --git a/configure.ac b/configure.ac
index e370e21171..671a9b6769 100644
--- a/configure.ac
+++ b/configure.ac
@@ -730,7 +730,7 @@ then
GTEST_FOUND="true"
# There is no gtest-config script on this
# system, which is supposed to inform us
- # whether we need pthreads as well (a
+ # whether we need pthreads as well (a
# gtest compile-time option). So we still
# need to test that manually.
CPPFLAGS_SAVED="$CPPFLAGS"
@@ -892,6 +892,8 @@ AC_CONFIG_FILES([Makefile
src/bin/auth/benchmarks/Makefile
src/bin/dhcp6/Makefile
src/bin/dhcp6/tests/Makefile
+ src/bin/dhcp4/Makefile
+ src/bin/dhcp4/tests/Makefile
src/bin/resolver/Makefile
src/bin/resolver/tests/Makefile
src/bin/sockcreator/Makefile
@@ -984,6 +986,8 @@ AC_CONFIG_FILES([Makefile
src/lib/util/tests/Makefile
src/lib/acl/Makefile
src/lib/acl/tests/Makefile
+ src/lib/statistics/Makefile
+ src/lib/statistics/tests/Makefile
tests/Makefile
tests/system/Makefile
tests/tools/Makefile
@@ -1031,6 +1035,7 @@ AC_OUTPUT([doc/version.ent
src/bin/msgq/run_msgq.sh
src/bin/auth/auth.spec.pre
src/bin/auth/spec_config.h.pre
+ src/bin/dhcp4/spec_config.h.pre
src/bin/dhcp6/spec_config.h.pre
src/bin/tests/process_rename_test.py
src/lib/config/tests/data_def_unittests_config.h
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index e61725f9e8..b1861111fd 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -1502,6 +1502,49 @@ what if a NOTIFY is sent?
-->
+
+ Secondary Manager
+
+
+ The b10-zonemgr process is started by
+ bind10 .
+ It keeps track of SOA refresh, retry, and expire timers
+ and other details for BIND 10 to perform as a slave.
+ When the b10-auth authoritative DNS server
+ receives a NOTIFY message, b10-zonemgr
+ may tell b10-xfrin to do a refresh
+ to start an inbound zone transfer.
+ The secondary manager resets its counters when a new zone is
+ transferred in.
+
+
+
+ Access control (such as allowing notifies) is not yet provided.
+ The primary/secondary service is not yet complete.
+
+
+
+ The following example shows using bindctl
+ to configure the server to be a secondary for the example zone:
+
+ > config add Zonemgr/secondary_zones
+> config set Zonemgr/secondary_zones[0]/name "example.com "
+> config set Zonemgr/secondary_zones[0]/class "IN "
+> config commit
+
+
+
+
+
+
+ If the zone does not exist in the data source already
+ (i.e. no SOA record for it), b10-zonemgr
+ will automatically tell b10-xfrin
+ to transfer the zone in.
+
+
+
+
Trigger an Incoming Zone Transfer Manually
@@ -1514,7 +1557,6 @@ what if a NOTIFY is sent?
-
@@ -1606,31 +1648,6 @@ what is XfroutClient xfr_client??
-
- Secondary Manager
-
-
- The b10-zonemgr process is started by
- bind10 .
- It keeps track of SOA refresh, retry, and expire timers
- and other details for BIND 10 to perform as a slave.
- When the b10-auth authoritative DNS server
- receives a NOTIFY message, b10-zonemgr
- may tell b10-xfrin to do a refresh
- to start an inbound zone transfer.
- The secondary manager resets its counters when a new zone is
- transferred in.
-
-
-
- Access control (such as allowing notifies) is not yet provided.
- The primary/secondary service is not yet complete.
-
-
-
-
-
-
Recursive Name Server
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 06d8df2e76..ba7ae812aa 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1,4 +1,4 @@
SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
- usermgr zonemgr stats tests resolver sockcreator dhcp6
+ usermgr zonemgr stats tests resolver sockcreator dhcp6 dhcp4
check-recursive: all-recursive
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 4d8ec833bd..3d604321dd 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -71,6 +71,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
+b10_auth_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
b10_auth_LDADD += $(SQLITE_LIBS)
# TODO: config.h.in is wrong because doesn't honor pkgdatadir
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index caf69b9a7c..da05e484c8 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -671,9 +671,9 @@ void
AuthSrvImpl::incCounter(const int protocol) {
// Increment query counter.
if (protocol == IPPROTO_UDP) {
- counters_.inc(AuthCounters::COUNTER_UDP_QUERY);
+ counters_.inc(AuthCounters::SERVER_UDP_QUERY);
} else if (protocol == IPPROTO_TCP) {
- counters_.inc(AuthCounters::COUNTER_TCP_QUERY);
+ counters_.inc(AuthCounters::SERVER_TCP_QUERY);
} else {
// unknown protocol
isc_throw(Unexpected, "Unknown protocol: " << protocol);
@@ -766,7 +766,7 @@ bool AuthSrv::submitStatistics() const {
}
uint64_t
-AuthSrv::getCounter(const AuthCounters::CounterType type) const {
+AuthSrv::getCounter(const AuthCounters::ServerCounterType type) const {
return (impl_->counters_.getCounter(type));
}
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index f2259a2994..a50e42759e 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -343,7 +343,7 @@ public:
/// \param type Type of a counter to get the value of
///
/// \return the value of the counter.
- uint64_t getCounter(const AuthCounters::CounterType type) const;
+ uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
/**
* \brief Set and get the addresses we listen on.
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index dd00ea53d5..da6a5c83a8 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -35,5 +35,6 @@ query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
+query_bench_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
query_bench_LDADD += $(SQLITE_LIBS)
diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc
index e62719f7e2..7397a50f09 100644
--- a/src/bin/auth/statistics.cc
+++ b/src/bin/auth/statistics.cc
@@ -18,48 +18,67 @@
#include
#include
+#include
+#include
+
#include
#include
+#include
+
using namespace isc::auth;
+using namespace isc::statistics;
// TODO: We need a namespace ("auth_server"?) to hold
// AuthSrv and AuthCounters.
-class AuthCountersImpl {
-private:
- // prohibit copy
- AuthCountersImpl(const AuthCountersImpl& source);
- AuthCountersImpl& operator=(const AuthCountersImpl& source);
+// TODO: Make use of wrappers like isc::dns::Opcode
+// for counter item type.
+
+class AuthCountersImpl : boost::noncopyable {
public:
AuthCountersImpl();
~AuthCountersImpl();
- void inc(const AuthCounters::CounterType type);
+ void inc(const AuthCounters::ServerCounterType type);
+ void inc(const std::string& zone,
+ const AuthCounters::PerZoneCounterType 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;
+ uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
private:
- std::vector counters_;
+ Counter server_counter_;
+ CounterDictionary per_zone_counter_;
isc::cc::AbstractSession* statistics_session_;
AuthCounters::validator_type validator_;
};
AuthCountersImpl::AuthCountersImpl() :
// initialize counter
- // size: AuthCounters::COUNTER_TYPES, initial value: 0
- counters_(AuthCounters::COUNTER_TYPES, 0),
+ // size of server_counter_: AuthCounters::SERVER_COUNTER_TYPES
+ // size of per_zone_counter_: AuthCounters::PER_ZONE_COUNTER_TYPES
+ server_counter_(AuthCounters::SERVER_COUNTER_TYPES),
+ per_zone_counter_(AuthCounters::PER_ZONE_COUNTER_TYPES),
statistics_session_(NULL)
-{}
+{
+ per_zone_counter_.addElement("_SERVER_");
+}
AuthCountersImpl::~AuthCountersImpl()
{}
void
-AuthCountersImpl::inc(const AuthCounters::CounterType type) {
- ++counters_.at(type);
+AuthCountersImpl::inc(const AuthCounters::ServerCounterType type) {
+ server_counter_.inc(type);
+}
+
+void
+AuthCountersImpl::inc(const std::string& zone,
+ const AuthCounters::PerZoneCounterType type)
+{
+ per_zone_counter_[zone].inc(type);
}
bool
@@ -73,9 +92,9 @@ AuthCountersImpl::submitStatistics() const {
<< "{ \"owner\": \"Auth\","
<< " \"data\":"
<< "{ \"queries.udp\": "
- << counters_.at(AuthCounters::COUNTER_UDP_QUERY)
+ << server_counter_.get(AuthCounters::SERVER_UDP_QUERY)
<< ", \"queries.tcp\": "
- << counters_.at(AuthCounters::COUNTER_TCP_QUERY)
+ << server_counter_.get(AuthCounters::SERVER_TCP_QUERY)
<< " }"
<< "}"
<< "]}";
@@ -126,19 +145,17 @@ AuthCountersImpl::registerStatisticsValidator
// Currently for testing purpose only
uint64_t
-AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const {
- return (counters_.at(type));
+AuthCountersImpl::getCounter(const AuthCounters::ServerCounterType type) const {
+ return (server_counter_.get(type));
}
AuthCounters::AuthCounters() : impl_(new AuthCountersImpl())
{}
-AuthCounters::~AuthCounters() {
- delete impl_;
-}
+AuthCounters::~AuthCounters() {}
void
-AuthCounters::inc(const AuthCounters::CounterType type) {
+AuthCounters::inc(const AuthCounters::ServerCounterType type) {
impl_->inc(type);
}
@@ -155,7 +172,7 @@ AuthCounters::setStatisticsSession
}
uint64_t
-AuthCounters::getCounter(const AuthCounters::CounterType type) const {
+AuthCounters::getCounter(const AuthCounters::ServerCounterType type) const {
return (impl_->getCounter(type));
}
diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h
index c930414c65..280b4a5d8f 100644
--- a/src/bin/auth/statistics.h
+++ b/src/bin/auth/statistics.h
@@ -17,6 +17,7 @@
#include
#include
+#include
class AuthCountersImpl;
@@ -51,13 +52,18 @@ class AuthCountersImpl;
/// \todo Consider overhead of \c AuthCounters::inc()
class AuthCounters {
private:
- AuthCountersImpl* impl_;
+ boost::scoped_ptr impl_;
public:
// Enum for the type of counter
- enum CounterType {
- COUNTER_UDP_QUERY = 0, ///< COUNTER_UDP_QUERY: counter for UDP queries
- COUNTER_TCP_QUERY = 1, ///< COUNTER_TCP_QUERY: counter for TCP queries
- COUNTER_TYPES = 2 ///< The number of defined counters
+ enum ServerCounterType {
+ SERVER_UDP_QUERY, ///< SERVER_UDP_QUERY: counter for UDP queries
+ SERVER_TCP_QUERY, ///< SERVER_TCP_QUERY: counter for TCP queries
+ SERVER_COUNTER_TYPES ///< The number of defined counters
+ };
+ enum PerZoneCounterType {
+ ZONE_UDP_QUERY, ///< ZONE_UDP_QUERY: counter for UDP queries
+ ZONE_TCP_QUERY, ///< ZONE_TCP_QUERY: counter for TCP queries
+ PER_ZONE_COUNTER_TYPES ///< The number of defined counters
};
/// The constructor.
///
@@ -77,9 +83,9 @@ public:
///
/// \throw std::out_of_range \a type is unknown.
///
- /// usage: counter.inc(CounterType::COUNTER_UDP_QUERY);
+ /// usage: counter.inc(AuthCounters::SERVER_UDP_QUERY);
///
- void inc(const CounterType type);
+ void inc(const ServerCounterType type);
/// \brief Submit statistics counters to statistics module.
///
@@ -130,7 +136,7 @@ public:
///
/// \return the value of the counter specified by \a type.
///
- uint64_t getCounter(const AuthCounters::CounterType type) const;
+ uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
/// \brief A type of validation function for the specification in
/// isc::config::ModuleSpec.
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index d27386e62e..b5b96d7b24 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -65,6 +65,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index ac25cd60d9..d90006a88d 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -779,7 +779,7 @@ TEST_F(AuthSrvTest, cacheSlots) {
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
// The counter should be initialized to 0.
- EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
+ EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
// Create UDP message and process.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("example.com"),
@@ -788,13 +788,13 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
// After processing UDP query, the counter should be 1.
- EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
+ EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
}
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
// The counter should be initialized to 0.
- EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
// Create TCP message and process.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("example.com"),
@@ -803,13 +803,13 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
// After processing TCP query, the counter should be 1.
- EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
// The counter should be initialized to 0.
- EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
@@ -818,13 +818,13 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP AXFR query, the counter should be 1.
- EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
// Submit TCP IXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
// The counter should be initialized to 0.
- EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
@@ -833,7 +833,7 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP IXFR query, the counter should be 1.
- EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
// class for queryCounterUnexpected test
diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc
index 98e573b495..3f19f911f6 100644
--- a/src/bin/auth/tests/statistics_unittest.cc
+++ b/src/bin/auth/tests/statistics_unittest.cc
@@ -150,25 +150,24 @@ AuthCountersTest::MockSession::setThrowSessionTimeout(bool flag) {
TEST_F(AuthCountersTest, incrementUDPCounter) {
// The counter should be initialized to 0.
- EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
- EXPECT_NO_THROW(counters.inc(AuthCounters::COUNTER_UDP_QUERY));
+ EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
+ EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_UDP_QUERY));
// After increment, the counter should be 1.
- EXPECT_EQ(1, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
+ EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
}
TEST_F(AuthCountersTest, incrementTCPCounter) {
// The counter should be initialized to 0.
- EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
- EXPECT_NO_THROW(counters.inc(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
+ EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_TCP_QUERY));
// After increment, the counter should be 1.
- EXPECT_EQ(1, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
TEST_F(AuthCountersTest, incrementInvalidCounter) {
- // Expect to throw isc::InvalidParameter if the type of the counter is
- // invalid.
- EXPECT_THROW(counters.inc(AuthCounters::COUNTER_TYPES),
- std::out_of_range);
+ // Expect to throw an isc::OutOfRange
+ EXPECT_THROW(counters.inc(AuthCounters::SERVER_COUNTER_TYPES),
+ isc::OutOfRange);
}
TEST_F(AuthCountersTest, submitStatisticsWithoutSession) {
@@ -195,14 +194,14 @@ TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
// Validate if it submits correct data.
// Counters should be initialized to 0.
- EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
- EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
+ EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
// UDP query counter is set to 2.
- counters.inc(AuthCounters::COUNTER_UDP_QUERY);
- counters.inc(AuthCounters::COUNTER_UDP_QUERY);
+ counters.inc(AuthCounters::SERVER_UDP_QUERY);
+ counters.inc(AuthCounters::SERVER_UDP_QUERY);
// TCP query counter is set to 1.
- counters.inc(AuthCounters::COUNTER_TCP_QUERY);
+ counters.inc(AuthCounters::SERVER_TCP_QUERY);
counters.submitStatistics();
// Destination is "Stats".
@@ -237,14 +236,14 @@ TEST_F(AuthCountersTest, submitStatisticsWithValidator) {
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));
+ EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
+ EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
// UDP query counter is set to 2.
- counters.inc(AuthCounters::COUNTER_UDP_QUERY);
- counters.inc(AuthCounters::COUNTER_UDP_QUERY);
+ counters.inc(AuthCounters::SERVER_UDP_QUERY);
+ counters.inc(AuthCounters::SERVER_UDP_QUERY);
// TCP query counter is set to 1.
- counters.inc(AuthCounters::COUNTER_TCP_QUERY);
+ counters.inc(AuthCounters::SERVER_TCP_QUERY);
// checks the value returned by submitStatistics
EXPECT_TRUE(counters.submitStatistics());
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
new file mode 100644
index 0000000000..513ae1cca2
--- /dev/null
+++ b/src/bin/dhcp4/Makefile.am
@@ -0,0 +1,43 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+CLEANFILES = spec_config.h
+
+man_MANS = b10-dhcp4.8
+EXTRA_DIST = $(man_MANS) dhcp4.spec
+
+if ENABLE_MAN
+
+b10-dhcp4.8: b10-dhcp4.xml
+ xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-dhcp4.xml
+
+endif
+
+spec_config.h: spec_config.h.pre
+ $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
+
+BUILT_SOURCES = spec_config.h
+pkglibexec_PROGRAMS = b10-dhcp4
+
+b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h
+
+b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/liblog.la
+
+# TODO: config.h.in is wrong because doesn't honor pkgdatadir
+# and can't use @datadir@ because doesn't expand default ${prefix}
+b10_dhcp4dir = $(pkgdatadir)
+b10_dhcp4_DATA = dhcp4.spec
diff --git a/src/bin/dhcp4/b10-dhcp4.8 b/src/bin/dhcp4/b10-dhcp4.8
new file mode 100644
index 0000000000..97bdeb8b73
--- /dev/null
+++ b/src/bin/dhcp4/b10-dhcp4.8
@@ -0,0 +1,60 @@
+'\" t
+.\" Title: b10-dhcp4
+.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2
+.\" Date: October 27, 2011
+.\" Manual: BIND10
+.\" Source: BIND10
+.\" Language: English
+.\"
+.TH "B10\-DHCP4" "8" "October 27, 2011" "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 '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-dhcp4 \- DHCPv4 server in BIND 10 architecture
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-dhcp4\fR\ 'u
+\fBb10\-dhcp4\fR [\fB\-v\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-dhcp4\fR
+daemon will provide the DHCPv4 server implementation when it becomes functional\&.
+.SH "ARGUMENTS"
+.PP
+The arguments are as follows:
+.PP
+\fB\-v\fR
+.RS 4
+Enable verbose mode\&.
+.RE
+.SH "SEE ALSO"
+.PP
+
+\fBbind10\fR(8)\&.
+.SH "HISTORY"
+.PP
+The
+\fBb10\-dhcp4\fR
+daemon was first coded in November 2011 by Tomek Mrugalski\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/src/bin/dhcp4/b10-dhcp4.xml b/src/bin/dhcp4/b10-dhcp4.xml
new file mode 100644
index 0000000000..370fa046cc
--- /dev/null
+++ b/src/bin/dhcp4/b10-dhcp4.xml
@@ -0,0 +1,98 @@
+]>
+
+
+
+
+
+ October 27, 2011
+
+
+
+ b10-dhcp4
+ 8
+ BIND10
+
+
+
+ b10-dhcp4
+ DHCPv4 server in BIND 10 architecture
+
+
+
+
+ 2011
+ Internet Systems Consortium, Inc. ("ISC")
+
+
+
+
+
+ b10-dhcp4
+ -v
+
+
+
+
+ DESCRIPTION
+
+ The b10-dhcp4 daemon will provide the
+ DHCPv4 server implementation when it becomes functional.
+
+
+
+
+
+ ARGUMENTS
+
+ The arguments are as follows:
+
+
+
+
+ -v
+
+ Enable verbose mode.
+
+
+
+
+
+
+
+
+ SEE ALSO
+
+
+ bind10 8
+ .
+
+
+
+
+ HISTORY
+
+ The b10-dhcp4 daemon was first coded in
+ November 2011 by Tomek Mrugalski.
+
+
+
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
new file mode 100644
index 0000000000..8061fd2261
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -0,0 +1,14 @@
+{
+ "module_spec": {
+ "module_name": "dhcp4",
+ "module_description": "DHCPv4 server daemon",
+ "config_data": [
+ { "item_name": "interface",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "eth0"
+ }
+ ],
+ "commands": []
+ }
+}
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
new file mode 100644
index 0000000000..9686a35499
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -0,0 +1,154 @@
+// 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
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
+ cout << "Initialization: opening sockets on port " << port << endl;
+
+ // first call to instance() will create IfaceMgr (it's a singleton)
+ // it may throw something if things go wrong
+ IfaceMgr::instance();
+
+ /// @todo: instantiate LeaseMgr here once it is imlpemented.
+
+ setServerID();
+
+ shutdown_ = false;
+}
+
+Dhcpv4Srv::~Dhcpv4Srv() {
+ cout << "DHCPv4 server shutdown." << endl;
+}
+
+bool
+Dhcpv4Srv::run() {
+ while (!shutdown_) {
+ boost::shared_ptr query; // client's message
+ boost::shared_ptr rsp; // server's response
+
+#if 0
+ // uncomment this once ticket 1239 is merged.
+ query = IfaceMgr::instance().receive4();
+#endif
+
+ if (query) {
+ if (!query->unpack()) {
+ cout << "Failed to parse incoming packet" << endl;
+ continue;
+ }
+ switch (query->getType()) {
+ case DHCPDISCOVER:
+ rsp = processDiscover(query);
+ break;
+ case DHCPREQUEST:
+ rsp = processRequest(query);
+ break;
+ case DHCPRELEASE:
+ processRelease(query);
+ break;
+ case DHCPDECLINE:
+ processDecline(query);
+ break;
+ case DHCPINFORM:
+ processInform(query);
+ break;
+ default:
+ cout << "Unknown pkt type received:"
+ << query->getType() << endl;
+ }
+
+ cout << "Received " << query->len() << " bytes packet type="
+ << query->getType() << endl;
+
+ // TODO: print out received packets only if verbose (or debug)
+ // mode is enabled
+ cout << query->toText();
+
+ if (rsp) {
+ rsp->setRemoteAddr(query->getRemoteAddr());
+ rsp->setLocalAddr(query->getLocalAddr());
+ rsp->setRemotePort(DHCP4_CLIENT_PORT);
+ rsp->setLocalPort(DHCP4_SERVER_PORT);
+ rsp->setIface(query->getIface());
+ rsp->setIndex(query->getIndex());
+
+ cout << "Replying with:" << rsp->getType() << endl;
+ cout << rsp->toText();
+ cout << "----" << endl;
+ if (rsp->pack()) {
+ cout << "Packet assembled correctly." << endl;
+ }
+#if 0
+ // uncomment this once ticket 1240 is merged.
+ IfaceMgr::instance().send4(rsp);
+#endif
+ }
+ }
+
+ // TODO add support for config session (see src/bin/auth/main.cc)
+ // so this daemon can be controlled from bob
+ }
+
+ return (true);
+}
+
+void
+Dhcpv4Srv::setServerID() {
+ /// TODO implement this for real once interface detection (ticket 1237)
+ /// is done. Use hardcoded server-id for now.
+
+#if 0
+ // uncomment this once ticket 1350 is merged.
+ IOAddress srvId("127.0.0.1");
+ serverid_ = boost::shared_ptr(
+ new Option4AddrLst(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, srvId));
+#endif
+}
+
+boost::shared_ptr
+Dhcpv4Srv::processDiscover(boost::shared_ptr& discover) {
+ /// TODO: Currently implemented echo mode. Implement this for real
+ return (discover);
+}
+
+boost::shared_ptr
+Dhcpv4Srv::processRequest(boost::shared_ptr& request) {
+ /// TODO: Currently implemented echo mode. Implement this for real
+ return (request);
+}
+
+void Dhcpv4Srv::processRelease(boost::shared_ptr& release) {
+ /// TODO: Implement this.
+ cout << "Received RELEASE on " << release->getIface() << " interface." << endl;
+}
+
+void Dhcpv4Srv::processDecline(boost::shared_ptr& decline) {
+ /// TODO: Implement this.
+ cout << "Received DECLINE on " << decline->getIface() << " interface." << endl;
+}
+
+boost::shared_ptr Dhcpv4Srv::processInform(boost::shared_ptr& inform) {
+ /// TODO: Currently implemented echo mode. Implement this for real
+ return (inform);
+}
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
new file mode 100644
index 0000000000..033ac5a193
--- /dev/null
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -0,0 +1,137 @@
+// 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 DHCPV4_SRV_H
+#define DHCPV4_SRV_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace isc {
+
+namespace dhcp {
+/// @brief DHCPv4 server service.
+///
+/// This singleton class represents DHCPv4 server. It contains all
+/// top-level methods and routines necessary for server operation.
+/// In particular, it instantiates IfaceMgr, loads or generates DUID
+/// that is going to be used as server-identifier, receives incoming
+/// packets, processes them, manages leases assignment and generates
+/// appropriate responses.
+class Dhcpv4Srv : public boost::noncopyable {
+
+ public:
+ /// @brief Default constructor.
+ ///
+ /// Instantiates necessary services, required to run DHCPv6 server.
+ /// In particular, creates IfaceMgr that will be responsible for
+ /// network interaction. Will instantiate lease manager, and load
+ /// old or create new DUID. It is possible to specify alternate
+ /// port on which DHCPv4 server will listen on. That is mostly useful
+ /// for testing purposes.
+ ///
+ /// @param port specifies port number to listen on
+ Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT);
+
+ /// @brief Destructor. Used during DHCPv6 service shutdown.
+ ~Dhcpv4Srv();
+
+ /// @brief Main server processing loop.
+ ///
+ /// Main server processing loop. Receives incoming packets, verifies
+ /// their correctness, generates appropriate answer (if needed) and
+ /// transmits respones.
+ ///
+ /// @return true, if being shut down gracefully, fail if experienced
+ /// critical error.
+ bool run();
+
+protected:
+ /// @brief Processes incoming DISCOVER and returns response.
+ ///
+ /// Processes received DISCOVER message and verifies that its sender
+ /// should be served. In particular, a lease is selected and sent
+ /// as an offer to a client if it should be served.
+ ///
+ /// @param solicit DISCOVER message received from client
+ ///
+ /// @return OFFER message or NULL
+ boost::shared_ptr
+ processDiscover(boost::shared_ptr& discover);
+
+ /// @brief Processes incoming REQUEST and returns REPLY response.
+ ///
+ /// Processes incoming REQUEST message and verifies that its sender
+ /// should be served. In particular, verifies that requested lease
+ /// is valid, not expired, not reserved, not used by other client and
+ /// that requesting client is allowed to use it.
+ ///
+ /// Returns ACK message, NACK message, or NULL
+ ///
+ /// @param request a message received from client
+ ///
+ /// @return ACK or NACK message
+ boost::shared_ptr processRequest(boost::shared_ptr& request);
+
+ /// @brief Stub function that will handle incoming RELEASE messages.
+ ///
+ /// In DHCPv4, server does not respond to RELEASE messages, therefore
+ /// this function does not return anything.
+ ///
+ /// @param release message received from client
+ void processRelease(boost::shared_ptr& release);
+
+ /// @brief Stub function that will handle incoming DHCPDECLINE messages.
+ ///
+ /// @param decline message received from client
+ void processDecline(boost::shared_ptr& decline);
+
+ /// @brief Stub function that will handle incoming INFORM messages.
+ ///
+ /// @param infRequest message received from client
+ boost::shared_ptr processInform(boost::shared_ptr& inform);
+
+ /// @brief Returns server-intentifier option
+ ///
+ /// @return server-id option
+ boost::shared_ptr
+ getServerID() { return serverid_; }
+
+ /// @brief Sets server-identifier.
+ ///
+ /// This method attempts to set server-identifier DUID. It tries to
+ /// load previously stored IP from configuration. If there is no previously
+ /// stored server identifier, it will pick up one address from configured
+ /// and supported network interfaces.
+ ///
+ /// @throws isc::Unexpected Failed to obtain server identifier (i.e. no
+ // previously stored configuration and no network interfaces available)
+ void setServerID();
+
+ /// server DUID (to be sent in server-identifier option)
+ boost::shared_ptr serverid_;
+
+ /// indicates if shutdown is in progress. Setting it to true will
+ /// initiate server shutdown procedure.
+ volatile bool shutdown_;
+};
+
+}; // namespace isc::dhcp
+}; // namespace isc
+
+#endif // DHCP4_SRV_H
diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc
new file mode 100644
index 0000000000..ee402955ad
--- /dev/null
+++ b/src/bin/dhcp4/main.cc
@@ -0,0 +1,112 @@
+// 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
+// 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
+#if 0
+// TODO cc is not used yet. It should be eventually
+#include
+#include
+#endif
+
+#include
+#include
+
+#include
+#include
+
+using namespace std;
+using namespace isc::util;
+
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+
+bool verbose_mode = false;
+
+void
+usage() {
+ cerr << "Usage: b10-dhcp4 [-v]"
+ << endl;
+ cerr << "\t-v: verbose output" << endl;
+ exit(1);
+}
+} // end of anonymous namespace
+
+int
+main(int argc, char* argv[]) {
+ int ch;
+
+ while ((ch = getopt(argc, argv, ":v")) != -1) {
+ switch (ch) {
+ case 'v':
+ verbose_mode = true;
+ isc::log::denabled = true;
+ break;
+ case ':':
+ default:
+ usage();
+ }
+ }
+
+ cout << "My pid=" << getpid() << endl;
+
+ if (argc - optind > 0) {
+ usage();
+ }
+
+ int ret = 0;
+
+ // TODO remainder of auth to dhcp4 code copy. We need to enable this in
+ // dhcp4 eventually
+#if 0
+ Session* cc_session = NULL;
+ Session* statistics_session = NULL;
+ ModuleCCSession* config_session = NULL;
+#endif
+ try {
+ string specfile;
+ if (getenv("B10_FROM_BUILD")) {
+ specfile = string(getenv("B10_FROM_BUILD")) +
+ "/src/bin/auth/dhcp4.spec";
+ } else {
+ specfile = string(DHCP4_SPECFILE_LOCATION);
+ }
+
+ cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl;
+
+ Dhcpv4Srv* srv = new Dhcpv4Srv();
+
+ srv->run();
+
+ } catch (const std::exception& ex) {
+ cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
+ ret = 1;
+ }
+
+ return (ret);
+}
diff --git a/src/bin/dhcp4/spec_config.h.pre.in b/src/bin/dhcp4/spec_config.h.pre.in
new file mode 100644
index 0000000000..17623edecc
--- /dev/null
+++ b/src/bin/dhcp4/spec_config.h.pre.in
@@ -0,0 +1,15 @@
+// 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 DHCP4_SPECFILE_LOCATION "@prefix@/share/@PACKAGE@/dhcp4.spec"
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
new file mode 100644
index 0000000000..dcda356ce7
--- /dev/null
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -0,0 +1,46 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+
+# 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/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/dhcp6/tests\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
+CLEANFILES = $(builddir)/interfaces.txt
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+TESTS =
+if HAVE_GTEST
+
+TESTS += dhcp4_unittests
+
+dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc
+dhcp4_unittests_SOURCES += dhcp4_unittests.cc
+dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
+
+dhcp4_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+dhcp4_unittests_LDADD = $(GTEST_LDADD)
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
new file mode 100644
index 0000000000..c1976e19d5
--- /dev/null
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -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.
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+
+class NakedDhcpv4Srv: public Dhcpv4Srv {
+ // "naked" DHCPv4 server, exposes internal fields
+public:
+ NakedDhcpv4Srv() { }
+
+ boost::shared_ptr processDiscover(boost::shared_ptr& discover) {
+ return Dhcpv4Srv::processDiscover(discover);
+ }
+ boost::shared_ptr processRequest(boost::shared_ptr& request) {
+ return Dhcpv4Srv::processRequest(request);
+ }
+ void processRelease(boost::shared_ptr& release) {
+ return Dhcpv4Srv::processRelease(release);
+ }
+ void processDecline(boost::shared_ptr& decline) {
+ Dhcpv4Srv::processDecline(decline);
+ }
+ boost::shared_ptr processInform(boost::shared_ptr& inform) {
+ return Dhcpv4Srv::processInform(inform);
+ }
+};
+
+class Dhcpv4SrvTest : public ::testing::Test {
+public:
+ Dhcpv4SrvTest() {
+ }
+
+ ~Dhcpv4SrvTest() {
+ };
+};
+
+TEST_F(Dhcpv4SrvTest, basic) {
+ // nothing to test. DHCPv4_srv instance is created
+ // in test fixture. It is destroyed in destructor
+
+ Dhcpv4Srv* srv = NULL;
+ ASSERT_NO_THROW({
+ srv = new Dhcpv4Srv();
+ });
+
+ delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processDiscover) {
+ NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+ boost::shared_ptr pkt(new Pkt4(DHCPDISCOVER, 1234));
+
+ // should not throw
+ EXPECT_NO_THROW(
+ srv->processDiscover(pkt);
+ );
+
+ // should return something
+ EXPECT_TRUE(srv->processDiscover(pkt));
+
+ // TODO: Implement more reasonable tests before starting
+ // work on processSomething() method.
+ delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processRequest) {
+ NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+ boost::shared_ptr pkt(new Pkt4(DHCPREQUEST, 1234));
+
+ // should not throw
+ EXPECT_NO_THROW(
+ srv->processRequest(pkt);
+ );
+
+ // should return something
+ EXPECT_TRUE(srv->processRequest(pkt));
+
+ // TODO: Implement more reasonable tests before starting
+ // work on processSomething() method.
+ delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processRelease) {
+ NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+ boost::shared_ptr pkt(new Pkt4(DHCPRELEASE, 1234));
+
+ // should not throw
+ EXPECT_NO_THROW(
+ srv->processRelease(pkt);
+ );
+
+ // TODO: Implement more reasonable tests before starting
+ // work on processSomething() method.
+
+ delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processDecline) {
+ NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+ boost::shared_ptr pkt(new Pkt4(DHCPDECLINE, 1234));
+
+ // should not throw
+ EXPECT_NO_THROW(
+ srv->processDecline(pkt);
+ );
+
+ // TODO: Implement more reasonable tests before starting
+ // work on processSomething() method.
+ delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processInform) {
+ NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+ boost::shared_ptr pkt(new Pkt4(DHCPINFORM, 1234));
+
+ // should not throw
+ EXPECT_NO_THROW(
+ srv->processInform(pkt);
+ );
+
+ // should return something
+ EXPECT_TRUE(srv->processInform(pkt));
+
+ // TODO: Implement more reasonable tests before starting
+ // work on processSomething() method.
+
+ delete srv;
+}
+
+} // end of anonymous namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_unittests.cc b/src/bin/dhcp4/tests/dhcp4_unittests.cc
new file mode 100644
index 0000000000..ebc72fbc92
--- /dev/null
+++ b/src/bin/dhcp4/tests/dhcp4_unittests.cc
@@ -0,0 +1,28 @@
+// 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
+
+int
+main(int argc, char* argv[]) {
+
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+
+ int result = RUN_ALL_TESTS();
+
+ return (result);
+}
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index b0f8cd9311..0e939245b2 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -32,10 +32,9 @@ spec_config.h: spec_config.h.pre
BUILT_SOURCES = spec_config.h
pkglibexec_PROGRAMS = b10-dhcp6
-b10_dhcp6_SOURCES = main.cc iface_mgr.cc dhcp6_srv.cc
-b10_dhcp6_SOURCES += iface_mgr.h dhcp6_srv.h
+b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
-b10_dhcp6_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp.la
+b10_dhcp6_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 0e7e8523b0..05c3529865 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -1,7 +1,7 @@
{
"module_spec": {
"module_name": "dhcp6",
- "module_description": "DHCPv6 daemon",
+ "module_description": "DHCPv6 server daemon",
"config_data": [
{ "item_name": "interface",
"item_type": "string",
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index d5a969f519..6bc7194536 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -14,7 +14,7 @@
#include
#include
-#include
+#include
#include
#include
#include
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index f37194cf07..6a0844fd09 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -43,10 +43,8 @@ if HAVE_GTEST
TESTS += dhcp6_unittests
-dhcp6_unittests_SOURCES = ../iface_mgr.h ../iface_mgr.cc
-dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
+dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc
dhcp6_unittests_SOURCES += dhcp6_unittests.cc
-dhcp6_unittests_SOURCES += iface_mgr_unittest.cc
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -54,7 +52,7 @@ dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
dhcp6_unittests_LDADD = $(GTEST_LDADD)
dhcp6_unittests_LDADD += $(SQLITE_LIBS)
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 50f37af9c8..092dd2e583 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -57,7 +57,7 @@ TEST_F(Dhcpv6SrvTest, basic) {
// interfaces.txt instead. It will pretend to have detected
// fe80::1234 link-local address on eth0 interface. Obviously
// an attempt to bind this socket will fail.
- Dhcpv6Srv* srv = 0;
+ Dhcpv6Srv* srv = NULL;
ASSERT_NO_THROW( {
// open an unpriviledged port
srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
@@ -67,7 +67,7 @@ TEST_F(Dhcpv6SrvTest, basic) {
}
TEST_F(Dhcpv6SrvTest, Solicit_basic) {
- NakedDhcpv6Srv * srv = 0;
+ NakedDhcpv6Srv* srv = NULL;
ASSERT_NO_THROW( srv = new NakedDhcpv6Srv(); );
// a dummy content for client-id
@@ -116,7 +116,7 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
boost::shared_ptr tmp = reply->getOption(D6O_IA_NA);
ASSERT_TRUE( tmp );
- Option6IA * reply_ia = dynamic_cast ( tmp.get() );
+ Option6IA* reply_ia = dynamic_cast ( tmp.get() );
EXPECT_EQ( 234, reply_ia->getIAID() );
// check that there's an address included
diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py
index 5ae1f5eba5..d63e04d7f0 100644
--- a/src/bin/dhcp6/tests/dhcp6_test.py
+++ b/src/bin/dhcp6/tests/dhcp6_test.py
@@ -59,7 +59,13 @@ class TestDhcpv6Daemon(unittest.TestCase):
# kill this process
# XXX: b10-dhcp6 is too dumb to understand 'shutdown' command for now,
# so let's just kill the bastard
- os.kill(pi.pid, signal.SIGTERM)
+
+ # TODO: Ignore errors for now. This test will be more thorough once ticket #1503
+ # (passing port number to b10-dhcp6 daemon) is implemented.
+ try:
+ os.kill(pi.pid, signal.SIGTERM)
+ except OSError:
+ print("Ignoring failed kill attempt. Process is dead already.")
if __name__ == '__main__':
unittest.main()
diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml
index 4f6a7fa9ea..87d0267430 100644
--- a/src/bin/xfrout/b10-xfrout.xml
+++ b/src/bin/xfrout/b10-xfrout.xml
@@ -20,7 +20,7 @@
- December 1, 2010
+ December 15, 2011
@@ -52,7 +52,7 @@
DESCRIPTION
The b10-xfrout daemon provides the BIND 10
- outgoing DNS zone transfer service.
+ outgoing DNS zone transfer service using AXFR or IXFR.
It is also used to send outgoing NOTIFY messages.
Normally it is started by the
bind10 8
@@ -67,13 +67,13 @@
process?, and then the socket and xfr request is sent to xfrout.
-->
+
- This development prototype release only supports AXFR.
- IXFR is not implemented.
+ Currently IXFR only works if it gets the zone via
+ b10-xfrin and only on TCP.
-
This daemon communicates with BIND 10 over a
b10-msgq 8
C-Channel connection. If this connection is not established,
@@ -100,15 +100,15 @@
tsig_key_ring
A list of TSIG keys (each of which is in the form of
- name:base64-key[:algorithm]) used for access control on transfer
- requests.
+ name:base64-key[:algorithm] )
+ used for access control on transfer requests.
The default is an empty list.
transfer_acl
A list of ACL elements that apply to all transfer requests by
- default (unless overridden in zone_config). See the BIND 10
- guide for configuration examples.
+ default (unless overridden in zone_config ).
+ See the BIND 10 Guide for configuration examples.
The default is an element that allows any transfer requests.
@@ -117,9 +117,9 @@
configuration concerning b10-xfrout .
The supported names of each object are "origin" (the origin
name of the zone), "class" (the RR class of the zone, optional,
- default to "IN"), and "acl_element" (ACL only applicable to
+ default to "IN"), and "transfer_acl" (ACL only applicable to
transfer requests for that zone).
- See the BIND 10 guide for configuration examples.
+ See the BIND 10 Guide for configuration examples.
The default is an empty list, that is, no zone specific configuration.
diff --git a/src/bin/zonemgr/b10-zonemgr.xml b/src/bin/zonemgr/b10-zonemgr.xml
index 00f5d040be..5ea6041ff0 100644
--- a/src/bin/zonemgr/b10-zonemgr.xml
+++ b/src/bin/zonemgr/b10-zonemgr.xml
@@ -20,7 +20,7 @@
- May 19, 2011
+ December 8, 2011
@@ -107,15 +107,20 @@
refresh_jitter
+ is used to provide a time range for randomizing the refresh
+ and retry timers to help avoid many zones needing to do a refresh
+ or retry at the same time.
This value is a real number.
- The maximum amount is 0.5.
- The default is 0.25.
+ The maximum amount is 0.5 (the new timer will be within
+ half the original time).
+ The default is 0.25 (up to a quarter sooner).
+ Set to 0 to disable this jitter.
-
-
reload_jitter
+
+
This value is a real number.
The default is 0.75.
@@ -224,14 +229,6 @@
-->
-
SEE ALSO
@@ -248,9 +245,6 @@
b10-xfrin 8
,
-
- b10-xfrout 8
- ,
bind10 8
,
diff --git a/src/bin/zonemgr/tests/Makefile.am b/src/bin/zonemgr/tests/Makefile.am
index 769d332b87..8c6b9049cf 100644
--- a/src/bin/zonemgr/tests/Makefile.am
+++ b/src/bin/zonemgr/tests/Makefile.am
@@ -20,6 +20,7 @@ endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
$(LIBRARY_PATH_PLACEHOLDER) \
+ B10_FROM_BUILD=$(abs_top_builddir) \
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/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 80e41b3194..600453dd17 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -48,28 +48,16 @@ class MySession():
def group_recvmsg(self, nonblock, seq):
return None, None
-class FakeConfig:
+class FakeCCSession(isc.config.ConfigData):
def __init__(self):
- self.zone_list = []
- self.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN,
- ZONE_NAME_CLASS2_CH])
- def set_zone_list_from_name_classes(self, zones):
- self.zone_list = map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
- def get(self, name):
- if name == 'lowerbound_refresh':
- return LOWERBOUND_REFRESH
- elif name == 'lowerbound_retry':
- return LOWERBOUND_RETRY
- elif name == 'max_transfer_timeout':
- return MAX_TRANSFER_TIMEOUT
- elif name == 'refresh_jitter':
- return REFRESH_JITTER
- elif name == 'reload_jitter':
- return RELOAD_JITTER
- elif name == 'secondary_zones':
- return self.zone_list
+ module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
+ ConfigData.__init__(self, module_spec)
+
+ def get_remote_config_value(self, module_name, identifier):
+ if module_name == "Auth" and identifier == "database_file":
+ return "initdb.file", False
else:
- raise ValueError('Uknown config option')
+ return "unknown", False
class MyZonemgrRefresh(ZonemgrRefresh):
def __init__(self):
@@ -92,7 +80,7 @@ class MyZonemgrRefresh(ZonemgrRefresh):
sqlite3_ds.get_zone_soa = get_zone_soa
ZonemgrRefresh.__init__(self, MySession(), "initdb.file",
- self._slave_socket, FakeConfig())
+ self._slave_socket, FakeCCSession())
current_time = time.time()
self._zonemgr_refresh_info = {
('example.net.', 'IN'): {
@@ -112,6 +100,7 @@ class TestZonemgrRefresh(unittest.TestCase):
self.stderr_backup = sys.stderr
sys.stderr = open(os.devnull, 'w')
self.zone_refresh = MyZonemgrRefresh()
+ self.cc_session = FakeCCSession()
def test_random_jitter(self):
max = 100025.120
@@ -458,7 +447,23 @@ class TestZonemgrRefresh(unittest.TestCase):
"secondary_zones": [ { "name": "example.net.",
"class": "IN" } ]
}
- self.zone_refresh.update_config_data(config_data)
+ self.zone_refresh.update_config_data(config_data, self.cc_session)
+ self.assertTrue(("example.net.", "IN") in
+ self.zone_refresh._zonemgr_refresh_info)
+
+ # make sure it does fail if we don't provide a name
+ config_data = {
+ "secondary_zones": [ { "class": "IN" } ]
+ }
+ self.assertRaises(ZonemgrException,
+ self.zone_refresh.update_config_data,
+ config_data, self.cc_session)
+
+ # But not if we don't provide a class
+ config_data = {
+ "secondary_zones": [ { "name": "example.net." } ]
+ }
+ self.zone_refresh.update_config_data(config_data, self.cc_session)
self.assertTrue(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
@@ -471,7 +476,7 @@ class TestZonemgrRefresh(unittest.TestCase):
"reload_jitter" : 0.75,
"secondary_zones": []
}
- self.zone_refresh.update_config_data(config_data)
+ self.zone_refresh.update_config_data(config_data, self.cc_session)
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)
@@ -482,7 +487,7 @@ class TestZonemgrRefresh(unittest.TestCase):
config_data = {
"reload_jitter" : 0.35,
}
- self.zone_refresh.update_config_data(config_data)
+ self.zone_refresh.update_config_data(config_data, self.cc_session)
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)
@@ -500,7 +505,7 @@ class TestZonemgrRefresh(unittest.TestCase):
"secondary_zones": [ { "name": "doesnotexist",
"class": "IN" } ]
}
- self.zone_refresh.update_config_data(config_data)
+ self.zone_refresh.update_config_data(config_data, self.cc_session)
name_class = ("doesnotexist.", "IN")
self.assertTrue(self.zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"]
is None)
@@ -520,7 +525,7 @@ class TestZonemgrRefresh(unittest.TestCase):
"reload_jitter" : 0.75,
"secondary_zones": []
}
- self.zone_refresh.update_config_data(config_data)
+ self.zone_refresh.update_config_data(config_data, self.cc_session)
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)
@@ -536,46 +541,68 @@ class TestZonemgrRefresh(unittest.TestCase):
self.assertFalse(listener.is_alive())
def test_secondary_zones(self):
+ def zone_list_from_name_classes(zones):
+ return map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
+
"""Test that we can modify the list of secondary zones"""
- config = FakeConfig()
- config.zone_list = []
+ config = self.cc_session.get_full_config()
+ config['secondary_zones'] = []
# First, remove everything
- self.zone_refresh.update_config_data(config)
+ self.zone_refresh.update_config_data(config, self.cc_session)
self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {})
# Put something in
- config.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
- self.zone_refresh.update_config_data(config)
+ config['secondary_zones'] = \
+ zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
+ self.zone_refresh.update_config_data(config, self.cc_session)
self.assertTrue(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
- # This one does not exist
- config.set_zone_list_from_name_classes(["example.net", "CH"])
- self.zone_refresh.update_config_data(config)
- self.assertFalse(("example.net.", "CH") in
- self.zone_refresh._zonemgr_refresh_info)
- # Simply skip loading soa for the zone, the other configs should be updated successful
+ # Reset the data, set to use a different class, and make sure
+ # it does not get set to IN
+ config['secondary_zones'] = \
+ zone_list_from_name_classes([ZONE_NAME_CLASS1_CH])
+ self.zone_refresh.update_config_data(config, self.cc_session)
self.assertFalse(("example.net.", "IN") in
- self.zone_refresh._zonemgr_refresh_info)
+ 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")])
- self.zone_refresh.update_config_data(config)
+ config['secondary_zones'] = \
+ zone_list_from_name_classes([("example.net", "IN")])
+ self.zone_refresh.update_config_data(config, self.cc_session)
self.assertTrue(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
+ # and with case-insensitive checking
+ config['secondary_zones'] = \
+ zone_list_from_name_classes([("Example.NeT.", "in")])
+ self.zone_refresh.update_config_data(config, self.cc_session)
+ self.assertTrue(("example.net.", "IN") in
+ self.zone_refresh._zonemgr_refresh_info)
+
+ # Try some bad names
+ config['secondary_zones'] = \
+ zone_list_from_name_classes([("example..net", "IN")])
+ self.assertRaises(ZonemgrException,
+ self.zone_refresh.update_config_data,
+ config, self.cc_session)
+ config['secondary_zones'] = \
+ zone_list_from_name_classes([("", "IN")])
+ self.assertRaises(ZonemgrException,
+ self.zone_refresh.update_config_data,
+ config, self.cc_session)
+ # Try a bad class
+ config['secondary_zones'] = \
+ zone_list_from_name_classes([("example.net", "BADCLASS")])
+ self.assertRaises(ZonemgrException,
+ self.zone_refresh.update_config_data,
+ config, self.cc_session)
+ config['secondary_zones'] = \
+ zone_list_from_name_classes([("example.net", "")])
+ self.assertRaises(ZonemgrException,
+ self.zone_refresh.update_config_data,
+ config, self.cc_session)
+
def tearDown(self):
sys.stderr= self.stderr_backup
-
-class MyCCSession():
- def __init__(self):
- pass
-
- def get_remote_config_value(self, module_name, identifier):
- if module_name == "Auth" and identifier == "database_file":
- return "initdb.file", False
- else:
- return "unknown", False
-
-
class MyZonemgr(Zonemgr):
def __init__(self):
@@ -583,7 +610,7 @@ class MyZonemgr(Zonemgr):
self._zone_refresh = None
self._shutdown_event = threading.Event()
self._cc = MySession()
- self._module_cc = MyCCSession()
+ self._module_cc = FakeCCSession()
self._config_data = {
"lowerbound_refresh" : 10,
"lowerbound_retry" : 5,
@@ -622,7 +649,7 @@ class TestZonemgr(unittest.TestCase):
self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
# 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)
+ FakeCCSession())
config_data1["secondary_zones"] = [{"name": "nonexistent.example",
"class": "IN"}]
self.assertEqual(self.zonemgr.config_handler(config_data1),
@@ -660,4 +687,5 @@ class TestZonemgr(unittest.TestCase):
pass
if __name__== "__main__":
+ isc.log.resetUnitTestRootLogger()
unittest.main()
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 5bdb765ab5..4060bb59d6 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -28,6 +28,7 @@ import os
import time
import signal
import isc
+import isc.dns
import random
import threading
import select
@@ -98,7 +99,7 @@ class ZonemgrRefresh:
can be stopped by calling shutdown() in another thread.
"""
- def __init__(self, cc, db_file, slave_socket, config_data):
+ def __init__(self, cc, db_file, slave_socket, module_cc_session):
self._cc = cc
self._check_sock = slave_socket
self._db_file = db_file
@@ -108,7 +109,8 @@ class ZonemgrRefresh:
self._max_transfer_timeout = None
self._refresh_jitter = None
self._reload_jitter = None
- self.update_config_data(config_data)
+ self.update_config_data(module_cc_session.get_full_config(),
+ module_cc_session)
self._running = False
def _random_jitter(self, max, jitter):
@@ -424,7 +426,7 @@ class ZonemgrRefresh:
self._read_sock = None
self._write_sock = None
- def update_config_data(self, new_config):
+ def update_config_data(self, new_config, module_cc_session):
""" update ZonemgrRefresh config """
# 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
@@ -456,11 +458,42 @@ class ZonemgrRefresh:
if secondary_zones is not None:
# Add new zones
for secondary_zone in new_config.get('secondary_zones'):
+ if 'name' not in secondary_zone:
+ raise ZonemgrException("Secondary zone specified "
+ "without a name")
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'])
+
+ # Convert to Name and back (both to check and to normalize)
+ try:
+ name = isc.dns.Name(name, True).to_text()
+ # Name() can raise a number of different exceptions, just
+ # catch 'em all.
+ except Exception as isce:
+ raise ZonemgrException("Bad zone name '" + name +
+ "': " + str(isce))
+
+ # Currently we use an explicit get_default_value call
+ # in case the class hasn't been set. Alternatively, we
+ # could use
+ # module_cc_session.get_value('secondary_zones[INDEX]/class')
+ # To get either the value that was set, or the default if
+ # it wasn't set.
+ # But the real solution would be to make new_config a type
+ # that contains default values itself
+ # (then this entire method can be simplified a lot, and we
+ # wouldn't need direct access to the ccsession object)
+ if 'class' in secondary_zone:
+ rr_class = secondary_zone['class']
+ else:
+ rr_class = module_cc_session.get_default_value(
+ 'secondary_zones/class')
+ # Convert rr_class to and from RRClass to check its value
+ try:
+ name_class = (name, isc.dns.RRClass(rr_class).to_text())
+ except isc.dns.InvalidRRClass:
+ raise ZonemgrException("Bad RR class '" +
+ rr_class +
+ "' for zone " + name)
required[name_class] = True
# Add it only if it isn't there already
if not name_class in self._zonemgr_refresh_info:
@@ -485,7 +518,7 @@ class Zonemgr:
self._db_file = self.get_db_file()
# Create socket pair for communicating between main thread and zonemgr timer thread
self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
- self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._config_data)
+ self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._module_cc)
self._zone_refresh.run_timer()
self._lock = threading.Lock()
@@ -540,7 +573,7 @@ class Zonemgr:
self._config_data_check(complete)
if self._zone_refresh is not None:
try:
- self._zone_refresh.update_config_data(complete)
+ self._zone_refresh.update_config_data(complete, self._module_cc)
except Exception as e:
answer = create_answer(1, str(e))
ok = False
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index a569ea75fd..9ebd541474 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,3 +1,3 @@
SUBDIRS = exceptions util log cryptolink dns cc config acl xfr bench \
asiolink asiodns nsas cache resolve testutils datasrc \
- server_common python dhcp
+ server_common python dhcp statistics
diff --git a/src/lib/cryptolink/tests/Makefile.am b/src/lib/cryptolink/tests/Makefile.am
index 6ac6fdfd7c..fbe1bad350 100644
--- a/src/lib/cryptolink/tests/Makefile.am
+++ b/src/lib/cryptolink/tests/Makefile.am
@@ -16,7 +16,7 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += crypto_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
+run_unittests_LDFLAGS = $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD) $(BOTAN_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 6e0b02b526..bf7f6ae54d 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -412,7 +412,7 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
// cut but the match we find for the glue is a wildcard match. In that
// case, we return the delegation instead (see RFC 1034, section 4.3.3).
// To save a new search, we record the location of the delegation cut when
- // we encounter it here.
+ // we encounter it here.
isc::dns::ConstRRsetPtr first_ns;
// We want to search from the apex down. We are given the full domain
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index 6dd6b0a072..113d03c202 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -17,6 +17,7 @@ endif
CLEANFILES = *.gcno *.gcda
TESTS =
+noinst_PROGRAMS =
if HAVE_GTEST
TESTS += run_unittests run_unittests_sqlite3 run_unittests_memory
@@ -84,25 +85,7 @@ run_unittests_memory_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_memory_LDADD = $(common_ldadd)
-endif
-
-noinst_PROGRAMS = $(TESTS)
-
-EXTRA_DIST = testdata/brokendb.sqlite3
-EXTRA_DIST += testdata/example.com.signed
-EXTRA_DIST += testdata/example.org
-EXTRA_DIST += testdata/example.org.sqlite3
-EXTRA_DIST += testdata/example2.com
-EXTRA_DIST += testdata/example2.com.sqlite3
-EXTRA_DIST += testdata/mkbrokendb.c
-EXTRA_DIST += testdata/root.zone
-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/test.sqlite3.nodiffs
-EXTRA_DIST += testdata/rwtest.sqlite3
-EXTRA_DIST += testdata/diffs.sqlite3
+noinst_PROGRAMS+= $(TESTS)
# For the factory unit tests, we need to specify that we want
# the loadable backend libraries from the build tree, and not from
@@ -121,3 +104,21 @@ run_unittests_factory_LDADD = $(common_ldadd)
check-local:
B10_FROM_BUILD=${abs_top_builddir} ./run_unittests_factory
endif
+
+endif
+
+EXTRA_DIST = testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/example.com.signed
+EXTRA_DIST += testdata/example.org
+EXTRA_DIST += testdata/example.org.sqlite3
+EXTRA_DIST += testdata/example2.com
+EXTRA_DIST += testdata/example2.com.sqlite3
+EXTRA_DIST += testdata/mkbrokendb.c
+EXTRA_DIST += testdata/root.zone
+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/test.sqlite3.nodiffs
+EXTRA_DIST += testdata/rwtest.sqlite3
+EXTRA_DIST += testdata/diffs.sqlite3
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 3991033527..99ee112553 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -7,21 +7,22 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
CLEANFILES = *.gcno *.gcda
-lib_LTLIBRARIES = libdhcp.la
-libdhcp_la_SOURCES =
-libdhcp_la_SOURCES += libdhcp.cc libdhcp.h
-libdhcp_la_SOURCES += option.cc option.h
-libdhcp_la_SOURCES += option6_ia.cc option6_ia.h
-libdhcp_la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
-libdhcp_la_SOURCES += option6_addrlst.cc option6_addrlst.h
-libdhcp_la_SOURCES += option4_addrlst.cc option4_addrlst.h
-libdhcp_la_SOURCES += dhcp6.h dhcp4.h
-libdhcp_la_SOURCES += pkt6.cc pkt6.h
-libdhcp_la_SOURCES += pkt4.cc pkt4.h
+lib_LTLIBRARIES = libdhcp++.la
+libdhcp___la_SOURCES =
+libdhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
+libdhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
+libdhcp___la_SOURCES += option.cc option.h
+libdhcp___la_SOURCES += option6_ia.cc option6_ia.h
+libdhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
+libdhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
+libdhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
+libdhcp___la_SOURCES += dhcp6.h dhcp4.h
+libdhcp___la_SOURCES += pkt6.cc pkt6.h
+libdhcp___la_SOURCES += pkt4.cc pkt4.h
EXTRA_DIST = README
#EXTRA_DIST += log_messages.mes
-libdhcp_la_CXXFLAGS = $(AM_CXXFLAGS)
-libdhcp_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-libdhcp_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+libdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libdhcp___la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/dhcp/README b/src/lib/dhcp/README
index 6c5353d003..6bd6384b6c 100644
--- a/src/lib/dhcp/README
+++ b/src/lib/dhcp/README
@@ -1,4 +1,4 @@
-This directory holds implementation for libdhcp.
+This directory holds implementation for libdhcp++.
Basic Ideas
@@ -8,4 +8,4 @@ Basic Ideas
Notes
=====
This work just begun. Don't expect to see much useful code here.
-We are working on it.
\ No newline at end of file
+We are working on it.
diff --git a/src/bin/dhcp6/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
similarity index 99%
rename from src/bin/dhcp6/iface_mgr.cc
rename to src/lib/dhcp/iface_mgr.cc
index de2b93c3f9..25999a030d 100644
--- a/src/bin/dhcp6/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -19,7 +19,7 @@
#include
#include
-#include
+#include
#include
using namespace std;
diff --git a/src/bin/dhcp6/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
similarity index 99%
rename from src/bin/dhcp6/iface_mgr.h
rename to src/lib/dhcp/iface_mgr.h
index 0aa25928de..759d65735b 100644
--- a/src/bin/dhcp6/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -184,8 +184,7 @@ public:
/// @return interface with requested index (or NULL if no such
/// interface is present)
///
- Iface*
- getIface(int ifindex);
+ Iface* getIface(int ifindex);
/// @brief Returns interface with specified interface name
///
diff --git a/src/lib/dhcp/libdhcp.cc b/src/lib/dhcp/libdhcp++.cc
similarity index 99%
rename from src/lib/dhcp/libdhcp.cc
rename to src/lib/dhcp/libdhcp++.cc
index f84e495454..78e91c957c 100644
--- a/src/lib/dhcp/libdhcp.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -15,7 +15,7 @@
#include
#include
#include
-#include
+#include
#include "config.h"
#include
#include
diff --git a/src/lib/dhcp/libdhcp.h b/src/lib/dhcp/libdhcp++.h
similarity index 100%
rename from src/lib/dhcp/libdhcp.h
rename to src/lib/dhcp/libdhcp++.h
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 20dd97ab11..96077b7a84 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -22,7 +22,7 @@
#include "util/io_utilities.h"
#include "dhcp/option.h"
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
using namespace std;
using namespace isc::dhcp;
diff --git a/src/lib/dhcp/option6_addrlst.cc b/src/lib/dhcp/option6_addrlst.cc
index 9be381012e..fb082fa082 100644
--- a/src/lib/dhcp/option6_addrlst.cc
+++ b/src/lib/dhcp/option6_addrlst.cc
@@ -19,7 +19,7 @@
#include "asiolink/io_address.h"
#include "util/io_utilities.h"
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
#include "dhcp/option6_addrlst.h"
#include "dhcp/dhcp6.h"
diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc
index 209f5003c2..cd55553716 100644
--- a/src/lib/dhcp/option6_ia.cc
+++ b/src/lib/dhcp/option6_ia.cc
@@ -17,7 +17,7 @@
#include
#include "exceptions/exceptions.h"
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
#include "dhcp/option6_ia.h"
#include "dhcp/dhcp6.h"
#include "util/io_utilities.h"
diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc
index fd3bca49d8..70c2948061 100644
--- a/src/lib/dhcp/option6_iaaddr.cc
+++ b/src/lib/dhcp/option6_iaaddr.cc
@@ -17,7 +17,7 @@
#include
#include "exceptions/exceptions.h"
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
#include "dhcp/option6_iaaddr.h"
#include "dhcp/dhcp6.h"
#include "asiolink/io_address.h"
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index bea93fcfbe..a2e886aa29 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include
-#include
+#include
#include
#include
#include
@@ -59,7 +59,7 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
:local_addr_(DEFAULT_ADDRESS),
remote_addr_(DEFAULT_ADDRESS),
iface_(""),
- ifindex_(-1),
+ ifindex_(0),
local_port_(DHCP4_SERVER_PORT),
remote_port_(DHCP4_CLIENT_PORT),
op_(BOOTREQUEST),
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 189d95d0db..9b6f726886 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -302,17 +302,79 @@ public:
boost::shared_ptr
getOption(uint8_t opt_type);
-
- /// @brief set interface over which packet should be sent
+ /// @brief Returns interface name.
///
- /// @param interface defines outbound interface
- void setIface(const std::string& interface){ iface_ = interface; }
-
- /// @brief gets interface over which packet was received or
- /// will be transmitted
+ /// Returns interface name over which packet was received or is
+ /// going to be transmitted.
///
- /// @return name of the interface
- std::string getIface() const { return iface_; }
+ /// @return interface name
+ std::string getIface() const { return iface_; };
+
+ /// @brief Sets interface name.
+ ///
+ /// Sets interface name over which packet was received or is
+ /// going to be transmitted.
+ ///
+ /// @return interface name
+ void setIface(const std::string& iface ) { iface_ = iface; };
+
+ /// @brief Sets interface index.
+ ///
+ /// @param ifindex specifies interface index.
+ void setIndex(uint32_t ifindex) { ifindex_ = ifindex; };
+
+ /// @brief Returns interface index.
+ ///
+ /// @return interface index
+ uint32_t getIndex() const { return (ifindex_); };
+
+ /// @brief Sets remote address.
+ ///
+ /// @params remote specifies remote address
+ void setRemoteAddr(const isc::asiolink::IOAddress& remote) {
+ remote_addr_ = remote;
+ }
+
+ /// @brief Returns remote address
+ ///
+ /// @return remote address
+ const isc::asiolink::IOAddress& getRemoteAddr() {
+ return (remote_addr_);
+ }
+
+ /// @brief Sets local address.
+ ///
+ /// @params local specifies local address
+ void setLocalAddr(const isc::asiolink::IOAddress& local) {
+ local_addr_ = local;
+ }
+
+ /// @brief Returns local address.
+ ///
+ /// @return local address
+ const isc::asiolink::IOAddress& getLocalAddr() {
+ return (local_addr_);
+ }
+
+ /// @brief Sets local port.
+ ///
+ /// @params local specifies local port
+ void setLocalPort(uint16_t local) { local_port_ = local; }
+
+ /// @brief Returns local port.
+ ///
+ /// @return local port
+ uint16_t getLocalPort() { return (local_port_); }
+
+ /// @brief Sets remote port.
+ ///
+ /// @params remote specifies remote port
+ void setRemotePort(uint16_t remote) { remote_port_ = remote; }
+
+ /// @brief Returns remote port.
+ ///
+ /// @return remote port
+ uint16_t getRemotePort() { return (remote_port_); }
protected:
@@ -338,13 +400,13 @@ protected:
/// Each network interface has assigned unique ifindex. It is functional
/// equvalent of name, but sometimes more useful, e.g. when using crazy
/// systems that allow spaces in interface names e.g. MS Windows)
- int ifindex_;
+ uint32_t ifindex_;
/// local UDP port
- int local_port_;
+ uint16_t local_port_;
/// remote UDP port
- int remote_port_;
+ uint16_t remote_port_;
/// @brief message operation code
///
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index 84c572920b..ff27d5e991 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -15,7 +15,7 @@
#include "dhcp/dhcp6.h"
#include "dhcp/pkt6.h"
-#include "dhcp/libdhcp.h"
+#include "dhcp/libdhcp++.h"
#include "exceptions/exceptions.h"
#include
#include
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 176992f6e3..ef849f15ba 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -2,6 +2,10 @@ SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dhcp/tests\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
@@ -12,31 +16,33 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
-TESTS += libdhcp_unittests
-libdhcp_unittests_SOURCES = run_unittests.cc
-libdhcp_unittests_SOURCES += ../libdhcp.h ../libdhcp.cc libdhcp_unittest.cc
-libdhcp_unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
-libdhcp_unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
-libdhcp_unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
-libdhcp_unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc
-libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
-libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
-libdhcp_unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc
+TESTS += libdhcp++_unittests
+libdhcp___unittests_SOURCES = run_unittests.cc
+libdhcp___unittests_SOURCES += ../libdhcp++.h ../libdhcp++.cc
+libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
+libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc
+libdhcp___unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
+libdhcp___unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
+libdhcp___unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
+libdhcp___unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc
+libdhcp___unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
+libdhcp___unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
+libdhcp___unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc
-libdhcp_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
-libdhcp_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-libdhcp_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
if USE_CLANGPP
# This is to workaround unused variables tcout and tcerr in
# log4cplus's streams.h.
-libdhcp_unittests_CXXFLAGS += -Wno-unused-variable
+libdhcp___unittests_CXXFLAGS += -Wno-unused-variable
endif
-libdhcp_unittests_LDADD = $(GTEST_LDADD)
-libdhcp_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-libdhcp_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-libdhcp_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-libdhcp_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+libdhcp___unittests_LDADD = $(GTEST_LDADD)
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp6/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
similarity index 93%
rename from src/bin/dhcp6/tests/iface_mgr_unittest.cc
rename to src/lib/dhcp/tests/iface_mgr_unittest.cc
index 0c54780fd5..b647c18bf6 100644
--- a/src/bin/dhcp6/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -22,7 +22,7 @@
#include
#include
-#include
+#include
#include
using namespace std;
@@ -31,7 +31,8 @@ using namespace isc::asiolink;
using namespace isc::dhcp;
// name of loopback interface detection
-char LOOPBACK[32] = "lo";
+const size_t buf_size = 32;
+char LOOPBACK[buf_size] = "lo";
namespace {
const char* const INTERFACE_FILE = TEST_DATA_BUILDDIR "/interfaces.txt";
@@ -69,16 +70,16 @@ TEST_F(IfaceMgrTest, loDetect) {
// poor man's interface detection
// it will go away as soon as proper interface detection
// is implemented
- if (if_nametoindex("lo")>0) {
+ if (if_nametoindex("lo") > 0) {
cout << "This is Linux, using lo as loopback." << endl;
- sprintf(LOOPBACK, "lo");
- } else if (if_nametoindex("lo0")>0) {
+ snprintf(LOOPBACK, buf_size - 1, "lo");
+ } else if (if_nametoindex("lo0") > 0) {
cout << "This is BSD, using lo0 as loopback." << endl;
- sprintf(LOOPBACK, "lo0");
+ snprintf(LOOPBACK, buf_size - 1, "lo0");
} else {
cout << "Failed to detect loopback interface. Neither "
- << "lo or lo0 worked. I give up." << endl;
- ASSERT_TRUE(false);
+ << "lo nor lo0 worked. I give up." << endl;
+ FAIL();
}
}
@@ -100,9 +101,9 @@ TEST_F(IfaceMgrTest, dhcp6Sniffer) {
interfaces << "eth0 fe80::21e:8cff:fe9b:7349";
interfaces.close();
- NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
- Pkt6 * pkt = 0;
+ Pkt6* pkt = NULL;
int cnt = 0;
cout << "---8X-----------------------------------------" << endl;
while (true) {
@@ -154,7 +155,7 @@ TEST_F(IfaceMgrTest, basic) {
TEST_F(IfaceMgrTest, ifaceClass) {
// basic tests for Iface inner class
- IfaceMgr::Iface * iface = new IfaceMgr::Iface("eth5", 7);
+ IfaceMgr::Iface* iface = new IfaceMgr::Iface("eth5", 7);
EXPECT_STREQ("eth5/7", iface->getFullName().c_str());
@@ -167,7 +168,7 @@ TEST_F(IfaceMgrTest, ifaceClass) {
TEST_F(IfaceMgrTest, getIface) {
cout << "Interface checks. Please ignore socket binding errors." << endl;
- NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
// interface name, ifindex
IfaceMgr::Iface iface1("lo1", 1);
@@ -191,7 +192,7 @@ TEST_F(IfaceMgrTest, getIface) {
// check that interface can be retrieved by ifindex
- IfaceMgr::Iface * tmp = ifacemgr->getIface(5);
+ IfaceMgr::Iface* tmp = ifacemgr->getIface(5);
// ASSERT_NE(NULL, tmp); is not supported. hmmmm.
ASSERT_TRUE( tmp != NULL );
@@ -224,11 +225,11 @@ TEST_F(IfaceMgrTest, detectIfaces) {
// interfaces. Nevertheless, this fake interface should
// be on list, but if_nametoindex() will fail.
- NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
ASSERT_TRUE( ifacemgr->getIface("eth0") != NULL );
- IfaceMgr::Iface * eth0 = ifacemgr->getIface("eth0");
+ IfaceMgr::Iface* eth0 = ifacemgr->getIface("eth0");
// there should be one address
IfaceMgr::AddressCollection addrs = eth0->getAddresses();
@@ -239,6 +240,7 @@ TEST_F(IfaceMgrTest, detectIfaces) {
EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
delete ifacemgr;
+ unlink(INTERFACE_FILE);
}
TEST_F(IfaceMgrTest, sockets6) {
@@ -247,7 +249,7 @@ TEST_F(IfaceMgrTest, sockets6) {
createLoInterfacesTxt();
- NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
IOAddress loAddr("::1");
@@ -271,6 +273,7 @@ TEST_F(IfaceMgrTest, sockets6) {
close(socket2);
delete ifacemgr;
+ unlink(INTERFACE_FILE);
}
// TODO: disabled due to other naming on various systems
@@ -279,7 +282,7 @@ TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
- NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
IOAddress loAddr("::1");
IOAddress mcastAddr("ff02::1:2");
@@ -357,6 +360,7 @@ TEST_F(IfaceMgrTest, sendReceive6) {
EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
delete ifacemgr;
+ unlink(INTERFACE_FILE);
}
TEST_F(IfaceMgrTest, socket4) {
@@ -384,11 +388,12 @@ TEST_F(IfaceMgrTest, socket4) {
close(socket1);
delete ifacemgr;
+ unlink(INTERFACE_FILE);
}
// Test the Iface structure itself
TEST_F(IfaceMgrTest, iface) {
- IfaceMgr::Iface* iface = 0;
+ IfaceMgr::Iface* iface = NULL;
EXPECT_NO_THROW(
iface = new IfaceMgr::Iface("eth0",1);
);
@@ -445,7 +450,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
// now let's test if IfaceMgr handles socket info properly
createLoInterfacesTxt();
- NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
IfaceMgr::Iface* loopback = ifacemgr->getIface(LOOPBACK);
ASSERT_TRUE(loopback);
loopback->addSocket(sock1);
@@ -513,6 +518,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
);
delete ifacemgr;
+ unlink(INTERFACE_FILE);
}
}
diff --git a/src/lib/dhcp/tests/libdhcp_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
similarity index 99%
rename from src/lib/dhcp/tests/libdhcp_unittest.cc
rename to src/lib/dhcp/tests/libdhcp++_unittest.cc
index 11b618c5cd..ee3b873f0c 100644
--- a/src/lib/dhcp/tests/libdhcp_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -18,7 +18,7 @@
#include
#include
#include
-#include
+#include
#include "config.h"
using namespace std;
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 091bfacc93..fd28c5d331 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -506,7 +506,7 @@ TEST(Pkt4Test, unpackOptions) {
vector expectedFormat = generateTestPacket2();
- for (int i=0; i < sizeof(v4Opts); i++) {
+ for (int i = 0; i < sizeof(v4Opts); i++) {
expectedFormat.push_back(v4Opts[i]);
}
@@ -563,15 +563,19 @@ TEST(Pkt4Test, unpackOptions) {
// This test verifies methods that are used for manipulating meta fields
// i.e. fields that are not part of DHCPv4 (e.g. interface name).
-TEST(Pkt4Ttest, metaFields) {
- Pkt4 pkt(DHCPDISCOVER, 1234);
+TEST(Pkt4Test, metaFields) {
- pkt.setIface("lo0");
+ Pkt4* pkt = new Pkt4(DHCPOFFER, 1234);
+ pkt->setIface("loooopback");
+ pkt->setIndex(42);
+ pkt->setRemoteAddr(IOAddress("1.2.3.4"));
+ pkt->setLocalAddr(IOAddress("4.3.2.1"));
- EXPECT_EQ("lo0", pkt.getIface());
+ EXPECT_EQ("loooopback", pkt->getIface());
+ EXPECT_EQ(42, pkt->getIndex());
+ EXPECT_EQ("1.2.3.4", pkt->getRemoteAddr().toText());
+ EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
- /// TODO: Expand this test once additonal getters/setters are
- /// implemented.
}
} // end of anonymous namespace
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index 1855f7ae9b..930b5935d1 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -633,12 +633,15 @@ private:
case ResponseClassifier::NOTSINGLE:
message_id = RESLIB_NOTSINGLE_RESPONSE;
+ break;
case ResponseClassifier::OPCODE:
message_id = RESLIB_OPCODE_RESPONSE;
+ break;
default:
message_id = RESLIB_ERROR_RESPONSE;
+ break;
}
LOG_DEBUG(logger, RESLIB_DBG_RESULTS, message_id).
arg(questionText(question_));
diff --git a/src/lib/statistics/Makefile.am b/src/lib/statistics/Makefile.am
new file mode 100644
index 0000000000..6c7b91047d
--- /dev/null
+++ b/src/lib/statistics/Makefile.am
@@ -0,0 +1,24 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/statistics -I$(top_builddir)/src/lib/statistics
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_CLANGPP
+# clang++ complains about unused function parameters in some boost header
+# files.
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+lib_LTLIBRARIES = libstatistics.la
+libstatistics_la_SOURCES = counter.h counter.cc
+libstatistics_la_SOURCES += counter_dict.h counter_dict.cc
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/statistics/counter.cc b/src/lib/statistics/counter.cc
new file mode 100644
index 0000000000..9cb1a6f4a7
--- /dev/null
+++ b/src/lib/statistics/counter.cc
@@ -0,0 +1,68 @@
+#include
+
+#include
+
+#include
+
+namespace {
+const unsigned int InitialValue = 0;
+} // namespace
+
+namespace isc {
+namespace statistics {
+
+class CounterImpl : boost::noncopyable {
+ private:
+ std::vector counters_;
+ public:
+ CounterImpl(const size_t nelements);
+ ~CounterImpl();
+ void inc(const Counter::Type&);
+ const Counter::Value& get(const Counter::Type&) const;
+};
+
+CounterImpl::CounterImpl(const size_t items) :
+ counters_(items, InitialValue)
+{
+ if (items == 0) {
+ isc_throw(isc::InvalidParameter, "Items must not be 0");
+ }
+}
+
+CounterImpl::~CounterImpl() {}
+
+void
+CounterImpl::inc(const Counter::Type& type) {
+ if(type >= counters_.size()) {
+ isc_throw(isc::OutOfRange, "Counter type is out of range");
+ }
+ ++counters_.at(type);
+ return;
+}
+
+const Counter::Value&
+CounterImpl::get(const Counter::Type& type) const {
+ if(type >= counters_.size()) {
+ isc_throw(isc::OutOfRange, "Counter type is out of range");
+ }
+ return (counters_.at(type));
+}
+
+Counter::Counter(const size_t items) : impl_(new CounterImpl(items))
+{}
+
+Counter::~Counter() {}
+
+void
+Counter::inc(const Type& type) {
+ impl_->inc(type);
+ return;
+}
+
+const Counter::Value&
+Counter::get(const Type& type) const {
+ return (impl_->get(type));
+}
+
+} // namespace statistics
+} // namespace isc
diff --git a/src/lib/statistics/counter.h b/src/lib/statistics/counter.h
new file mode 100644
index 0000000000..b077616fb6
--- /dev/null
+++ b/src/lib/statistics/counter.h
@@ -0,0 +1,55 @@
+#ifndef __COUNTER_H
+#define __COUNTER_H 1
+
+#include
+#include
+
+#include
+
+namespace isc {
+namespace statistics {
+
+// forward declaration for pImpl idiom
+class CounterImpl;
+
+class Counter : boost::noncopyable {
+private:
+ boost::scoped_ptr impl_;
+public:
+ typedef unsigned int Type;
+ typedef unsigned int Value;
+
+ /// The constructor.
+ ///
+ /// This constructor is mostly exception free. But it may still throw
+ /// a standard exception if memory allocation fails inside the method.
+ ///
+ /// \param items A number of counter items to hold (greater than 0)
+ ///
+ /// \throw isc::InvalidParameter \a items is 0
+ Counter(const size_t items);
+
+ /// The destructor.
+ ///
+ /// This method never throws an exception.
+ ~Counter();
+
+ /// \brief Increment a counter item specified with \a type.
+ ///
+ /// \param type %Counter item to increment
+ ///
+ /// \throw isc::OutOfRange \a type is invalid
+ void inc(const Type& type);
+
+ /// \brief Get the value of a counter item specified with \a type.
+ ///
+ /// \param type %Counter item to get the value of
+ ///
+ /// \throw isc::OutOfRange \a type is invalid
+ const Value& get(const Type& type) const;
+};
+
+} // namespace statistics
+} // namespace isc
+
+#endif
diff --git a/src/lib/statistics/counter_dict.cc b/src/lib/statistics/counter_dict.cc
new file mode 100644
index 0000000000..da6aacebe4
--- /dev/null
+++ b/src/lib/statistics/counter_dict.cc
@@ -0,0 +1,251 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace {
+typedef boost::shared_ptr CounterPtr;
+typedef std::map DictionaryMap;
+}
+
+namespace isc {
+namespace statistics {
+
+// Implementation detail class for CounterDictionary::ConstIterator
+class CounterDictionaryConstIteratorImpl;
+
+class CounterDictionaryImpl : boost::noncopyable {
+private:
+ DictionaryMap dictionary_;
+ std::vector elements_;
+ const size_t items_;
+ // Default constructor is forbidden; number of counter items must be
+ // specified at the construction of this class.
+ CounterDictionaryImpl();
+public:
+ CounterDictionaryImpl(const size_t items);
+ ~CounterDictionaryImpl();
+ void addElement(const std::string& name);
+ void deleteElement(const std::string& name);
+ Counter& getElement(const std::string& name);
+public:
+ CounterDictionaryConstIteratorImpl begin() const;
+ CounterDictionaryConstIteratorImpl end() const;
+};
+
+// Constructor with number of items
+CounterDictionaryImpl::CounterDictionaryImpl(const size_t items) :
+ items_(items)
+{
+ // The number of items must not be 0
+ if (items == 0) {
+ isc_throw(isc::InvalidParameter, "Items must not be 0");
+ }
+}
+
+// Destructor
+CounterDictionaryImpl::~CounterDictionaryImpl() {}
+
+void
+CounterDictionaryImpl::addElement(const std::string& name) {
+ // throw if the element already exists
+ if (dictionary_.count(name) != 0) {
+ isc_throw(isc::InvalidParameter,
+ "Element " << name << " already exists");
+ }
+ assert(items_ != 0);
+ // Create a new Counter and add to the map
+ dictionary_.insert(
+ DictionaryMap::value_type(name, CounterPtr(new Counter(items_))));
+}
+
+void
+CounterDictionaryImpl::deleteElement(const std::string& name) {
+ size_t result = dictionary_.erase(name);
+ if (result != 1) {
+ // If an element with specified name does not exist, throw
+ // isc::OutOfRange.
+ isc_throw(isc::OutOfRange, "Element " << name << " does not exist");
+ }
+}
+
+Counter&
+CounterDictionaryImpl::getElement(const std::string& name) {
+ DictionaryMap::const_iterator i = dictionary_.find(name);
+ if (i != dictionary_.end()) {
+ // the key was found. return the element.
+ return (*(i->second));
+ } else {
+ // If an element with specified name does not exist, throw
+ // isc::OutOfRange.
+ isc_throw(isc::OutOfRange, "Element " << name << " does not exist");
+ }
+}
+
+// Constructor
+// Initialize impl_
+CounterDictionary::CounterDictionary(const size_t items) :
+ impl_(new CounterDictionaryImpl(items))
+{}
+
+// Destructor
+// impl_ will be freed automatically with scoped_ptr
+CounterDictionary::~CounterDictionary() {}
+
+void
+CounterDictionary::addElement(const std::string& name) {
+ impl_->addElement(name);
+}
+
+void
+CounterDictionary::deleteElement(const std::string& name) {
+ impl_->deleteElement(name);
+}
+
+Counter&
+CounterDictionary::getElement(const std::string& name) const {
+ return (impl_->getElement(name));
+}
+
+Counter&
+CounterDictionary::operator[](const std::string& name) const {
+ return (impl_->getElement(name));
+}
+
+// Implementation detail class for CounterDictionary::ConstIterator
+class CounterDictionaryConstIteratorImpl {
+ public:
+ CounterDictionaryConstIteratorImpl();
+ ~CounterDictionaryConstIteratorImpl();
+ CounterDictionaryConstIteratorImpl(
+ const CounterDictionaryConstIteratorImpl &other);
+ CounterDictionaryConstIteratorImpl &operator=(
+ const CounterDictionaryConstIteratorImpl &source);
+ CounterDictionaryConstIteratorImpl(
+ DictionaryMap::const_iterator iterator);
+ public:
+ void increment();
+ const CounterDictionary::ConstIterator::value_type&
+ dereference() const;
+ bool equal(const CounterDictionaryConstIteratorImpl& other) const;
+ private:
+ DictionaryMap::const_iterator iterator_;
+};
+
+CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl() {}
+
+CounterDictionaryConstIteratorImpl::~CounterDictionaryConstIteratorImpl() {}
+
+// Copy constructor: deep copy of iterator_
+CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl(
+ const CounterDictionaryConstIteratorImpl &other) :
+ iterator_(other.iterator_)
+{}
+
+// Assignment operator: deep copy of iterator_
+CounterDictionaryConstIteratorImpl &
+CounterDictionaryConstIteratorImpl::operator=(
+ const CounterDictionaryConstIteratorImpl &source)
+{
+ iterator_ = source.iterator_;
+ return (*this);
+}
+
+// Constructor from implementation detail DictionaryMap::const_iterator
+CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl(
+ DictionaryMap::const_iterator iterator) :
+ iterator_(iterator)
+{}
+
+CounterDictionaryConstIteratorImpl
+CounterDictionaryImpl::begin() const {
+ return (CounterDictionaryConstIteratorImpl(dictionary_.begin()));
+}
+
+CounterDictionaryConstIteratorImpl
+CounterDictionaryImpl::end() const {
+ return (CounterDictionaryConstIteratorImpl(dictionary_.end()));
+}
+
+void
+CounterDictionaryConstIteratorImpl::increment() {
+ ++iterator_;
+ return;
+}
+
+const CounterDictionary::ConstIterator::value_type&
+CounterDictionaryConstIteratorImpl::dereference() const {
+ return (iterator_->first);
+}
+
+bool
+CounterDictionaryConstIteratorImpl::equal(
+ const CounterDictionaryConstIteratorImpl& other) const
+{
+ return (iterator_ == other.iterator_);
+}
+
+CounterDictionary::ConstIterator
+CounterDictionary::begin() const {
+ return (CounterDictionary::ConstIterator(
+ CounterDictionaryConstIteratorImpl(impl_->begin())));
+}
+
+CounterDictionary::ConstIterator
+CounterDictionary::end() const {
+ return (CounterDictionary::ConstIterator(
+ CounterDictionaryConstIteratorImpl(impl_->end())));
+}
+
+CounterDictionary::ConstIterator::ConstIterator() :
+ impl_(new CounterDictionaryConstIteratorImpl())
+{}
+
+CounterDictionary::ConstIterator::~ConstIterator() {}
+
+// Copy constructor: deep copy of impl_
+CounterDictionary::ConstIterator::ConstIterator(
+ const CounterDictionary::ConstIterator& source) :
+ impl_(new CounterDictionaryConstIteratorImpl(*(source.impl_)))
+{}
+
+// Assignment operator: deep copy of impl_
+CounterDictionary::ConstIterator &
+CounterDictionary::ConstIterator::operator=(
+ const CounterDictionary::ConstIterator &source)
+{
+ *impl_ = *source.impl_;
+ return (*this);
+}
+
+// The constructor from implementation detail
+CounterDictionary::ConstIterator::ConstIterator(
+ const CounterDictionaryConstIteratorImpl& source) :
+ impl_(new CounterDictionaryConstIteratorImpl(source))
+{}
+
+const CounterDictionary::ConstIterator::value_type&
+CounterDictionary::ConstIterator::dereference() const
+{
+ return (impl_->dereference());
+}
+
+bool
+CounterDictionary::ConstIterator::equal(
+ CounterDictionary::ConstIterator const& other) const
+{
+ return (impl_->equal(*(other.impl_)));
+}
+
+void
+CounterDictionary::ConstIterator::increment() {
+ impl_->increment();
+ return;
+}
+
+} // namespace statistics
+} // namespace isc
diff --git a/src/lib/statistics/counter_dict.h b/src/lib/statistics/counter_dict.h
new file mode 100644
index 0000000000..4a4cab179b
--- /dev/null
+++ b/src/lib/statistics/counter_dict.h
@@ -0,0 +1,145 @@
+#ifndef __COUNTER_DICT_H
+#define __COUNTER_DICT_H 1
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace isc {
+namespace statistics {
+
+class CounterDictionaryImpl;
+class CounterDictionaryConstIteratorImpl;
+
+class CounterDictionary : boost::noncopyable {
+private:
+ boost::scoped_ptr impl_;
+ // Default constructor is forbidden; number of counter items must be
+ // specified at the construction of this class.
+ CounterDictionary();
+public:
+ /// The constructor.
+ /// This constructor is mostly exception free. But it may still throw
+ /// a standard exception if memory allocation fails inside the method.
+ ///
+ /// \param items A number of counter items to hold (greater than 0)
+ ///
+ /// \throw isc::InvalidParameter \a items is 0
+ CounterDictionary(const size_t items);
+
+ /// The destructor.
+ ///
+ /// This method never throws an exception.
+ ~CounterDictionary();
+
+ /// \brief Add an element
+ ///
+ /// \throw isc::InvalidParameter \a element already exists.
+ ///
+ /// \param name A name of the element to append
+ void addElement(const std::string& name);
+
+ /// \brief Delete
+ ///
+ /// \throw isc::OutOfRange \a element does not exist.
+ ///
+ /// \param name A name of the element to delete
+ void deleteElement(const std::string& name);
+
+ /// \brief Lookup
+ ///
+ /// \throw isc::OutOfRange \a element does not exist.
+ ///
+ /// \param name A name of the element to get the counters
+ Counter& getElement(const std::string &name) const;
+
+ /// Same as getElement()
+ Counter& operator[](const std::string &name) const;
+
+ /// \brief \c ConstIterator is a constant iterator that provides an
+ /// interface for enumerating name of zones stored in CounterDictionary.
+ ///
+ /// This class is derived from boost::iterator_facade and uses pImpl
+ /// idiom not to expose implementation detail of
+ /// CounterDictionary::iterator.
+ ///
+ /// It is intended to walk through the elements when sending the
+ /// counters to statistics module.
+ class ConstIterator :
+ public boost::iterator_facade
+ {
+ private:
+ boost::scoped_ptr impl_;
+ public:
+ /// The constructor.
+ ///
+ /// This constructor is mostly exception free. But it may still
+ /// throw a standard exception if memory allocation fails
+ /// inside the method.
+ ConstIterator();
+ /// The destructor.
+ ///
+ /// This method never throws an exception.
+ ~ConstIterator();
+ /// The assignment operator.
+ ///
+ /// This method is mostly exception free. But it may still
+ /// throw a standard exception if memory allocation fails
+ /// inside the method.
+ ConstIterator& operator=(const ConstIterator &source);
+ /// The copy constructor.
+ ///
+ /// This constructor is mostly exception free. But it may still
+ /// throw a standard exception if memory allocation fails
+ /// inside the method.
+ ConstIterator(const ConstIterator& source);
+ /// The constructor from implementation detail.
+ ///
+ /// This method is used to create an instance of ConstIterator
+ /// by CounterDict::begin() and CounterDict::end().
+ ///
+ /// This constructor is mostly exception free. But it may still
+ /// throw a standard exception if memory allocation fails
+ /// inside the method.
+ ConstIterator(
+ const CounterDictionaryConstIteratorImpl& source);
+ private:
+ /// \brief An internal method to increment this iterator.
+ void increment();
+ /// \brief An internal method to check equality.
+ bool equal(const ConstIterator& other) const;
+ /// \brief An internal method to dereference this iterator.
+ const value_type& dereference() const;
+ private:
+ friend class boost::iterator_core_access;
+ };
+
+ typedef ConstIterator const_iterator;
+
+ /// \brief Return an iterator corresponding to the beginning of the
+ /// elements stored in CounterDictionary.
+ ///
+ /// This method is mostly exception free. But it may still throw a
+ /// standard exception if memory allocation fails inside the method.
+ const_iterator begin() const;
+
+ /// \brief Return an iterator corresponding to the end of the elements
+ /// stored in CounterDictionary.
+ ///
+ /// This method is mostly exception free. But it may still throw a
+ /// standard exception if memory allocation fails inside the method.
+ const_iterator end() const;
+};
+
+} // namespace statistics
+} // namespace isc
+
+#endif
diff --git a/src/lib/statistics/tests/Makefile.am b/src/lib/statistics/tests/Makefile.am
new file mode 100644
index 0000000000..d66acdf857
--- /dev/null
+++ b/src/lib/statistics/tests/Makefile.am
@@ -0,0 +1,47 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += counter_unittest.cc
+run_unittests_SOURCES += counter_dict_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.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
+
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS)
+run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_GXX
+run_unittests_CXXFLAGS += -Wno-unused-parameter
+endif
+if USE_CLANGPP
+# Same for clang++, but we need to turn off -Werror completely.
+run_unittests_CXXFLAGS += -Wno-error
+endif
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/statistics/tests/counter_dict_unittest.cc b/src/lib/statistics/tests/counter_dict_unittest.cc
new file mode 100644
index 0000000000..2578b46632
--- /dev/null
+++ b/src/lib/statistics/tests/counter_dict_unittest.cc
@@ -0,0 +1,174 @@
+// 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
+
+enum CounterItems {
+ ITEM1 = 0,
+ ITEM2 = 1,
+ ITEM3 = 2,
+ NUMBER_OF_ITEMS = 3
+};
+
+using namespace isc::statistics;
+
+TEST(CounterDictionaryCreateTest, invalidCounterSize) {
+ // Creating counter with 0 elements will cause an isc::InvalidParameter
+ // exception
+ EXPECT_THROW(CounterDictionary counters(0), isc::InvalidParameter);
+}
+
+// This fixture is for testing CounterDictionary.
+class CounterDictionaryTest : public ::testing::Test {
+protected:
+ CounterDictionaryTest() : counters(NUMBER_OF_ITEMS) {
+ counters.addElement("test");
+ counters.addElement("sub.test");
+ }
+ ~CounterDictionaryTest() {}
+
+ CounterDictionary counters;
+};
+
+TEST_F(CounterDictionaryTest, initializeCheck) {
+ // Check if the all counters are initialized with 0
+ EXPECT_EQ(counters["test"].get(ITEM1), 0);
+ EXPECT_EQ(counters["test"].get(ITEM2), 0);
+ EXPECT_EQ(counters["test"].get(ITEM3), 0);
+}
+
+TEST_F(CounterDictionaryTest, getElement) {
+ // Another member function to get counters for the element
+ EXPECT_EQ(counters.getElement("test").get(ITEM1), 0);
+ EXPECT_EQ(counters.getElement("test").get(ITEM2), 0);
+ EXPECT_EQ(counters.getElement("test").get(ITEM3), 0);
+}
+
+TEST_F(CounterDictionaryTest, incrementCounterItem) {
+ // Increment counters
+ counters["test"].inc(ITEM1);
+ counters["test"].inc(ITEM2);
+ counters["test"].inc(ITEM2);
+ counters["test"].inc(ITEM3);
+ counters["test"].inc(ITEM3);
+ counters["test"].inc(ITEM3);
+ // Check if the counters have expected values
+ EXPECT_EQ(counters["test"].get(ITEM1), 1);
+ EXPECT_EQ(counters["test"].get(ITEM2), 2);
+ EXPECT_EQ(counters["test"].get(ITEM3), 3);
+ EXPECT_EQ(counters["sub.test"].get(ITEM1), 0);
+ EXPECT_EQ(counters["sub.test"].get(ITEM2), 0);
+ EXPECT_EQ(counters["sub.test"].get(ITEM3), 0);
+}
+
+TEST_F(CounterDictionaryTest, deleteElement) {
+ // Ensure the element is accessible
+ EXPECT_EQ(counters["test"].get(ITEM1), 0);
+ EXPECT_EQ(counters["test"].get(ITEM2), 0);
+ EXPECT_EQ(counters["test"].get(ITEM3), 0);
+ // Delete the element
+ counters.deleteElement("test");
+ // Accessing to the deleted element will cause an isc::OutOfRange exception
+ EXPECT_THROW(counters["test"].get(ITEM1), isc::OutOfRange);
+ // Deleting an element which does not exist will cause an isc::OutOfRange
+ // exception
+ EXPECT_THROW(counters.deleteElement("test"), isc::OutOfRange);
+}
+
+TEST_F(CounterDictionaryTest, invalidCounterItem) {
+ // Incrementing out-of-bound counter will cause an isc::OutOfRange
+ // exception
+ EXPECT_THROW(counters["test"].inc(NUMBER_OF_ITEMS), isc::OutOfRange);
+}
+
+TEST_F(CounterDictionaryTest, uniquenessCheck) {
+ // Adding an element which already exists will cause an isc::OutOfRange
+ // exception
+ EXPECT_THROW(counters.addElement("test"), isc::InvalidParameter);
+}
+
+TEST_F(CounterDictionaryTest, iteratorTest) {
+ // Increment counters
+ counters["test"].inc(ITEM1);
+ counters["sub.test"].inc(ITEM2);
+ counters["sub.test"].inc(ITEM2);
+
+ // boolean values to check all of the elements can be accessed through
+ // the iterator
+ bool element_test_visited = false;
+ bool element_sub_test_visited = false;
+ // Walk through the elements with iterator
+ // Check if the elements "test" and "sub.test" appears only once
+ // and the counters have expected value
+ for (CounterDictionary::ConstIterator i = counters.begin(),
+ e = counters.end();
+ i != e;
+ ++i
+ )
+ {
+ const std::string& zone = *i;
+ if (zone == "test" && element_test_visited == false) {
+ element_test_visited = true;
+ // Check if the counters have expected value
+ EXPECT_EQ(counters[zone].get(ITEM1), 1);
+ EXPECT_EQ(counters[zone].get(ITEM2), 0);
+ } else if (zone == "sub.test" &&
+ element_sub_test_visited == false) {
+ element_sub_test_visited = true;
+ // Check if the counters have expected value
+ EXPECT_EQ(counters[zone].get(ITEM1), 0);
+ EXPECT_EQ(counters[zone].get(ITEM2), 2);
+ } else {
+ // Test fails when reaches here: the element is not expected or
+ // the element appeared twice
+ FAIL() << "Unexpected iterator value";
+ }
+ }
+ // Check if the "test" and "sub.test" is accessible
+ EXPECT_TRUE(element_test_visited);
+ EXPECT_TRUE(element_sub_test_visited);
+}
+
+TEST_F(CounterDictionaryTest, iteratorCopyTest) {
+ // Increment counters
+ counters["test"].inc(ITEM1);
+ counters["sub.test"].inc(ITEM2);
+ counters["sub.test"].inc(ITEM2);
+
+ CounterDictionary::ConstIterator i1 = counters.begin();
+ CounterDictionary::ConstIterator i2(i1);
+ CounterDictionary::ConstIterator i3;
+ i3 = i1;
+
+ EXPECT_TRUE(i1 == i2);
+ EXPECT_TRUE(i1 == i3);
+ EXPECT_TRUE(i2 == i3);
+
+ ++i2;
+ EXPECT_TRUE(i1 != i2);
+ EXPECT_TRUE(i1 == i3);
+ EXPECT_TRUE(i2 != i3);
+
+ ++i3;
+ EXPECT_TRUE(i1 != i2);
+ EXPECT_TRUE(i1 != i3);
+ EXPECT_TRUE(i2 == i3);
+}
diff --git a/src/lib/statistics/tests/counter_unittest.cc b/src/lib/statistics/tests/counter_unittest.cc
new file mode 100644
index 0000000000..e0d29acd9e
--- /dev/null
+++ b/src/lib/statistics/tests/counter_unittest.cc
@@ -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.
+
+#include
+#include
+
+#include
+
+namespace {
+enum CounterItems {
+ ITEM1 = 0,
+ ITEM2 = 1,
+ ITEM3 = 2,
+ NUMBER_OF_ITEMS = 3
+};
+}
+
+using namespace isc::statistics;
+
+TEST(CounterCreateTest, invalidCounterSize) {
+ // Creating counter with 0 elements will cause an isc::InvalidParameter
+ // exception
+ EXPECT_THROW(Counter counter(0), isc::InvalidParameter);
+}
+
+// This fixture is for testing Counter.
+class CounterTest : public ::testing::Test {
+protected:
+ CounterTest() : counter(NUMBER_OF_ITEMS) {}
+ ~CounterTest() {}
+
+ Counter counter;
+};
+
+TEST_F(CounterTest, createCounter) {
+ // Check if the all counters are initialized with 0
+ EXPECT_EQ(counter.get(ITEM1), 0);
+ EXPECT_EQ(counter.get(ITEM2), 0);
+ EXPECT_EQ(counter.get(ITEM3), 0);
+}
+
+TEST_F(CounterTest, incrementCounterItem) {
+ // Increment counters
+ counter.inc(ITEM1);
+ counter.inc(ITEM2);
+ counter.inc(ITEM2);
+ counter.inc(ITEM3);
+ counter.inc(ITEM3);
+ counter.inc(ITEM3);
+ // Check if the counters have expected values
+ EXPECT_EQ(counter.get(ITEM1), 1);
+ EXPECT_EQ(counter.get(ITEM2), 2);
+ EXPECT_EQ(counter.get(ITEM3), 3);
+ // Increment counters once more
+ counter.inc(ITEM1);
+ counter.inc(ITEM2);
+ counter.inc(ITEM2);
+ counter.inc(ITEM3);
+ counter.inc(ITEM3);
+ counter.inc(ITEM3);
+ // Check if the counters have expected values
+ EXPECT_EQ(counter.get(ITEM1), 2);
+ EXPECT_EQ(counter.get(ITEM2), 4);
+ EXPECT_EQ(counter.get(ITEM3), 6);
+}
+
+TEST_F(CounterTest, invalidCounterItem) {
+ // Incrementing out-of-bound counter will cause an isc::OutOfRange
+ // exception
+ EXPECT_THROW(counter.inc(NUMBER_OF_ITEMS), isc::OutOfRange);
+ // Trying to get out-of-bound counter will cause an isc::OutOfRange
+ // exception
+ EXPECT_THROW(counter.get(NUMBER_OF_ITEMS), isc::OutOfRange);
+}
diff --git a/src/lib/statistics/tests/run_unittests.cc b/src/lib/statistics/tests/run_unittests.cc
new file mode 100644
index 0000000000..38a299e8ca
--- /dev/null
+++ b/src/lib/statistics/tests/run_unittests.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2009 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
+
+int
+main(int argc, char* argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv); // Initialize Google test
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
+}
diff --git a/tests/lettuce/README.tutorial b/tests/lettuce/README.tutorial
index 18c94cfd1d..c7d3cd7e7b 100644
--- a/tests/lettuce/README.tutorial
+++ b/tests/lettuce/README.tutorial
@@ -50,7 +50,7 @@ will need to expand these, but we will look at them shortly.
This file defines a feature, just under the feature name we can
provide a description of the feature.
-The one scenario we have no has no steps, so if we run it we should
+The one scenario we have has no steps, so if we run it we should
see something like:
-- output
@@ -84,7 +84,7 @@ So let's add a step that starts bind10.
When I start bind10 with configuration example.org.config
--
-This is not good enough; it will fire of the process, but setting up
+This is not good enough; it will start the process, but setting up
b10-auth may take a few moments, so we need to add a step to wait for
it to be started before we continue.
@@ -121,7 +121,7 @@ regular expression itself), so if the step is defined with "do foo bar", the
scenario can add words for readability "When I do foo bar".
Each captured group will be passed as an argument to the function we define.
-For bind10, i defined a configuration file, a cmdctl port, and a process
+For bind10, I defined a configuration file, a cmdctl port, and a process
name. The first two should be self-evident, and the process name is an
optional name we give it, should we want to address it in the rest of the
tests. This is most useful if we want to start multiple instances. In the
diff --git a/tests/lettuce/configurations/ixfr-out/testset1-config.db b/tests/lettuce/configurations/ixfr-out/testset1-config.db
new file mode 100644
index 0000000000..c5fc165e77
--- /dev/null
+++ b/tests/lettuce/configurations/ixfr-out/testset1-config.db
@@ -0,0 +1 @@
+{"Xfrin": {"zones": [{"use_ixfr": true, "class": "IN", "name": "example.com.", "master_addr": "178.18.82.80"}]}, "version": 2, "Logging": {"loggers": [{"debuglevel": 99, "severity": "DEBUG", "output_options": [{"output": "stderr", "flush": true}], "name": "*"}]}, "Auth": {"database_file": "data/ixfr-out/zones.slite3", "listen_on": [{"port": 47806, "address": "::"}, {"port": 47806, "address": "0.0.0.0"}]}}
diff --git a/tests/lettuce/data/ixfr-out/zones.slite3 b/tests/lettuce/data/ixfr-out/zones.slite3
new file mode 100644
index 0000000000..a2b2dbdb92
Binary files /dev/null and b/tests/lettuce/data/ixfr-out/zones.slite3 differ
diff --git a/tests/lettuce/features/ixfr_out_bind10.feature b/tests/lettuce/features/ixfr_out_bind10.feature
new file mode 100644
index 0000000000..e84ad8cb0d
--- /dev/null
+++ b/tests/lettuce/features/ixfr_out_bind10.feature
@@ -0,0 +1,195 @@
+Feature: IXFR out
+ Tests for IXFR-out, specific for BIND 10 behaviour.
+ These are (part of) the tests as described on
+ http://bind10.isc.org/wiki/IxfrSystemTests
+
+ # A lot of these tests test specific UDP behaviour.
+ #
+ # Where possible, we use the TCP equivalent. Some of the behaviour
+ # tested is UDP-specific though. In either case, a comment above
+ # the test shows how and why it differs from the test specification,
+ # or why it is commented out for now.
+ # When we do implement UDP IXFR, we should probably keep the TCP
+ # tests, and add them to the test specification, so we still have a
+ # 1-to-1 mapping between these tests and the specification document.
+ #
+ # These tests use a zone with just a few records, the first serial
+ # is 2, and it is incremented in steps of 2, up to serial 22.
+ # Each updates either deletes or adds the www.example.com A record.
+ # Version 2 has the record, then the update to version 4 deletes it,
+ # the update to 6 adds it again, and so on, until version 22 (where
+ # the last update has added it again)
+ #
+ # Some of the tests (scenario 1 tests 3 and 4, and scenario 2 tests 1 and
+ # 2 may still not work if we replicate BIND 9's behaviour; it always
+ # responds to UDP IXFR requests with just the SOA, and it does not do
+ # AXFR-style IXFR if the number of changes exceeds the size of the zone)
+ #
+ # So in effect, there is only one test that is currently active (scenario
+ # 1 test 7)
+
+
+ Scenario: Test Set 1
+ Given I have bind10 running with configuration ixfr-out/testset1-config.db
+ Then wait for bind10 xfrout to start
+ The SOA serial for example.com should be 22
+
+ #
+ # Test 1
+ #
+ # We don't support UDP yet, and for TCP we currently return full zone,
+ # so this test is currently skipped
+ #
+ #When I do an IXFR transfer of example.com 123 over udp
+ #The transfer result should have 1 RRs
+ #The full result of the last transfer should be
+ #"""
+ #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ #"""
+
+ #
+ # Test 2
+ #
+ # Original test specification was for UDP, using TCP for now
+ #
+ #When I do an IXFR transfer of example.com 22 over udp
+ When I do an IXFR transfer of example.com 22 over tcp
+ The transfer result should have 1 RRs
+ The full result of the last transfer should be
+ """
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ """
+
+ #
+ # Test 3
+ #
+ # Original test specification was for UDP, using TCP for now
+ #
+ #When I do an IXFR transfer of example.com 20 over udp
+ When I do an IXFR transfer of example.com 20 over tcp
+ The transfer result should have 5 RRs
+ The full result of the last transfer should be
+ """
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ www.example.com. 3600 IN A 192.0.2.1
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ """
+
+ #
+ # Test 4
+ #
+ # Original test specification was for UDP, using TCP for now
+ #
+ #When I do an IXFR transfer of example.com 18 over udp
+ When I do an IXFR transfer of example.com 18 over tcp
+ The transfer result should have 8 RRs
+ The full result of the last transfer should be
+ """
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 18 28800 7200 604800 18000
+ www.example.com. 3600 IN A 192.0.2.1
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ www.example.com. 3600 IN A 192.0.2.1
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ """
+
+ #
+ # Test 5
+ #
+ # This test does not have a TCP equivalent, so it is skipped.
+ #
+ #When I do an IXFR transfer of example.com 2 over udp
+ #The transfer result should have 1 RRs
+ #The full result of the last transfer should be
+ #"""
+ #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ #"""
+
+ #
+ # Test 6
+ #
+ # This test does not have a TCP equivalent, so it is skipped.
+ #
+ #When I do an IXFR transfer of example.com 5 over udp
+ #The transfer result should have 1 RRs
+ #The full result of the last transfer should be
+ #"""
+ #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ #"""
+
+ #
+ # Test 7
+ #
+ When I do an IXFR transfer of example.com 14 over tcp
+ The transfer result should have 14 RRs
+ The full result of the last transfer should be
+ """
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 14 28800 7200 604800 18000
+ www.example.com. 3600 IN A 192.0.2.1
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 16 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 16 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 18 28800 7200 604800 18000
+ www.example.com. 3600 IN A 192.0.2.1
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 18 28800 7200 604800 18000
+ www.example.com. 3600 IN A 192.0.2.1
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ www.example.com. 3600 IN A 192.0.2.1
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ """
+
+ Scenario: Test Set 2
+ Given I have bind10 running with configuration ixfr-out/testset1-config.db
+ Then wait for bind10 xfrout to start
+ The SOA serial for example.com should be 22
+
+ #
+ # Test 1
+ #
+ # Original test specification was for UDP, using TCP for now
+ #
+ #When I do an IXFR transfer of example.com 19 over udp
+ When I do an IXFR transfer of example.com 19 over tcp
+ The transfer result should have 5 RRs
+ The full result of the last transfer should be
+ """
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ example.com. 3600 IN NS ns.example.com.
+ ns.example.com. 3600 IN A 192.0.2.1
+ www.example.com. 3600 IN A 192.0.2.1
+ example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ """
+
+ #
+ # Test 2
+ #
+ # This test has no TCP equivalent
+ #
+ #When I do an IXFR transfer of example.com 6 over udp
+ #The transfer result should have 5 RRs
+ #The full result of the last transfer should be
+ #"""
+ #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ #example.com. 3600 IN NS ns.example.com.
+ #ns.example.com. 3600 IN A 192.0.2.1
+ #www.example.com. 3600 IN A 192.0.2.1
+ #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ #"""
+
+ #
+ # Test 3
+ #
+ # This test has no TCP equivalent
+ #
+ #When I do an IXFR transfer of example.com 2 over udp
+ #The transfer result should have 1 RRs
+ #The full result of the last transfer should be
+ #"""
+ #example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
+ #"""
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index 52483165d7..fdc419b8eb 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -79,6 +79,20 @@ def wait_for_auth(step, process_name):
world.processes.wait_for_stderr_str(process_name, ['AUTH_SERVER_STARTED'],
False)
+@step('wait for bind10 xfrout (?:of (\w+) )?to start')
+def wait_for_xfrout(step, process_name):
+ """Wait for b10-xfrout to run. This is done by blocking until the message
+ XFROUT_NEW_CONFIG_DONE is logged.
+ Parameters:
+ process_name ('of 0 and line[0] != ';':
+ self.records.append(line)
+
+@step('An AXFR transfer of ([\w.]+)(?: from ([^:]+)(?::([0-9]+))?)?')
+def perform_axfr(step, zone_name, address, port):
+ """
+ Perform an AXFR transfer, and store the result as an instance of
+ TransferResult in world.transfer_result.
+
+ Step definition:
+ An AXFR transfer of [from :]
+
+ Address defaults to 127.0.0.1
+ Port defaults to 47806
+ """
+ if address is None:
+ address = "127.0.0.1"
+ if port is None:
+ port = 47806
+ args = [ 'dig', 'AXFR', '@' + str(address), '-p', str(port), zone_name ]
+ world.transfer_result = TransferResult(args)
+
+@step('An IXFR transfer of ([\w.]+) (\d+)(?: from ([^:]+)(?::([0-9]+))?)?(?: over (tcp|udp))?')
+def perform_ixfr(step, zone_name, serial, address, port, protocol):
+ """
+ Perform an IXFR transfer, and store the result as an instance of
+ TransferResult in world.transfer_result.
+
+ Step definition:
+ An IXFR transfer of [from :port] [over ]
+
+ Address defaults to 127.0.0.1
+ Port defaults to 47806
+ If either tcp or udp is specified, only this protocol will be used.
+ """
+ if address is None:
+ address = "127.0.0.1"
+ if port is None:
+ port = 47806
+ args = [ 'dig', 'IXFR=' + str(serial), '@' + str(address), '-p', str(port), zone_name ]
+ if protocol is not None:
+ assert protocol == 'tcp' or protocol == 'udp', "Unknown protocol: " + protocol
+ if protocol == 'tcp':
+ args.append('+tcp')
+ elif protocol == 'udp':
+ args.append('+notcp')
+ world.transfer_result = TransferResult(args)
+
+@step('transfer result should have (\d+) rrs?')
+def check_transfer_result_count(step, number_of_rrs):
+ """
+ Check the number of rrs in the transfer result object created by
+ the AXFR transfer or IXFR transfer step.
+
+ Step definition:
+ transfer result should have rr[s]
+
+ Fails if the number of RRs is not equal to number
+ """
+ assert int(number_of_rrs) == len(world.transfer_result.records),\
+ "Got " + str(len(world.transfer_result.records)) +\
+ " records, expected " + str(number_of_rrs)
+
+@step('full result of the last transfer should be')
+def check_full_transfer_result(step):
+ """
+ Check the complete output from the last transfer call.
+
+ Step definition:
+ full result of the last transfer should be
+
+ Whitespace is normalized in both the multiline value and the
+ output, but the order of the output is not.
+ Fails if there is any difference between the two. Prints
+ full output and expected value upon failure.
+ """
+ records_string = "\n".join(world.transfer_result.records)
+ records_string = re.sub("[ \t]+", " ", records_string)
+ expect = re.sub("[ \t]+", " ", step.multiline)
+ assert records_string.strip() == expect.strip(),\
+ "Got:\n'" + records_string + "'\nExpected:\n'" + expect + "'"
diff --git a/tools/reorder_message_file.py b/tools/reorder_message_file.py
index 31f4941131..2ba4d7c3e2 100644
--- a/tools/reorder_message_file.py
+++ b/tools/reorder_message_file.py
@@ -191,6 +191,6 @@ if __name__ == "__main__":
# Read the files and load the data
if len(sys.argv) != 2:
- print "Usage: python reorder.py message_file"
+ print("Usage: python reorder.py message_file")
else:
process_file(sys.argv[1])