Commit 064529e8 authored by Yoshitaka Aharen's avatar Yoshitaka Aharen
Browse files

Merged trac #347: Implement query counters in auth module (branches/trac347 r3685:r4016)



git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@4026 e5f2f494-b856-4b98-b285-d166d9295462
parents 70833edc 46d7cd53
140. [func] y-aharen
src/bin/auth: Added a feature to count queries and send counter
values to statistics periodically. To support it, added wrapping
class of asio::deadline_timer to use as interval timer.
The counters can be seen using the "Stats show" command from
bindctl. The result would look like:
... "auth.queries.tcp": 1, "auth.queries.udp": 1 ...
Using the "Auth sendstats" command you can make b10-auth send the
counters to b10-stats immediately.
(Trac #347, svn r4026)
139. [build] jreed
Introduced configure option and make targets for generating
Python code coverage report. This adds new make targets:
......
......@@ -371,7 +371,7 @@ if test "${boost_include_path}" ; then
BOOST_INCLUDES="-I${boost_include_path}"
CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
fi
AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp],,
AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp boost/date_time/posix_time/posix_time_types.hpp boost/bind.hpp boost/function.hpp],,
AC_MSG_ERROR([Missing required header files.]))
CPPFLAGS="$CPPFLAGS_SAVES"
AC_SUBST(BOOST_INCLUDES)
......
......@@ -41,6 +41,7 @@ b10_auth_SOURCES += auth_srv.cc auth_srv.h
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += config.cc config.h
b10_auth_SOURCES += common.h
b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
......
......@@ -59,6 +59,11 @@
"command_name": "shutdown",
"command_description": "Shut down authoritative DNS server",
"command_args": []
},
{
"command_name": "sendstats",
"command_description": "Send data to a statistics module at once",
"command_args": []
}
]
}
......
......@@ -55,6 +55,7 @@
#include <auth/config.h>
#include <auth/auth_srv.h>
#include <auth/query.h>
#include <auth/statistics.h>
using namespace std;
......@@ -100,6 +101,9 @@ public:
/// Hot spot cache
isc::datasrc::HotCache cache_;
/// Query counters for statistics
AuthCounters counters_;
private:
std::string db_file_;
......@@ -111,6 +115,9 @@ private:
bool xfrout_connected_;
AbstractXfroutClient& xfrout_client_;
/// Increment query counter
void incCounter(const int protocol);
};
AuthSrvImpl::AuthSrvImpl(const bool use_cache,
......@@ -118,6 +125,7 @@ AuthSrvImpl::AuthSrvImpl(const bool use_cache,
config_session_(NULL), verbose_mode_(false),
xfrin_session_(NULL),
memory_datasrc_class_(RRClass::IN()),
counters_(verbose_mode_),
xfrout_connected_(false),
xfrout_client_(xfrout_client)
{
......@@ -294,6 +302,11 @@ AuthSrv::setConfigSession(ModuleCCSession* config_session) {
impl_->config_session_ = config_session;
}
void
AuthSrv::setStatisticsSession(AbstractSession* statistics_session) {
impl_->counters_.setStatisticsSession(statistics_session);
}
ModuleCCSession*
AuthSrv::getConfigSession() const {
return (impl_->config_session_);
......@@ -429,6 +442,9 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
message->setHeaderFlag(Message::HEADERFLAG_AA);
message->setRcode(Rcode::NOERROR());
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
if (remote_edns) {
EDNSPtr local_edns = EDNSPtr(new EDNS());
local_edns->setDNSSECAwareness(dnssec_ok);
......@@ -476,6 +492,9 @@ bool
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer)
{
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
if (verbose_mode_) {
cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
......@@ -601,6 +620,19 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
return (true);
}
void
AuthSrvImpl::incCounter(const int protocol) {
// Increment query counter.
if (protocol == IPPROTO_UDP) {
counters_.inc(AuthCounters::COUNTER_UDP_QUERY);
} else if (protocol == IPPROTO_TCP) {
counters_.inc(AuthCounters::COUNTER_TCP_QUERY);
} else {
// unknown protocol
isc_throw(Unexpected, "Unknown protocol: " << protocol);
}
}
ConstElementPtr
AuthSrvImpl::setDbFile(ConstElementPtr config) {
ConstElementPtr answer = isc::config::createAnswer();
......@@ -670,3 +702,12 @@ AuthSrv::updateConfig(ConstElementPtr new_config) {
return (isc::config::createAnswer(1, error.what()));
}
}
bool AuthSrv::submitStatistics() const {
return (impl_->counters_.submitStatistics());
}
uint64_t
AuthSrv::getCounter(const AuthCounters::CounterType type) const {
return (impl_->counters_.getCounter(type));
}
......@@ -27,6 +27,7 @@
#include <config/ccsession.h>
#include <asiolink/asiolink.h>
#include <auth/statistics.h>
namespace isc {
namespace datasrc {
......@@ -62,6 +63,7 @@ class AuthSrvImpl;
///
/// The design of this class is still in flux. It's quite likely to change
/// in future versions.
///
class AuthSrv {
///
/// \name Constructors, Assignment Operator and Destructor.
......@@ -96,6 +98,8 @@ public:
/// \param message Pointer to the \c Message object
/// \param buffer Pointer to an \c OutputBuffer for the resposne
/// \param server Pointer to the \c DNSServer
///
/// \throw isc::Unexpected Protocol type of \a message is unexpected
void processMessage(const asiolink::IOMessage& io_message,
isc::dns::MessagePtr message,
isc::dns::OutputBufferPtr buffer,
......@@ -281,6 +285,49 @@ public:
void setMemoryDataSrc(const isc::dns::RRClass& rrclass,
MemoryDataSrcPtr memory_datasrc);
/// \brief Set the communication session with Statistics.
///
/// This function never throws an exception as far as
/// AuthCounters::setStatisticsSession() doesn't throw.
///
/// Note: this interface is tentative. We'll revisit the ASIO and
/// session frameworks, at which point the session will probably
/// be passed on construction of the server.
///
/// \param statistics_session A Session object over which statistics
/// information is exchanged with statistics module.
/// The session must be established before setting in the server
/// object.
/// Ownership isn't transferred: the caller is responsible for keeping
/// this object to be valid while the server object is working and for
/// disconnecting the session and destroying the object when the server
/// is shutdown.
void setStatisticsSession(isc::cc::AbstractSession* statistics_session);
/// \brief Submit statistics counters to statistics module.
///
/// This function can throw an exception from
/// AuthCounters::submitStatistics().
///
/// \return true on success, false on failure (e.g. session timeout,
/// session error).
bool submitStatistics() const;
/// \brief Get the value of counter in the AuthCounters.
///
/// This function calls AuthCounters::getCounter() and
/// returns its return value.
///
/// This function never throws an exception as far as
/// AuthCounters::getCounter() doesn't throw.
///
/// Note: Currently this function is for testing purpose only.
///
/// \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;
private:
AuthSrvImpl* impl_;
asiolink::IOService* io_service_;
......
......@@ -11,6 +11,7 @@ query_bench_SOURCES = query_bench.cc
query_bench_SOURCES += ../query.h ../query.cc
query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../config.h ../config.cc
query_bench_SOURCES += ../statistics.h ../statistics.cc
query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
......
......@@ -25,7 +25,7 @@
#include <cassert>
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <exceptions/exceptions.h>
......@@ -62,6 +62,10 @@ static bool verbose_mode = false;
static const string PROGRAM = "Auth";
static const char* DNSPORT = "5300";
// Note: this value must be greater than 0.
// TODO: make it configurable via command channel.
const uint32_t STATISTICS_SEND_INTERVAL_SEC = 60;
/* need global var for config/command handlers.
* todo: turn this around, and put handlers in the authserver
* class itself? */
......@@ -84,6 +88,12 @@ my_command_handler(const string& command, ConstElementPtr args) {
answer = createAnswer(0, args);
} else if (command == "shutdown") {
io_service.stop();
} else if (command == "sendstats") {
if (verbose_mode) {
cerr << "[b10-auth] command 'sendstats' received" << endl;
}
assert(auth_server != NULL);
auth_server->submitStatistics();
}
return (answer);
......@@ -103,6 +113,12 @@ usage() {
cerr << "\t-v: verbose output" << endl;
exit(1);
}
void
statisticsTimerCallback(AuthSrv* auth_server) {
assert(auth_server != NULL);
auth_server->submitStatistics();
}
} // end of anonymous namespace
int
......@@ -168,7 +184,10 @@ main(int argc, char* argv[]) {
// XXX: we should eventually pass io_service here.
Session* cc_session = NULL;
Session* xfrin_session = NULL;
Session* statistics_session = NULL;
IntervalTimer* itimer = NULL;
bool xfrin_session_established = false; // XXX (see Trac #287)
bool statistics_session_established = false; // XXX (see Trac #287)
ModuleCCSession* config_session = NULL;
string xfrout_socket_path;
if (getenv("B10_FROM_BUILD") != NULL) {
......@@ -230,12 +249,19 @@ main(int argc, char* argv[]) {
xfrin_session_established = true;
cout << "[b10-auth] Xfrin session channel established." << endl;
statistics_session = new Session(io_service.get_io_service());
cout << "[b10-auth] Statistics session channel created." << endl;
statistics_session->establish(NULL);
statistics_session_established = true;
cout << "[b10-auth] Statistics session channel established." << endl;
// XXX: with the current interface to asiolink we have to create
// auth_server before io_service while Session needs io_service.
// In a next step of refactoring we should make asiolink independent
// from auth_server, and create io_service, auth_server, and
// sessions in that order.
auth_server->setXfrinSession(xfrin_session);
auth_server->setStatisticsSession(statistics_session);
// Configure the server. configureAuthServer() is expected to install
// all initial configurations, but as a short term workaround we
......@@ -245,6 +271,14 @@ main(int argc, char* argv[]) {
configureAuthServer(*auth_server, config_session->getFullConfig());
auth_server->updateConfig(ElementPtr());
// create interval timer instance
itimer = new IntervalTimer(io_service);
// set up interval timer
// register function to send statistics with interval
itimer->setupTimer(boost::bind(statisticsTimerCallback, auth_server),
STATISTICS_SEND_INTERVAL_SEC);
cout << "[b10-auth] Interval timer to send statistics set." << endl;
cout << "[b10-auth] Server started." << endl;
io_service.run();
......@@ -254,10 +288,16 @@ main(int argc, char* argv[]) {
ret = 1;
}
if (statistics_session_established) {
statistics_session->disconnect();
}
if (xfrin_session_established) {
xfrin_session->disconnect();
}
delete itimer;
delete statistics_session;
delete xfrin_session;
delete config_session;
delete cc_session;
......
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id$
#include <auth/statistics.h>
#include <cc/data.h>
#include <cc/session.h>
#include <sstream>
#include <iostream>
// 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);
public:
// References verbose_mode flag in AuthSrvImpl
// TODO: Fix this short term workaround for logging
// after we have logging framework
AuthCountersImpl(const bool& verbose_mode);
~AuthCountersImpl();
void inc(const AuthCounters::CounterType type);
bool submitStatistics() const;
void setStatisticsSession(isc::cc::AbstractSession* statistics_session);
// Currently for testing purpose only
uint64_t getCounter(const AuthCounters::CounterType type) const;
private:
std::vector<uint64_t> counters_;
isc::cc::AbstractSession* statistics_session_;
const bool& verbose_mode_;
};
AuthCountersImpl::AuthCountersImpl(const bool& verbose_mode) :
// initialize counter
// size: AuthCounters::COUNTER_TYPES, initial value: 0
counters_(AuthCounters::COUNTER_TYPES, 0),
statistics_session_(NULL),
verbose_mode_(verbose_mode)
{}
AuthCountersImpl::~AuthCountersImpl()
{}
void
AuthCountersImpl::inc(const AuthCounters::CounterType type) {
++counters_.at(type);
}
bool
AuthCountersImpl::submitStatistics() const {
if (statistics_session_ == NULL) {
if (verbose_mode_) {
std::cerr << "[b10-auth] "
<< "session interface for statistics"
<< " is not available" << std::endl;
}
return (false);
}
std::stringstream statistics_string;
statistics_string << "{\"command\": [\"set\","
<< "{ \"stats_data\": "
<< "{ \"auth.queries.udp\": "
<< counters_.at(AuthCounters::COUNTER_UDP_QUERY)
<< ", \"auth.queries.tcp\": "
<< counters_.at(AuthCounters::COUNTER_TCP_QUERY)
<< " }"
<< "}"
<< "]}";
isc::data::ConstElementPtr statistics_element =
isc::data::Element::fromJSON(statistics_string);
try {
// group_{send,recv}msg() can throw an exception when encountering
// an error, and group_recvmsg() will throw an exception on timeout.
// We don't want to kill the main server just due to this, so we
// handle them here.
const int seq =
statistics_session_->group_sendmsg(statistics_element, "Stats");
isc::data::ConstElementPtr env, answer;
if (verbose_mode_) {
std::cerr << "[b10-auth] "
<< "send statistics data" << std::endl;
}
// TODO: parse and check response from statistics module
// currently it just returns empty message
statistics_session_->group_recvmsg(env, answer, false, seq);
} catch (const isc::cc::SessionError& ex) {
if (verbose_mode_) {
std::cerr << "[b10-auth] "
<< "communication error in sending statistics data: "
<< ex.what() << std::endl;
}
return (false);
} catch (const isc::cc::SessionTimeout& ex) {
if (verbose_mode_) {
std::cerr << "[b10-auth] "
<< "timeout happened while sending statistics data: "
<< ex.what() << std::endl;
}
return (false);
}
return (true);
}
void
AuthCountersImpl::setStatisticsSession
(isc::cc::AbstractSession* statistics_session)
{
statistics_session_ = statistics_session;
}
// Currently for testing purpose only
uint64_t
AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const {
return (counters_.at(type));
}
AuthCounters::AuthCounters(const bool& verbose_mode) :
impl_(new AuthCountersImpl(verbose_mode))
{}
AuthCounters::~AuthCounters() {
delete impl_;
}
void
AuthCounters::inc(const AuthCounters::CounterType type) {
impl_->inc(type);
}
bool
AuthCounters::submitStatistics() const {
return (impl_->submitStatistics());
}
void
AuthCounters::setStatisticsSession
(isc::cc::AbstractSession* statistics_session)
{
impl_->setStatisticsSession(statistics_session);
}
uint64_t
AuthCounters::getCounter(const AuthCounters::CounterType type) const {
return (impl_->getCounter(type));
}
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id$
#ifndef __STATISTICS_H
#define __STATISTICS_H 1
#include <cc/session.h>
class AuthCountersImpl;
/// \brief Set of query counters.
///
/// \c AuthCounters is set of query counters class. It holds query counters
/// and provides an interface to increment the counter of specified type
/// (e.g. UDP query, TCP query).
///
/// This class also provides a function to send statistics information to
/// statistics module.
///
/// This class is designed to be a part of \c AuthSrv.
/// Call \c setStatisticsSession() to set a session to communicate with
/// statistics module like Xfrin session.
/// Call \c inc() to increment a counter for specific type of query in
/// the query processing function. use \c enum \c CounterType to specify
/// the type of query.
/// Call \c submitStatistics() to submit statistics information to statistics
/// module with statistics_session, periodically or at a time the command
/// \c sendstats is received.
///
/// We may eventually want to change the structure to hold values that are
/// not counters (such as concurrent TCP connections), or seperate generic
/// part to src/lib to share with the other modules.
///
/// This class uses pimpl idiom and hides detailed implementation.
/// This class is constructed on startup of the server, so
/// construction overhead of this approach should be acceptable.
///
/// \todo Hold counters for each query types (Notify, Axfr, Ixfr, Normal)
/// \todo Consider overhead of \c AuthCounters::inc()
class AuthCounters {
private:
AuthCountersImpl* 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
};
/// The constructor.
///
/// \param verbose_mode reference to verbose_mode_ of AuthSrvImpl
///
/// This constructor is mostly exception free. But it may still throw
/// a standard exception if memory allocation fails inside the method.
///
/// \todo Fix this short term workaround for logging
/// after we have logging framework.
///
AuthCounters(const bool& verbose_mode);
/// The destructor.
///
/// This method never throws an exception.
///
~AuthCounters();
/// \brief Increment the counter specified by the parameter.
///
/// \param type Type of a counter to increment.
///
/// \throw std::out_of_range \a type is unknown.
///
/// usage: counter.inc(CounterType::COUNTER_UDP_QUERY);
///
void inc(const CounterType type);
/// \brief Submit statistics counters to statistics module.
///
/// This method is desinged to be called periodically
/// with \c asio_link::StatisticsSendTimer, or arbitrary
/// by the command 'sendstats'.
///
/// Note: Set the session to communicate with statistics module
/// by \c setStatisticsSession() before calling \c submitStatistics().
///
/// This method is mostly exception free (error conditions are
/// represented via the return value). But it may still throw
/// a standard exception if memory allocation fails inside the method.
///
/// \return true on success, false on error.
///
/// \todo Do not block message handling in auth_srv while submitting
/// statistics data.
///
bool submitStatistics() const;
/// \brief Set the session to communicate with Statistics
/// module.
///
/// This method never throws an exception.
///
/// Note: this interface is tentative. We'll revisit the ASIO and session
/// frameworks, at which point the session will probably be passed on
/// construction of the server.
///
/// Ownership isn't transferred: the caller is responsible for keeping
/// this object to be valid while the server object is working and for
/// disconnecting the session and destroying the object when the server
/// is shutdown.
///
/// \param statistics_session A pointer to the session
///
void setStatisticsSession(isc::cc::AbstractSession* statistics_session);
/// \brief Get a value of a counter in the AuthCounters.
///
/// This function returns a value of the counter specified by \a type.
/// This method never throws an exception.
///
/// Note: Currently this function is for testing purpose only.
///
/// \param type Type of a counter to get the value of