Commit 684524bc authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3181'

parents 82c80862 840afe67
......@@ -25,6 +25,7 @@ perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
perfdhcp_SOURCES += packet_storage.h
perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
perfdhcp_SOURCES += rate_control.cc rate_control.h
perfdhcp_SOURCES += stats_mgr.h
perfdhcp_SOURCES += test_control.cc test_control.h
libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
......
......@@ -114,6 +114,7 @@ CommandOptions::reset() {
lease_type_.set(LeaseType::ADDRESS);
rate_ = 0;
renew_rate_ = 0;
release_rate_ = 0;
report_delay_ = 0;
clients_num_ = 0;
mac_template_.assign(mac, mac + 6);
......@@ -211,7 +212,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
// In this section we collect argument values from command line
// they will be tuned and validated elsewhere
while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
"s:iBc1T:X:O:E:S:I:x:w:e:f:")) != -1) {
"s:iBc1T:X:O:E:S:I:x:w:e:f:F:")) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
stream << " " << optarg;
......@@ -307,6 +308,12 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
" must be a positive integer");
break;
case 'F':
release_rate_ = positiveInteger("value of the release rate:"
" -F<release-rate> must be a"
" positive integer");
break;
case 'h':
usage();
return (true);
......@@ -690,6 +697,8 @@ CommandOptions::validate() const {
"-6 (IPv6) must be set to use -c");
check((getIpVersion() != 6) && (getRenewRate() !=0),
"-f<renew-rate> may be used with -6 (IPv6) only");
check((getIpVersion() != 6) && (getReleaseRate() != 0),
"-F<release-rate> may be used with -6 (IPv6) only");
check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
"second -n<num-request> is not compatible with -i");
check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
......@@ -719,6 +728,8 @@ CommandOptions::validate() const {
"-I<ip-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
"-f<renew-rate> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getReleaseRate() != 0),
"-F<release-rate> is not compatible with -i");
check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
"-i must be set to use -c");
check((getRate() == 0) && (getReportDelay() != 0),
......@@ -730,12 +741,16 @@ CommandOptions::validate() const {
check((getRate() == 0) &&
((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
"-r<rate> must be set to use -D<max-drop>");
check((getRate() != 0) && (getRenewRate() > getRate()),
"Renew rate specified as -f<renew-rate> must not be greater than"
" the rate specified as -r<rate>");
check((getRate() != 0) && (getRenewRate() + getReleaseRate() > getRate()),
"The sum of Renew rate (-f<renew-rate>) and Release rate"
" (-F<release-rate>) must not be greater than the exchange"
" rate specified as -r<rate>");
check((getRate() == 0) && (getRenewRate() != 0),
"Renew rate specified as -f<renew-rate> must not be specified"
" when -r<rate> parameter is not specified");
check((getRate() == 0) && (getReleaseRate() != 0),
"Release rate specified as -F<release-rate> must not be specified"
" when -r<rate> parameter is not specified");
check((getTemplateFiles().size() < getTransactionIdOffset().size()),
"-T<template-file> must be set to use -X<xid-offset>");
check((getTemplateFiles().size() < getRandomOffset().size()),
......@@ -816,6 +831,9 @@ CommandOptions::printCommandLine() const {
if (getRenewRate() != 0) {
std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
}
if (getReleaseRate() != 0) {
std::cout << "release-rate[1/s]=" << getReleaseRate() << std::endl;
}
if (report_delay_ != 0) {
std::cout << "report[s]=" << report_delay_ << std::endl;
}
......@@ -899,13 +917,14 @@ void
CommandOptions::usage() const {
std::cout <<
"perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-f<renew-rate>]\n"
" [-t<report>] [-R<range>] [-b<base>] [-n<num-request>]\n"
" [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
" [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
" [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
" [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
" [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
" [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
" [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n"
" [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
" [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
" [-a<aggressivity>] [-L<local-port>] [-s<seed>] [-i] [-B]\n"
" [-c] [-1] [-T<template-file>] [-X<xid-offset>]\n"
" [-O<random-offset] [-E<time-offset>] [-S<srvid-offset>]\n"
" [-I<ip-offset>] [-x<diagnostic-selector>] [-w<wrapped>]\n"
" [server]\n"
"\n"
"The [server] argument is the name/address of the DHCP server to\n"
"contact. For DHCPv4 operation, exchanges are initiated by\n"
......@@ -948,10 +967,6 @@ CommandOptions::usage() const {
"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
" elapsed-time option in the (second/request) template.\n"
" The value 0 disables it.\n"
"-f<renew-rate>: A rate at which IPv6 Renew requests are sent to\n"
" a server. This value must not be equal or lower than the rate\n"
" specified as -r<rate>. If -r<rate> is not specified, this\n"
" parameter must not be specified too.\n"
"-h: Print this help.\n"
"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
" whether -6 is given.\n"
......@@ -999,6 +1014,16 @@ CommandOptions::usage() const {
"\n"
"DHCPv6 only options:\n"
"-c: Add a rapid commit option (exchanges will be SA).\n"
"-f<renew-rate>: Rate at which IPv6 Renew requests are sent to\n"
" a server. This value is only valid when used in conjunction with\n"
" the exchange rate (given by -r<rate>). Furthermore the sum of\n"
" this value and the release-rate (given by -F<rate) must be equal\n"
" to or less than the exchange rate.\n"
"-F<release-rate>: Rate at which IPv6 Release requests are sent to\n"
" a server. This value is only valid when used in conjunction with\n"
" the exchange rate (given by -r<rate>). Furthermore the sum of\n"
" this value and the renew-rate (given by -f<rate) must be equal\n"
" to or less than the exchange rate.\n"
"\n"
"The remaining options are used only in conjunction with -r:\n"
"\n"
......
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
......@@ -156,11 +155,16 @@ public:
/// \return exchange rate per second.
int getRate() const { return rate_; }
/// \brief Returns a rate at which IPv6 Renew messages are sent.
/// \brief Returns a rate at which DHCPv6 Renew messages are sent.
///
/// \return A rate at which IPv6 Renew messages are sent.
int getRenewRate() const { return (renew_rate_); }
/// \brief Returns a rate at which DHCPv6 Release messages are sent.
///
/// \return A rate at which DHCPv6 Release messages are sent.
int getReleaseRate() const { return (release_rate_); }
/// \brief Returns delay between two performance reports.
///
/// \return delay between two consecutive performance reports.
......@@ -469,6 +473,8 @@ private:
int rate_;
/// A rate at which DHCPv6 Renew messages are sent.
int renew_rate_;
/// A rate at which DHCPv6 Release messages are sent.
int release_rate_;
/// Delay between generation of two consecutive
/// performance reports
int report_delay_;
......
// Copyright (C) 2013 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 <exceptions/exceptions.h>
#include <tests/tools/perfdhcp/rate_control.h>
namespace isc {
namespace perfdhcp {
using namespace boost::posix_time;
RateControl::RateControl()
: send_due_(currentTime()), last_sent_(currentTime()),
aggressivity_(1), rate_(0), late_sent_(false) {
}
RateControl::RateControl(const int rate, const int aggressivity)
: send_due_(currentTime()), last_sent_(currentTime()),
aggressivity_(aggressivity), rate_(rate), late_sent_(false) {
if (aggressivity_ < 1) {
isc_throw(isc::BadValue, "invalid value of aggressivity "
<< aggressivity << ", expected value is greater than 0");
}
if (rate_ < 0) {
isc_throw(isc::BadValue, "invalid value of rate " << rate
<< ", expected non-negative value");
}
}
uint64_t
RateControl::getOutboundMessageCount() {
// We need calculate the due time for sending next set of messages.
updateSendDue();
// Get current time. If we are behind due time, we have to calculate
// how many messages to send to catch up with the rate.
ptime now = currentTime();
if (now >= send_due_) {
// Reset number of exchanges.
uint64_t due_exchanges = 0;
// If rate is specified from the command line we have to
// synchornize with it.
if (getRate() != 0) {
time_period period(send_due_, now);
time_duration duration = period.length();
// due_factor indicates the number of seconds that
// sending next chunk of packets will take.
double due_factor = duration.fractional_seconds() /
time_duration::ticks_per_second();
due_factor += duration.total_seconds();
// Multiplying due_factor by expected rate gives the number
// of exchanges to be initiated.
due_exchanges = static_cast<uint64_t>(due_factor * getRate());
// We want to make sure that at least one packet goes out.
if (due_exchanges == 0) {
due_exchanges = 1;
}
// We should not exceed aggressivity as it could have been
// restricted from command line.
if (due_exchanges > getAggressivity()) {
due_exchanges = getAggressivity();
}
} else {
// Rate is not specified so we rely on aggressivity
// which is the number of packets to be sent in
// one chunk.
due_exchanges = getAggressivity();
}
return (due_exchanges);
}
return (0);
}
boost::posix_time::ptime
RateControl::currentTime() {
return (microsec_clock::universal_time());
}
void
RateControl::updateSendDue() {
// There is no sense to update due time if the current due time is in the
// future. The due time is calculated as a duration between the moment
// when the last message of the given type was sent and the time when
// next one is supposed to be sent based on a given rate. The former value
// will not change until we send the next message, which we don't do
// until we reach the due time.
if (send_due_ > currentTime()) {
return;
}
// This is initialized in the class constructor, so if it is not initialized
// it is a programmatic error.
if (last_sent_.is_not_a_date_time()) {
isc_throw(isc::Unexpected, "timestamp of the last sent packet not"
" initialized");
}
// If rate was not specified we will wait just one clock tick to
// send next packet. This simulates best effort conditions.
long duration = 1;
if (getRate() != 0) {
// We use number of ticks instead of nanoseconds because
// nanosecond resolution may not be available on some
// machines. Number of ticks guarantees the highest possible
// timer resolution.
duration = time_duration::ticks_per_second() / getRate();
}
// Calculate due time to initiate next chunk of exchanges.
send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
if (send_due_ > currentTime()) {
late_sent_ = true;
} else {
late_sent_ = false;
}
}
void
RateControl::setAggressivity(const int aggressivity) {
if (aggressivity < 1) {
isc_throw(isc::BadValue, "invalid value of aggressivity "
<< aggressivity << ", expected value is greater than 0");
}
aggressivity_ = aggressivity;
}
void
RateControl::setRate(const int rate) {
if (rate < 0) {
isc_throw(isc::BadValue, "invalid value of rate " << rate
<< ", expected non-negative value");
}
rate_ = rate;
}
void
RateControl::setRelativeDue(const int offset) {
send_due_ = offset > 0 ?
currentTime() + seconds(abs(offset)) :
currentTime() - seconds(abs(offset));
}
void
RateControl::updateSendTime() {
last_sent_ = currentTime();
}
} // namespace perfdhcp
} // namespace isc
// Copyright (C) 2013 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 RATE_CONTROL_H
#define RATE_CONTROL_H
#include <boost/date_time/posix_time/posix_time.hpp>
namespace isc {
namespace perfdhcp {
/// \brief A message sending rate control class for perfdhcp.
///
/// This class provides the means to control the rate at which messages
/// of the specific type are sent by perfdhcp. Each message type,
/// for which the desired rate can be specified, has a corresponding
/// \c RateControl object. So, the perfdhcp is using up to three objects
/// of this type at the same time, to control the rate of the following
/// messages being sent:
/// - Discover(DHCPv4) or Solicit (DHCPv6)
/// - Renew (DHCPv6) or Request (DHCPv4) to renew leases.
/// - Release
///
/// The purpose of the RateControl class is to track the due time for
/// sending next message (or bunch of messages) to keep outbound rate
/// of particular messages at the desired level. The due time is calculated
/// using the desired rate value and the timestamp when the last message of
/// the particular type has been sent. That puts the responsibility on the
/// \c TestControl class to invoke the \c RateControl::updateSendDue, every
/// time the message is sent.
///
/// The \c RateControl object returns the number of messages to be sent at
/// the time. The number returned is 0, if perfdhcp shouldn't send any messages
/// yet, or 1 (sometimes more) if the send due time has been reached.
class RateControl {
public:
/// \brief Default constructor.
RateControl();
/// \brief Constructor which sets desired rate and aggressivity.
///
/// \param rate A desired rate.
/// \param aggressivity A desired aggressivity.
RateControl(const int rate, const int aggressivity);
/// \brief Returns the value of aggressivity.
int getAggressivity() const {
return (aggressivity_);
}
/// \brief Returns current due time to send next message.
boost::posix_time::ptime getDue() const {
return (send_due_);
}
/// \brief Returns number of messages to be sent "now".
///
/// This function calculates how many messages of the given type should
/// be sent immediately when the call to the function returns, to catch
/// up with the desired message rate.
///
/// The value returned depends on the due time calculated with the
/// \c RateControl::updateSendDue function and the current time. If
/// the due time has been hit, the non-zero number of messages is returned.
/// If the due time hasn't been hit, the number returned is 0.
///
/// If the rate is non-zero, the number of messages to be sent is calculated
/// as follows:
/// \code
/// num = duration * rate
/// \endcode
/// where <b>duration</b> is a time period between the due time to send
/// next set of messages and current time. The duration is expressed in
/// seconds with the fractional part having 6 or 9 digits (depending on
/// the timer resolution). If the calculated value is equal to 0, it is
/// rounded to 1, so as at least one message is sent.
///
/// The value of aggressivity limits the maximal number of messages to
/// be sent one after another. If the number of messages calculated with
/// the equation above exceeds the aggressivity, this function will return
/// the value equal to aggressivity.
///
/// If the rate is not specified (equal to 0), the value calculated by
/// this function is equal to aggressivity.
///
/// \return A number of messages to be sent immediately.
uint64_t getOutboundMessageCount();
/// \brief Returns the rate.
int getRate() const {
return (rate_);
}
/// \brief Returns the value of the late send flag.
///
/// The flag returned by this function indicates whether the new due time
/// calculated by the \c RateControl::updateSendDue is in the past.
/// This value is used by the \c TestControl object to increment the counter
/// of the late sent messages in the \c StatsMgr.
bool isLateSent() const {
return (late_sent_);
}
/// \brief Sets the value of aggressivity.
///
/// \param aggressivity A new value of aggressivity. This value must be
/// a positive integer.
/// \throw isc::BadValue if new value is not a positive integer.
void setAggressivity(const int aggressivity);
/// \brief Sets the new rate.
///
/// \param rate A new value of rate. This value must not be negative.
/// \throw isc::BadValue if new rate is negative.
void setRate(const int rate);
/// \brief Sets the value of the due time.
///
/// This function is intended for unit testing. It manipulates the value of
/// the due time. The parameter passed to this function specifies the
/// (positive or negative) number of seconds relative to current time.
///
/// \param offset A number of seconds relative to current time which
/// constitutes the new due time.
void setRelativeDue(const int offset);
/// \brief Sets the timestamp of the last sent message to current time.
void updateSendTime();
protected:
/// \brief Convenience function returning current time.
///
/// \return current time.
static boost::posix_time::ptime currentTime();
/// \brief Calculates the send due.
///
/// This function calculates the send due timestamp using the current time
/// and desired rate. The due timestamp is calculated as a sum of the
/// timestamp when the last message was sent and the reciprocal of the rate
/// in micro or nanoseconds (depending on the timer resolution). If the rate
/// is not specified, the duration between two consecutive sends is one
/// timer tick.
void updateSendDue();
/// \brief Holds a timestamp when the next message should be sent.
boost::posix_time::ptime send_due_;
/// \brief Holds a timestamp when the last message was sent.
boost::posix_time::ptime last_sent_;
/// \brief Holds an aggressivity value.
int aggressivity_;
/// \brief Holds a desired rate value.
int rate_;
/// \brief A flag which indicates that the calculated due time is in the
/// past.
bool late_sent_;
};
}
}
#endif
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2013 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
......@@ -124,7 +124,8 @@ public:
XCHG_RA, ///< DHCPv4 REQUEST-ACK
XCHG_SA, ///< DHCPv6 SOLICIT-ADVERTISE
XCHG_RR, ///< DHCPv6 REQUEST-REPLY
XCHG_RN ///< DHCPv6 RENEW-REPLY
XCHG_RN, ///< DHCPv6 RENEW-REPLY
XCHG_RL ///< DHCPv6 RELEASE-REPLY
};
/// \brief Exchange Statistics.
......@@ -632,12 +633,19 @@ public:
/// Method prints main statistics for particular exchange.
/// Statistics includes: number of sent and received packets,
/// number of dropped packets and number of orphans.
///
/// \todo Currently the number of orphans is not displayed because
/// Reply messages received for Renew and Releases are counted as
/// orphans for the 4-way exchanges, which is wrong. We will need to
/// move the orphans counting out of the Statistics Manager so as
/// orphans counter is increased only if the particular message is
/// not identified as a reponse to any of the messages sent by perfdhcp.
void printMainStats() const {
using namespace std;
cout << "sent packets: " << getSentPacketsNum() << endl
<< "received packets: " << getRcvdPacketsNum() << endl
<< "drops: " << getDroppedPacketsNum() << endl
<< "orphans: " << getOrphans() << endl;
<< "drops: " << getDroppedPacketsNum() << endl;
// << "orphans: " << getOrphans() << endl;
}
/// \brief Print round trip time packets statistics.
......@@ -871,6 +879,20 @@ public:
boot_time_));
}
/// \brief Check if the exchange type has been specified.
///
/// This method checks if the \ref ExchangeStats object of a particular type
/// exists (has been added using \ref addExchangeStats function).
///
/// \param xchg_type A type of the exchange being repersented by the
/// \ref ExchangeStats object.
///
/// \return true if the \ref ExchangeStats object has been added for a
/// specified exchange type.
bool hasExchangeStats(const ExchangeType xchg_type) const {
return (exchanges_.find(xchg_type) != exchanges_.end());
}
/// \brief Add named custom uint64 counter.
///
/// Method creates new named counter and stores in counter's map under
......@@ -1159,7 +1181,7 @@ public:
///
/// \param xchg_type exchange type.
/// \return string representing name of the exchange.
std::string exchangeToString(ExchangeType xchg_type) const {
static std::string exchangeToString(ExchangeType xchg_type) {
switch(xchg_type) {
case XCHG_DO:
return("DISCOVER-OFFER");
......@@ -1171,6 +1193,8 @@ public:
return("REQUEST-REPLY");
case XCHG_RN:
return("RENEW-REPLY");
case XCHG_RL:
return("RELEASE-REPLY");
default:
return("Unknown exchange type");
}
......
......@@ -97,6 +97,21 @@ TestControl::TestControl() {
reset();
}
void
TestControl::checkLateMessages(RateControl& rate_control) {
// If diagnostics is disabled, there is no need to log late sent messages.
// If it is enabled and the rate control object indicates that the last
// sent message was late, bump up the counter in Stats Manager.
if (rate_control.isLateSent() && testDiags('i')) {
CommandOptions& options = CommandOptions::instance();
if (options.getIpVersion() == 4) {
stats_mgr4_->incrementCounter("latesend");
} else if (options.getIpVersion() == 6) {
stats_mgr6_->incrementCounter("latesend");
}
}
}
void
TestControl::cleanCachedPackets() {
CommandOptions& options = CommandOptions::instance();
......@@ -316,28 +331,43 @@ TestControl::checkExitConditions() const {
}
Pkt6Ptr
TestControl::createRenew(const Pkt6Ptr& reply) {
TestControl::createMessageFromReply(const uint16_t msg_type,
const dhcp::Pkt6Ptr& reply) {
// Restrict messages to Release and Renew.
if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
isc_throw(isc::BadValue, "invalid message type " << msg_type
<< " to be created from Reply, expected DHCPV6_RENEW or"
" DHCPV6_RELEASE");
}
// Get the string representation of the message - to be used for error
// logging purposes.
const char* msg_type_str = (msg_type == DHCPV6_RENEW ? "Renew" : "Release");
// Reply message must be specified.