Commit 98e64a69 authored by Michal Nowikowski's avatar Michal Nowikowski

perfdhcp avalache: improvements after review

- simplified differentiating for IP version and exchange types
- added more comments
- fixed unittest for receiver by mocking socket
- added option for building perfdhcp by hammer
- added workaround for compiler bug that cannot handle enum class
  as a key to std::unordered_map
- hidden warnings from boost by changing compiler flag
  from -I<boost-path> to -isystem <boost-path>
- removed unused options_ field from StatsMgr class
parent a20cf461
......@@ -224,7 +224,9 @@ def execute(cmd, timeout=60, cwd=None, env=None, raise_error=True, dry_run=False
# if still running, kill harder
if p.poll() is None:
execute('sudo kill -s KILL %s' % p.pid)
raise ExecutionError('Execution timeout')
msg = "Execution timeout, %d > %d seconds elapsed (start: %d, stop %d), cmd: '%s'"
msg = msg % (t1 - t0, timeout, t0, t1, cmd)
raise ExecutionError(msg)
exitcode = p.returncode
if exitcode == 0:
......@@ -984,6 +986,8 @@ def _build_binaries_and_run_ut(system, revision, features, tarball_path, env, ch
cmd += ' --with-freeradius=/usr/local'
if 'shell' in features:
cmd += ' --enable-shell'
if 'perfdhcp' in features:
cmd += ' --enable-perfdhcp'
# do ./configure
execute(cmd, cwd=src_path, env=env, timeout=120, check_times=check_times, dry_run=dry_run)
......@@ -1016,7 +1020,7 @@ def _build_binaries_and_run_ut(system, revision, features, tarball_path, env, ch
env['KEA_SOCKET_TEST_DIR'] = '/tmp/'
# run unit tests
execute('make check -k',
cwd=src_path, env=env, timeout=60 * 60, raise_error=False,
cwd=src_path, env=env, timeout=90 * 60, raise_error=False,
check_times=check_times, dry_run=dry_run)
# parse unit tests results
......@@ -1052,7 +1056,8 @@ def _build_binaries_and_run_ut(system, revision, features, tarball_path, env, ch
f.write(json.dumps(results))
if 'install' in features:
execute('sudo make install', cwd=src_path, env=env, check_times=check_times, dry_run=dry_run)
execute('sudo make install', timeout=2 * 60,
cwd=src_path, env=env, check_times=check_times, dry_run=dry_run)
execute('sudo ldconfig', dry_run=dry_run) # TODO: this shouldn't be needed
if 'forge' in features:
......@@ -1244,9 +1249,9 @@ class CollectCommaSeparatedArgsAction(argparse.Action):
setattr(namespace, self.dest, values2)
DEFAULT_FEATURES = ['install', 'unittest', 'docs']
DEFAULT_FEATURES = ['install', 'unittest', 'docs', 'perfdhcp']
ALL_FEATURES = ['install', 'distcheck', 'unittest', 'docs', 'mysql', 'pgsql', 'cql', 'native-pkg',
'radius', 'shell', 'forge']
'radius', 'shell', 'forge', 'perfdhcp']
def parse_args():
......
......@@ -71,7 +71,7 @@ fi
# Check the path with some specific headers.
CPPFLAGS_SAVED="$CPPFLAGS"
if test "${boost_include_path}" ; then
BOOST_INCLUDES="-I${boost_include_path}"
BOOST_INCLUDES="-isystem ${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 boost/date_time/posix_time/posix_time_types.hpp boost/bind.hpp boost/function.hpp boost/asio.hpp boost/asio/ip/address.hpp boost/system/error_code.hpp boost/atomic.hpp boost/circular_buffer.hpp],,
......
......@@ -26,7 +26,16 @@ public:
/// \param socket reference to a socket.
AbstractScen(CommandOptions& options, BasePerfSocket &socket) :
options_(options),
tc_(options, socket) {};
tc_(options, socket)
{
if (options_.getIpVersion() == 4) {
stage1_xchg_ = ExchangeType::DO;
stage2_xchg_ = ExchangeType::RA;
} else {
stage1_xchg_ = ExchangeType::SA;
stage2_xchg_ = ExchangeType::RR;
}
};
/// \brief Run performance test.
///
......@@ -39,8 +48,13 @@ public:
virtual ~AbstractScen() {};
protected:
CommandOptions& options_;
CommandOptions& options_; ///< Reference to commandline options.
TestControl tc_; ///< Object for controling sending and receiving packets.
// Helper fields to avoid checking IP version each time an exchange type
// is needed.
ExchangeType stage1_xchg_;
ExchangeType stage2_xchg_;
};
......
......@@ -128,16 +128,9 @@ AvalancheScen::run() {
if (now - prev_cycle_time > milliseconds(200)) { // check if 0.2s elapsed
prev_cycle_time = now;
int still_left_cnt = 0;
if (options_.getIpVersion() == 4) {
still_left_cnt += resendPackets(ExchangeType::DO);
if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
still_left_cnt += resendPackets(ExchangeType::RA);
}
} else {
still_left_cnt += resendPackets(ExchangeType::SA);
if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
still_left_cnt += resendPackets(ExchangeType::RR);
}
still_left_cnt += resendPackets(stage1_xchg_);
if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
still_left_cnt += resendPackets(stage2_xchg_);
}
if (still_left_cnt == 0) {
......@@ -173,22 +166,16 @@ AvalancheScen::run() {
}
// Calculate total stats.
int total_sent_pkts = total_resent_;
int total_rcvd_pkts = 0;
if (options_.getIpVersion() == 4) {
total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO);
total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO);
if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA);
total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA);
}
} else {
total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA);
total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA);
if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR);
total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR);
}
int total_sent_pkts = total_resent_; // This holds sent + resent packets counts.
int total_rcvd_pkts = 0; // This holds received packets count.
// Get sent and received counts for DO/SA (stage1) exchange from StatsMgr.
total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(stage1_xchg_);
total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(stage1_xchg_);
// Get sent and received counts for RA/RR (stage2) exchange from StatsMgr
// if RA/RR was not disabled.
if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(stage2_xchg_);
total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(stage2_xchg_);
}
std::cout << "It took " << duration.length() << " to provision " << clients_num
......
......@@ -15,6 +15,17 @@
namespace isc {
namespace perfdhcp {
// This class fixes an issue in older compilers
// that cannot handle enum class as key in std::unordered_map.
// See: https://stackoverflow.com/questions/18837857/cant-use-enum-class-as-unordered-map-key
struct EnumClassHash
{
template <typename T>
std::size_t operator()(T t) const
{
return static_cast<std::size_t>(t);
}
};
/// \brief Avalanche Scenario class.
///
......@@ -46,9 +57,9 @@ protected:
BasePerfSocket &socket_;
/// A map xchg type -> (a map of trans id -> retransmissions count.
std::unordered_map<ExchangeType, std::unordered_map<uint32_t, int>> retransmissions_;
std::unordered_map<ExchangeType, std::unordered_map<uint32_t, int>, EnumClassHash> retransmissions_;
/// A map xchg type -> (a map of trans id -> time of sending first packet.
std::unordered_map<ExchangeType, std::unordered_map<uint32_t, boost::posix_time::ptime>> start_times_;
std::unordered_map<ExchangeType, std::unordered_map<uint32_t, boost::posix_time::ptime>, EnumClassHash> start_times_;
/// Total number of resent packets.
int total_resent_;
......
......@@ -27,50 +27,32 @@ BasicScen::checkExitConditions() {
const StatsMgr& stats_mgr(tc_.getStatsMgr());
bool test_period_reached = false;
// Check if test period passed.
if (options_.getPeriod() != 0) {
time_period period(stats_mgr.getTestPeriod());
if (period.length().total_seconds() >= options_.getPeriod()) {
test_period_reached = true;
}
}
if (test_period_reached) {
if (options_.testDiags('e')) {
std::cout << "reached test-period." << std::endl;
}
if (!tc_.waitToExit()) {
return true;
if (options_.testDiags('e')) {
std::cout << "reached test-period." << std::endl;
}
if (!tc_.waitToExit()) {
return true;
}
}
}
bool max_requests = false;
// Check if we reached maximum number of DISCOVER/SOLICIT sent.
if (options_.getNumRequests().size() > 0) {
if (options_.getIpVersion() == 4) {
if (stats_mgr.getSentPacketsNum(ExchangeType::DO) >=
options_.getNumRequests()[0]) {
max_requests = true;
}
} else if (options_.getIpVersion() == 6) {
if (stats_mgr.getSentPacketsNum(ExchangeType::SA) >=
options_.getNumRequests()[0]) {
max_requests = true;
}
if (stats_mgr.getSentPacketsNum(stage1_xchg_) >=
options_.getNumRequests()[0]) {
max_requests = true;
}
}
// Check if we reached maximum number REQUEST packets.
if (options_.getNumRequests().size() > 1) {
if (options_.getIpVersion() == 4) {
if (stats_mgr.getSentPacketsNum(ExchangeType::RA) >=
options_.getNumRequests()[1]) {
max_requests = true;
}
} else if (options_.getIpVersion() == 6) {
if (stats_mgr.getSentPacketsNum(ExchangeType::RR) >=
options_.getNumRequests()[1]) {
max_requests = true;
}
if (stats_mgr.getSentPacketsNum(stage2_xchg_) >=
options_.getNumRequests()[1]) {
max_requests = true;
}
}
if (max_requests) {
......@@ -85,30 +67,16 @@ BasicScen::checkExitConditions() {
// Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
bool max_drops = false;
if (options_.getMaxDrop().size() > 0) {
if (options_.getIpVersion() == 4) {
if (stats_mgr.getDroppedPacketsNum(ExchangeType::DO) >=
options_.getMaxDrop()[0]) {
max_drops = true;
}
} else if (options_.getIpVersion() == 6) {
if (stats_mgr.getDroppedPacketsNum(ExchangeType::SA) >=
options_.getMaxDrop()[0]) {
max_drops = true;
}
if (stats_mgr.getDroppedPacketsNum(stage1_xchg_) >=
options_.getMaxDrop()[0]) {
max_drops = true;
}
}
// Check if we reached maximum number of drops of ACK/REPLY packets.
if (options_.getMaxDrop().size() > 1) {
if (options_.getIpVersion() == 4) {
if (stats_mgr.getDroppedPacketsNum(ExchangeType::RA) >=
options_.getMaxDrop()[1]) {
max_drops = true;
}
} else if (options_.getIpVersion() == 6) {
if (stats_mgr.getDroppedPacketsNum(ExchangeType::RR) >=
options_.getMaxDrop()[1]) {
max_drops = true;
}
if (stats_mgr.getDroppedPacketsNum(stage2_xchg_) >=
options_.getMaxDrop()[1]) {
max_drops = true;
}
}
if (max_drops) {
......@@ -123,39 +91,22 @@ BasicScen::checkExitConditions() {
// Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
bool max_pdrops = false;
if (options_.getMaxDropPercentage().size() > 0) {
if (options_.getIpVersion() == 4) {
if ((stats_mgr.getSentPacketsNum(ExchangeType::DO) > 10) &&
((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::DO) /
stats_mgr.getSentPacketsNum(ExchangeType::DO)) >=
options_.getMaxDropPercentage()[0])) {
max_pdrops = true;
}
} else if (options_.getIpVersion() == 6) {
if ((stats_mgr.getSentPacketsNum(ExchangeType::SA) > 10) &&
((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::SA) /
stats_mgr.getSentPacketsNum(ExchangeType::SA)) >=
options_.getMaxDropPercentage()[0])) {
max_pdrops = true;
}
if ((stats_mgr.getSentPacketsNum(stage1_xchg_) > 10) &&
((100. * stats_mgr.getDroppedPacketsNum(stage1_xchg_) /
stats_mgr.getSentPacketsNum(stage1_xchg_)) >=
options_.getMaxDropPercentage()[0]))
{
max_pdrops = true;
}
}
// Check if we reached maximum drops percentage of ACK/REPLY packets.
if (options_.getMaxDropPercentage().size() > 1) {
if (options_.getIpVersion() == 4) {
if ((stats_mgr.getSentPacketsNum(ExchangeType::RA) > 10) &&
((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::RA) /
stats_mgr.getSentPacketsNum(ExchangeType::RA)) >=
options_.getMaxDropPercentage()[1])) {
max_pdrops = true;
}
} else if (options_.getIpVersion() == 6) {
if ((stats_mgr.getSentPacketsNum(ExchangeType::RR) > 10) &&
((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::RR) /
stats_mgr.getSentPacketsNum(ExchangeType::RR)) >=
options_.getMaxDropPercentage()[1])) {
max_pdrops = true;
}
if ((stats_mgr.getSentPacketsNum(stage2_xchg_) > 10) &&
((100. * stats_mgr.getDroppedPacketsNum(stage2_xchg_) /
stats_mgr.getSentPacketsNum(stage2_xchg_)) >=
options_.getMaxDropPercentage()[1]))
{
max_pdrops = true;
}
}
if (max_pdrops) {
......
......@@ -404,6 +404,9 @@ private:
/// \brief Validates initialized options.
///
/// It checks provided options. If there are issues they are reported
/// and exception is raised. If possible some options are corrected
/// e.g. overriding drop_time in case of avalanche scenario.
/// \throws isc::InvalidParameter if command line validation fails.
void validate();
......
......@@ -17,11 +17,18 @@
namespace isc {
namespace perfdhcp {
/// \brief Socket wrapper structure.
///
/// This is a base class that is inherited by PerfSocket
/// and unit tests derived that. This way it allows mocking
/// out socket operations and avoid using real network
/// interfaces.
class BasePerfSocket : public dhcp::SocketInfo {
public:
/// Interface index.
uint16_t ifindex_;
/// \brief Default constructor of BasePerfSocket.
BasePerfSocket() :
SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, 0),
ifindex_(0) {}
......@@ -29,10 +36,19 @@ public:
/// \brief Destructor of the socket wrapper class.
virtual ~BasePerfSocket() = default;
/// \brief See description of this method in PerfSocket class below.
virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) = 0;
/// \brief See description of this method in PerfSocket class below.
virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) = 0;
/// \brief See description of this method in PerfSocket class below.
virtual bool send(const dhcp::Pkt4Ptr& pkt) = 0;
/// \brief See description of this method in PerfSocket class below.
virtual bool send(const dhcp::Pkt6Ptr& pkt) = 0;
/// \brief See description of this method in PerfSocket class below.
virtual dhcp::IfacePtr getIface() = 0;
};
......
......@@ -316,8 +316,7 @@ ExchangeStats::printTimestamps() {
StatsMgr::StatsMgr(CommandOptions& options) :
exchanges_(),
boot_time_(boost::posix_time::microsec_clock::universal_time()),
options_(options)
boot_time_(boost::posix_time::microsec_clock::universal_time())
{
// Check if packet archive mode is required. If user
// requested diagnostics option -x t we have to enable
......
......@@ -1110,8 +1110,6 @@ private:
bool archive_enabled_;
boost::posix_time::ptime boot_time_; ///< Time when test is started.
CommandOptions& options_;
};
/// Pointer to Statistics Manager;
......
......@@ -126,7 +126,7 @@ public:
class TestControl : public boost::noncopyable {
public:
/// \brief Default constructor.
TestControl(CommandOptions& options, BasePerfSocket &socket);
TestControl(CommandOptions& options, BasePerfSocket& socket);
/// Packet template buffer.
typedef std::vector<uint8_t> TemplateBuffer;
......
......@@ -43,7 +43,9 @@ public:
};
TEST_F(PerfSocketTest, Basic) {
TEST_F(PerfSocketTest, WrongCommandOptions) {
// Check if incorrect command options are casing failure during
// socket setup.
CommandOptions opt;
// make sure we catch -6 paired with v4 address
......
......@@ -8,25 +8,90 @@
#include "command_options_helper.h"
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
#include "receiver.h"
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::dhcp;
using namespace isc::perfdhcp;
/// \brief FakeReceiverPerfSocket class that mocks PerfSocket.
///
/// It stubs send and receive operations and collects statistics.
class FakeReceiverPerfSocket: public BasePerfSocket {
public:
/// \brief Default constructor for FakeReceiverPerfSocket.
FakeReceiverPerfSocket() :
iface_(boost::make_shared<Iface>("fake", 0)),
sent_cnt_(0),
recv_cnt_(0) {};
IfacePtr iface_; ///< Local fake interface.
int sent_cnt_; ///< Counter of sent packets
int recv_cnt_; ///< Counter of received packets.
/// \brief Simulate receiving DHCPv4 packet.
virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override {
(void)timeout_sec; // silence compile 'unused parameter' warning;
(void)timeout_usec; // silence compile 'unused parameter' warning;
recv_cnt_++;
// slow down receiving as receiver calls it in a loop thousands of time
// if null is returned
usleep(50);
return(dhcp::Pkt4Ptr());
};
/// \brief Simulate receiving DHCPv6 packet.
virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override {
(void)timeout_sec; // silence compile 'unused parameter' warning;
(void)timeout_usec; // silence compile 'unused parameter' warning;
recv_cnt_++;
return(dhcp::Pkt6Ptr());
};
/// \brief Simulate sending DHCPv4 packet.
virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
sent_cnt_++;
pkt->updateTimestamp();
return true;
};
/// \brief Simulate sending DHCPv6 packet.
virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
sent_cnt_++;
pkt->updateTimestamp();
return true;
};
/// \brief Override getting interface.
virtual IfacePtr getIface() override { return iface_; }
void reset() {
sent_cnt_ = 0;
recv_cnt_ = 0;
}
};
TEST(Receiver, singleThreaded) {
CommandOptions opt;
CommandOptionsHelper::process(opt, "perfdhcp -g single -l 127.0.0.1 all");
ASSERT_TRUE(opt.isSingleThreaded());
PerfSocket socket(opt);
FakeReceiverPerfSocket socket;
Receiver receiver(socket, opt.isSingleThreaded(), opt.getIpVersion());
ASSERT_NO_THROW(receiver.start());
auto pkt = receiver.getPkt();
EXPECT_EQ(pkt, nullptr);
ASSERT_NO_THROW(receiver.stop());
......@@ -38,12 +103,13 @@ TEST(Receiver, multiThreaded) {
CommandOptionsHelper::process(opt, "perfdhcp -g multi -l 127.0.0.1 all");
ASSERT_FALSE(opt.isSingleThreaded());
PerfSocket socket(opt);
FakeReceiverPerfSocket socket;
Receiver receiver(socket, opt.isSingleThreaded(), opt.getIpVersion());
ASSERT_NO_THROW(receiver.start());
auto pkt = receiver.getPkt();
EXPECT_EQ(pkt, nullptr);
ASSERT_NO_THROW(receiver.stop());
......
......@@ -34,10 +34,10 @@ using namespace isc::perfdhcp;
/// \brief FakePerfSocket class that mocks PerfSocket.
///
/// It stubs send and receive operations and collects statistics.
class FakePerfSocket: public BasePerfSocket {
class FakeTestControlPerfSocket: public BasePerfSocket {
public:
/// \brief Default constructor for FakePerfSocket.
FakePerfSocket() :
FakeTestControlPerfSocket() :
iface_(boost::make_shared<Iface>("fake", 0)),
sent_cnt_(0),
recv_cnt_(0) {};
......@@ -169,7 +169,7 @@ public:
using TestControl::options_;
using TestControl::stats_mgr_;
FakePerfSocket fake_sock_;
FakeTestControlPerfSocket fake_sock_;
NakedTestControl(CommandOptions &opt) : TestControl(opt, fake_sock_) {
uint32_t clients_num = opt.getClientsNum() == 0 ?
......
// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment