# 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:
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)
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
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:
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():
# 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],,
/// \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.
///
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_;
};
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) {
}
// 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
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.
///
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_;
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) {
// 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) {
// 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) {
/// \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();
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) {}
/// \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;
};
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
bool archive_enabled_;
boost::posix_time::ptime boot_time_; ///< Time when test is started.
-
- CommandOptions& options_;
};
/// Pointer to Statistics Manager;
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;
};
-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
#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());
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());
/// \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) {};
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