Commit da0bb577 authored by Yoshitaka Aharen's avatar Yoshitaka Aharen
Browse files

add query counters in b10-auth, interval timer class in asio_link and unittests (ticket #347)

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac347@3686 e5f2f494-b856-4b98-b285-d166d9295462
parent 23a2e4f6
TBD. [func] y-aharen
src/bin/auth: Added a feature to count query and send counter
values to b10-stats periodically. To support it, added wrapping
class of asio::deadline_timer to use as interval timer.
Added a command 'sendstats' to send counter values at once.
(Trac #347, svn rxxxx)
122. [func] stephen
src/bin/bind10: Added configuration options to Boss to determine
whether to start the authoritative server, recursive server (or
......
......@@ -58,6 +58,7 @@ pkglibexec_PROGRAMS = b10-auth
b10_auth_SOURCES = auth_srv.cc auth_srv.h
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += common.h
b10_auth_SOURCES += stats.cc stats.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
......
......@@ -666,4 +666,78 @@ IOService::setCallBack(const IOCallBack callback) {
impl_->tcp6_server_->setCallBack(&impl_->callback_);
}
}
class IntervalTimerImpl {
private:
// prohibit copy to avoid the function be called twice
IntervalTimerImpl(const IntervalTimerImpl& source);
IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
public:
IntervalTimerImpl(asio::io_service& io_service);
~IntervalTimerImpl();
bool setupTimer(const IntervalTimer::Callback& cbfunc,
const uint32_t interval);
void callback(const asio::error_code& error);
private:
void updateTimer();
IntervalTimer::Callback cbfunc_;
// interval in seconds
uint32_t interval_;
asio::deadline_timer timer_;
};
IntervalTimerImpl::IntervalTimerImpl(asio::io_service& io_service) :
timer_(io_service)
{}
IntervalTimerImpl::~IntervalTimerImpl() {
timer_.cancel();
}
bool
IntervalTimerImpl::setupTimer(const IntervalTimer::Callback& cbfunc,
const uint32_t interval)
{
// interval value must be positive
assert(interval > 0);
cbfunc_ = cbfunc;
interval_ = interval;
// start timer
updateTimer();
return (true);
}
void
IntervalTimerImpl::updateTimer() {
// update expire time (current time + interval_)
timer_.expires_from_now(boost::posix_time::seconds(interval_));
// restart timer
timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
}
void
IntervalTimerImpl::callback(const asio::error_code& cancelled) {
assert(!cbfunc_.empty());
// skip function call in case the timer was cancelled
if (!cancelled) {
cbfunc_();
// restart timer
updateTimer();
}
}
IntervalTimer::IntervalTimer(asio::io_service& io_service) {
impl_ = new IntervalTimerImpl(io_service);
}
IntervalTimer::~IntervalTimer() {
delete impl_;
}
bool
IntervalTimer::setupTimer(const Callback& cbfunc,
const uint32_t interval)
{
return (impl_->setupTimer(cbfunc, interval));
}
}
......@@ -89,6 +89,7 @@ class AuthSrv;
namespace asio_link {
class IOServiceImpl;
struct IntervalTimerImpl;
/// \brief An exception that is thrown if an error occurs within the IO
/// module. This is mainly intended to be a wrapper exception class for
......@@ -444,6 +445,46 @@ public:
private:
IOServiceImpl* impl_;
};
/// \brief The \c IntervalTimer class is a wrapper for the ASIO \c deadline_timer
/// class.
///
/// This class is implemented to use boost::deadline_timer as interval timer.
/// Copy of this class is prohibited not to call the callback function twice.
class IntervalTimer {
public:
/// \name The type of timer callback function
typedef boost::function<void(void)> Callback;
///
/// \name Constructors and Destructor
///
/// Note: The copy constructor and the assignment operator are
/// intentionally defined as private, making this class non-copyable.
//@{
private:
IntervalTimer(const IntervalTimer& source);
IntervalTimer& operator=(const IntervalTimer& source);
public:
/// \brief The constructor with asio::io_service.
///
/// \param io_service A reference to an instance of asio::io_service
IntervalTimer(asio::io_service& io_service);
/// \brief The destructor.
~IntervalTimer();
//@}
/// \brief Register timer callback function
///
/// \param cbfunc A reference to a function to call back
/// when the timer is expired
/// \param interval Interval in seconds
///
/// \return \c true on success
bool setupTimer(const Callback& cbfunc, const uint32_t interval);
private:
IntervalTimerImpl* impl_;
};
} // asio_link
#endif // __ASIO_LINK_H
......
......@@ -14,6 +14,11 @@
"command_name": "shutdown",
"command_description": "Shut down authoritative DNS server",
"command_args": []
},
{
"command_name": "sendstats",
"command_description": "Send statistics data to b10-stats at once",
"command_args": []
}
]
}
......
......@@ -128,10 +128,13 @@ AuthSrvImpl::~AuthSrvImpl() {
AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client) :
impl_(new AuthSrvImpl(use_cache, xfrout_client))
{}
{
counter = new statistics::Counter(impl_->verbose_mode_);
}
AuthSrv::~AuthSrv() {
delete impl_;
delete counter;
}
namespace {
......@@ -214,6 +217,11 @@ AuthSrv::setConfigSession(ModuleCCSession* config_session) {
impl_->config_session_ = config_session;
}
void
AuthSrv::setStatsSession(AbstractSession* stats_session) {
counter->setStatsSession(stats_session);
}
ModuleCCSession*
AuthSrv::getConfigSession() const {
return (impl_->config_session_);
......@@ -266,6 +274,19 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
cerr << "[b10-auth] received a message:\n" << message.toText() << endl;
}
// increment Query Counter
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
counter->inc(statistics::Counter::COUNTER_UDP);
} else if (io_message.getSocket().getProtocol() == IPPROTO_TCP) {
counter->inc(statistics::Counter::COUNTER_TCP);
} else {
// unknown protocol
if (impl_->verbose_mode_) {
cerr << "[b10-auth] Unknown protocol: " <<
io_message.getSocket().getProtocol() << endl;
}
}
// Perform further protocol-level validation.
if (message.getOpcode() == Opcode::NOTIFY()) {
......@@ -543,3 +564,10 @@ AuthSrv::updateConfig(ConstElementPtr new_config) {
return (isc::config::createAnswer(1, error.what()));
}
}
asio_link::IntervalTimer::Callback
AuthSrv::getStatsCallback() {
// just invoke statistics::Counter::getStatsCallback()
// and return its return value
return (counter->getCallback());
}
......@@ -22,6 +22,12 @@
#include <cc/data.h>
#include <config/ccsession.h>
/// for the typedef of asio_link::IntervalTimer::Callback
#include <auth/asio_link.h>
// for class statistics::Counter
#include <auth/stats.h>
namespace isc {
namespace dns {
class InputBuffer;
......@@ -199,8 +205,28 @@ public:
/// is shutdown.
///
void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
/// \brief Set the communication session with Statistics.
///
void setStatsSession(isc::cc::AbstractSession* stats_session);
/// \brief Return the function that sends statistics information
/// to Statistics module.
///
/// This function returns the return value of
/// statistics::Counter::getCallback().
///
/// \return \c boost::function which contains the procedure
/// to send statistics.
asio_link::IntervalTimer::Callback getStatsCallback();
private:
AuthSrvImpl* impl_;
// TODO: consider where to put the counter.
// Currently, count-up is in AuthSrv::processMessage.
// In the future, count-up will be in AuthSrvImpl::process*Query
// and this declaration will be moved into AuthSrvImpl.
statistics::Counter *counter;
};
#endif // __AUTH_SRV_H
......
......@@ -9,6 +9,7 @@ CLEANFILES = *.gcno *.gcda
noinst_PROGRAMS = query_bench
query_bench_SOURCES = query_bench.cc
query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../stats.h ../stats.cc
query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
......
......@@ -59,6 +59,10 @@ bool verbose_mode = false;
const string PROGRAM = "Auth";
const char* DNSPORT = "5300";
// Note: this value must be greater than 0.
// TODO: make it configurable via command channel.
const uint32_t STATS_SEND_INTERVAL_SEC = 60;
/* need global var for config/command handlers.
* todo: turn this around, and put handlers in the authserver
* class itself? */
......@@ -81,6 +85,13 @@ 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;
}
if (auth_server != NULL) {
auth_server->getStatsCallback()();
}
}
return (answer);
......@@ -154,7 +165,10 @@ main(int argc, char* argv[]) {
// XXX: we should eventually pass io_service here.
Session* cc_session = NULL;
Session* xfrin_session = NULL;
Session* stats_session = NULL;
asio_link::IntervalTimer* itimer = NULL;
bool xfrin_session_established = false; // XXX (see Trac #287)
bool stats_session_established = false; // XXX (see Trac #287)
ModuleCCSession* config_session = NULL;
string xfrout_socket_path;
if (getenv("B10_FROM_BUILD") != NULL) {
......@@ -209,15 +223,30 @@ main(int argc, char* argv[]) {
xfrin_session_established = true;
cout << "[b10-auth] Xfrin session channel established." << endl;
stats_session = new Session(io_service->get_io_service());
cout << "[b10-auth] Stats session channel created." << endl;
stats_session->establish(NULL);
stats_session_established = true;
cout << "[b10-auth] Stats session channel established." << endl;
// XXX: with the current interface to asio_link we have to create
// auth_server before io_service while Session needs io_service.
// In a next step of refactoring we should make asio_link independent
// from auth_server, and create io_service, auth_server, and
// sessions in that order.
auth_server->setXfrinSession(xfrin_session);
auth_server->setStatsSession(stats_session);
auth_server->setConfigSession(config_session);
auth_server->updateConfig(ElementPtr());
// create interval timer instance
itimer = new asio_link::IntervalTimer(io_service->get_io_service());
// set up interval timer
// register function to send statistics with interval
itimer->setupTimer(auth_server->getStatsCallback(),
STATS_SEND_INTERVAL_SEC);
cout << "[b10-auth] Interval timer set to send stats." << endl;
cout << "[b10-auth] Server started." << endl;
io_service->run();
} catch (const std::exception& ex) {
......@@ -225,10 +254,16 @@ main(int argc, char* argv[]) {
ret = 1;
}
if (stats_session_established) {
stats_session->disconnect();
}
if (xfrin_session_established) {
xfrin_session->disconnect();
}
delete itimer;
delete stats_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 <boost/bind.hpp>
#include <auth/stats.h>
#include <cc/data.h>
#include <sstream>
#include <iostream>
namespace statistics {
class CounterImpl {
private:
// prohibit copy
CounterImpl(const CounterImpl& source);
CounterImpl& operator=(const CounterImpl& source);
public:
CounterImpl(const bool& verbose_mode);
~CounterImpl();
void inc(const Counter::CounterType type);
asio_link::IntervalTimer::Callback getCallback();
void sendStats();
void setStatsSession(isc::cc::AbstractSession* stats_session);
private:
std::vector<uint64_t>* counters_;
isc::cc::AbstractSession* stats_session_;
const bool& verbose_mode_;
};
CounterImpl::CounterImpl(const bool& verbose_mode) :
// initialize counter
// size: Counter::COUNTER_TYPES, initial value: 0
stats_session_(NULL),
verbose_mode_(verbose_mode)
{
counters_ = new std::vector<uint64_t>(Counter::COUNTER_TYPES, 0);
}
CounterImpl::~CounterImpl() {
delete counters_;
}
void
CounterImpl::inc(const Counter::CounterType type)
{
++counters_->at(type);
}
asio_link::IntervalTimer::Callback
CounterImpl::getCallback()
{
return (boost::bind(&CounterImpl::sendStats, this));
}
void
CounterImpl::sendStats()
{
if (stats_session_ == NULL) {
if (verbose_mode_) {
std::cerr << "[b10-auth] "
<< "session interface for statistics"
<< " is not available" << std::endl;
}
return;
}
std::stringstream strstats;
strstats << "{\"command\": [\"set\","
<< "{ \"stats_data\": "
<< "{ \"auth.queries.udp\": "
<< counters_->at(Counter::COUNTER_UDP)
<< ", \"auth.queries.tcp\": "
<< counters_->at(Counter::COUNTER_TCP)
<< " }"
<< "}"
<< "]}";
isc::data::ConstElementPtr set_stats =
isc::data::Element::fromJSON(strstats);
try {
// group_recvmsg() will time out in MSGQ_DEFAULT_TIMEOUT.
// On timeout or error, an exception will be thrown
const int seq =
stats_session_->group_sendmsg(set_stats, "Stats");
isc::data::ConstElementPtr env, answer;
if (verbose_mode_) {
std::cerr << "[b10-auth] "
<< "send statistics data to b10-stats"
<< std::endl;
}
// TODO: parse and check response from b10-stats
// currently it just returns empty message
stats_session_->group_recvmsg(env, answer, false, seq);
} catch (const isc::Exception& ex) {
// catch the exception and do nothing.
if (verbose_mode_) {
std::cerr << "[b10-auth] "
<< "failed to send statistics data to b10-stats: "
<< ex.what() << std::endl;
}
}
return;
}
void
CounterImpl::setStatsSession(isc::cc::AbstractSession* stats_session) {
stats_session_ = stats_session;
}
Counter::Counter(const bool& verbose_mode) :
impl_(new CounterImpl(verbose_mode))
{}
Counter::~Counter() {
delete impl_;
}
asio_link::IntervalTimer::Callback
Counter::getCallback()
{
return (impl_->getCallback());
}
void
Counter::inc(const Counter::CounterType type) {
impl_->inc(type);
}
void
Counter::setStatsSession(isc::cc::AbstractSession* stats_session) {
impl_->setStatsSession(stats_session);
}
} // statistics
// 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 __STATS_H
#define __STATS_H 1
#include <boost/function.hpp>
#include <cc/session.h>
/// for asio_link::IntervalTimer::Callback
#include <auth/asio_link.h>
namespace statistics {
class CounterImpl;
/// \brief A query counter.
///
/// \c Counter is a query counter class. It holds query counter
/// and provide an interface to increment the counter.
/// This class also provides a function to send information to
/// statistics (b10-stats).
///
/// 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.
class Counter {
private:
CounterImpl* impl_;
public:
// Enum for the type of counter
// COUNTER_UDP: counter for UDP queries
// COUNTER_TCP: counter for TCP queries
enum CounterType {
COUNTER_UDP = 0,
COUNTER_TCP = 1,
COUNTER_TYPES = 2
};
/// The constructor.
///
/// verbose_mode references verbose_mode_ of AuthSrvImpl
Counter(const bool& verbose_mode);
/// The destructor.
~Counter();
/// \brief Increment the counter specified by the parameter
///
/// \param type Type of a counter to increment
/// usage: counter->inc(CounterType::COUNTER_DUP);
///
void inc(const CounterType type);
/// \brief Get the function to send statistics information
/// to Statistics module
///
/// The returned function is inteneded to be called
/// perioically.
///
/// \return \c asio_link::IntervalTimer::Callback points
/// to a function to send statistics
asio_link::IntervalTimer::Callback getCallback();
/// \brief Set the session to communicate with Statistics
/// module
///
/// \param stats_session A pointer to the session
void setStatsSession(isc::cc::AbstractSession* stats_session);
};
} // statistics
#endif // __STATS_H
......@@ -22,6 +22,7 @@ run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
run_unittests_SOURCES += ../change_user.h ../change_user.cc
run_unittests_SOURCES += ../stats.h ../stats.cc
run_unittests_SOURCES += auth_srv_unittest.cc
run_unittests_SOURCES += change_user_unittest.cc
run_unittests_SOURCES += asio_link_unittest.cc
......
......@@ -252,6 +252,15 @@ protected:
callback_data_.size(),
expected_data, expected_datasize);
}
void doTimerTest(asio_link::IntervalTimer* itimer) {
// interval timer test
// set test_obj_->timerCallBack() as callback function
// and check that the function was called
timer_called_ = false;
EXPECT_TRUE(itimer->setupTimer(TimerCallBack(this), 1));
io_service_->run();
EXPECT_TRUE(timer_called_);
}
private:
class ASIOCallBack : public std::unary_function<IOMessage, void> {
public:
......@@ -273,12 +282,27 @@ private:
io_message.getDataSize());
io_service_->stop();
}
private:
class TimerCallBack : public std::unary_function<void, void> {
public:
TimerCallBack(ASIOLinkTest* test_obj) : test_obj_(test_obj) {}
void operator()(void) const {
test_obj_->timerCallBack();