AM_LDFLAGS += -static
endif
-lib_LTLIBRARIES = libperfdhcp++.la
-libperfdhcp___la_SOURCES = command_options.cc command_options.h
-libperfdhcp___la_SOURCES += localized_option.h
-libperfdhcp___la_SOURCES += perf_pkt6.cc perf_pkt6.h
-libperfdhcp___la_SOURCES += perf_pkt4.cc perf_pkt4.h
-libperfdhcp___la_SOURCES += pkt_transform.cc pkt_transform.h
-
-libperfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
+pkglibexec_PROGRAMS = perfdhcp2
+perfdhcp2_SOURCES = main.cc
+perfdhcp2_SOURCES += command_options.cc command_options.h
+perfdhcp2_SOURCES += localized_option.h
+perfdhcp2_SOURCES += perf_pkt6.cc perf_pkt6.h
+perfdhcp2_SOURCES += perf_pkt4.cc perf_pkt4.h
+perfdhcp2_SOURCES += pkt_transform.cc pkt_transform.h
+perfdhcp2_SOURCES += test_control.cc test_control.h
+
+perfdhcp2_CXXFLAGS = $(AM_CXXFLAGS)
if USE_CLANGPP
# Disable unused parameter warning caused by some of the
# Boost headers when compiling with clang.
-libperfdhcp___la_CXXFLAGS += -Wno-unused-parameter
+perfdhcp2_CXXFLAGS += -Wno-unused-parameter
endif
-libperfdhcp___la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
-libperfdhcp___la_LIBADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
-libperfdhcp___la_LIBADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+perfdhcp2_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+perfdhcp2_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
+perfdhcp2_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-pkglibexec_PROGRAMS = perfdhcp
-perfdhcp_SOURCES = perfdhcp.c
+#pkglibexec_PROGRAMS = perfdhcp
+#perfdhcp_SOURCES = perfdhcp.c
--- /dev/null
+// Copyright (C) 2012 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 <iostream>
+#include <stdint.h>
+
+#include <config.h>
+#include <exceptions/exceptions.h>
+
+#include "test_control.h"
+#include "command_options.h"
+
+using namespace isc::perfdhcp;
+
+int
+main(int argc, char* argv[]) {
+ CommandOptions& command_options = CommandOptions::instance();
+ try {
+ command_options.parse(argc, argv);
+ } catch(isc::Exception& e) {
+ std::cout << "Error parsing command line options: "
+ << e.what() << std::endl;
+ command_options.usage();
+ return(1);
+ }
+ try{
+ TestControl& test_control = TestControl::instance();
+ test_control.run();
+ } catch (isc::Exception& e) {
+ std::cout << "Error starting perfdhcp: " << e.what() << std::endl;
+ return(1);
+ }
+ return(0);
+}
+
--- /dev/null
+// Copyright (C) 2012 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <dhcp/libdhcp++.h>
+#include <dhcp/dhcp4.h>
+#include <exceptions/exceptions.h>
+#include "test_control.h"
+#include "command_options.h"
+
+using namespace std;
+using namespace boost;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace isc {
+namespace perfdhcp {
+
+TestControl&
+TestControl::instance() {
+ static TestControl test_control;
+ return (test_control);
+}
+
+TestControl::TestControl() :
+ send_due_(microsec_clock::universal_time()),
+ last_sent_(send_due_) {
+}
+
+bool
+TestControl::checkExitConditions() const {
+ CommandOptions& options = CommandOptions::instance();
+ if ((options.getNumRequests().size() > 0) &&
+ (sent_packets_0_ >= options.getNumRequests()[0])) {
+ return(true);
+ } else if ((options.getNumRequests().size() == 2) &&
+ (sent_packets_1_ >= options.getNumRequests()[1])) {
+ return(true);
+ }
+ return(false);
+}
+
+Pkt4*
+TestControl::createDiscoverPkt4() {
+ const uint32_t transid = static_cast<uint32_t>(random());
+ Pkt4* pkt4 = new Pkt4(DHCPDISCOVER, transid);
+
+ OptionBuffer opt_request_list_buf();
+ // createRequestListBuffer4(opt_request_list_buf);
+ return NULL;
+}
+
+OptionPtr
+TestControl::factoryRequestList4(Option::Universe u,
+ uint16_t type,
+ const OptionBuffer& buf)
+{
+ const uint8_t buf_array[] = {
+ DHO_SUBNET_MASK,
+ DHO_BROADCAST_ADDRESS,
+ DHO_TIME_OFFSET,
+ DHO_ROUTERS,
+ DHO_DOMAIN_NAME,
+ DHO_DOMAIN_NAME_SERVERS,
+ DHO_HOST_NAME
+ };
+
+ OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
+ Option* opt = new Option(u, type, buf);
+ opt->setData(buf_with_options.begin(),
+ buf_with_options.end());
+ return OptionPtr(opt);
+}
+
+uint64_t
+TestControl::getNextExchangesNum() const {
+ CommandOptions& options = CommandOptions::instance();
+ // Reset number of exchanges.
+ uint64_t due_exchanges = 0;
+ // Get current time.
+ ptime now(microsec_clock::universal_time());
+ // The due time indicates when we should start sening next chunk
+ // of packets. If it is already due time, we should calculate
+ // how many packets to send.
+ if (now >= send_due_) {
+ // If rate is specified from the command line we have to
+ // synchornize with it.
+ if (options.getRate() != 0) {
+ time_period period(send_due_, now);
+ // Null condition should not occur because we
+ // have checked it in the first if statement but
+ // let's keep this check just in case.
+ if (period.is_null()) {
+ return (0);
+ }
+ 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 * options.getRate());
+ // We want to make sure that at least one packet goes out.
+ due_exchanges += 1;
+ // We should not exceed aggressivity as it could have been
+ // restricted from command line.
+ if (due_exchanges > options.getAggressivity()) {
+ due_exchanges = options.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 = options.getAggressivity();
+ }
+ return (due_exchanges);
+ }
+ return (0);
+}
+
+void
+TestControl::registerOptionFactories4() const {
+ static bool factories_registered = false;
+ if (!factories_registered) {
+ LibDHCP::OptionFactoryRegister(Option::V4,
+ DHO_DHCP_PARAMETER_REQUEST_LIST,
+ &TestControl::factoryRequestList4);
+ }
+ factories_registered = true;
+}
+
+void
+TestControl::registerOptionFactories6() const {
+ static bool factories_registered = false;
+ if (!factories_registered) {
+ }
+ factories_registered = true;
+}
+
+void
+TestControl::registerOptionFactories() const {
+ CommandOptions& options = CommandOptions::instance();
+ switch(options.getIpVersion()) {
+ case 4:
+ registerOptionFactories4();
+ break;
+ case 6:
+ registerOptionFactories6();
+ break;
+ default:
+ isc_throw(InvalidOperation, "command line options have to be parsed "
+ "before DHCP option factories can be registered");
+ }
+}
+
+void
+TestControl::run() {
+ sent_packets_0_ = 0;
+ sent_packets_1_ = 0;
+ CommandOptions& options = CommandOptions::instance();
+ // Ip version is not set ONLY in case the command options
+ // where not parsed. This surely means that parse() function
+ // was not called prior to starting the test. This is fatal
+ // error.
+ if (options.getIpVersion() == 0) {
+ isc_throw(InvalidOperation, "command options must be parsed before running "
+ "a test");
+ }
+ registerOptionFactories();
+ uint64_t packets_sent = 0;
+ for (;;) {
+ updateSendDue();
+ if (checkExitConditions()) {
+ break;
+ }
+ uint64_t packets_due = getNextExchangesNum();
+ for (uint64_t i = packets_due; i > 0; --i) {
+ startExchange();
+ ++packets_sent;
+ cout << "Packets sent " << packets_sent << endl;
+ }
+ }
+
+}
+
+void
+TestControl::startExchange() {
+ ++sent_packets_0_;
+ last_sent_ = microsec_clock::universal_time();
+}
+
+void
+TestControl::updateSendDue() {
+ // If default constructor was called, this should not happen but
+ // if somebody has changed default constructor it is better to
+ // keep this check.
+ if (last_sent_.is_not_a_date_time()) {
+ isc_throw(Unexpected, "time of last sent packet not initialized");
+ }
+ // Get the expected exchange rate.
+ CommandOptions& options = CommandOptions::instance();
+ int rate = options.getRate();
+ // 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 (rate != 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() / rate;
+ }
+ // Calculate due time to initate next chunk of exchanges.
+ send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
+}
+
+
+} // namespace perfdhcp
+} // namespace isc
--- /dev/null
+// Copyright (C) 2012 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 __TEST_CONTROL_H
+#define __TEST_CONTROL_H
+
+#include <string>
+#include <vector>
+
+#include <boost/noncopyable.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief Test Control class.
+///
+/// This class is responsible for running whole perfdhcp test.
+///
+class TestControl : public boost::noncopyable {
+public:
+ /// TestControl is a singleton class. This method returns reference
+ /// to its sole instance.
+ ///
+ /// \return the only existing instance of test control
+ static TestControl& instance();
+
+ /// Run performance test.
+ ///
+ /// Method runs whole performance test. Command line options must
+ /// be parsed prior to running this function. Othewise function will
+ /// throw exception.
+ ///
+ /// \throw isc::InvalidOperation if command line options are not parsed.
+ /// \throw isc::Unexpected if internal Test Controler error occured.
+ void run();
+
+private:
+
+ /// \brief Private default constructor.
+ ///
+ /// Default constructor is private as the object can be created
+ /// only via \ref instance method.
+ TestControl();
+
+ /// \brief Check if test exit condtitions fulfiled.
+ ///
+ /// Method checks if test exit conditions are fulfiled.
+ /// Exit conditions are checked periodically from the
+ /// main loop. Program should break the main loop when
+ /// this method returns true. It is calling function
+ /// responsibility to break main loop gracefully and
+ /// cleanup after test execution.
+ ///
+ /// \return true if any of the exit conditions is fulfiled.
+ bool checkExitConditions() const;
+
+ dhcp::Pkt4* createDiscoverPkt4();
+
+ static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u,
+ uint16_t type,
+ const dhcp::OptionBuffer& buf);
+
+ /// \brief Returns number of exchanges to be started.
+ ///
+ /// Method returns number of new exchanges to be started as soon
+ /// as possible to satisfy expected rate. Calculation used here
+ /// is based on current time, due time calculated with
+ /// \ref updateSendTime function and expected rate.
+ ///
+ /// \return number of exchanges to be started immediatelly.
+ uint64_t getNextExchangesNum() const;
+
+ void registerOptionFactories4() const;
+
+ void registerOptionFactories6() const;
+
+ void registerOptionFactories() const;
+
+ /// \brief Start new exchange of DHCP messages.
+ ///
+ void startExchange();
+
+ /// \brief Update due time to initiate next chunk of exchanges.
+ ///
+ /// Method updates due time to initiate next chunk of exchanges.
+ /// Function takes current time, last sent packet's time and
+ /// expected rate in its calculations.
+ void updateSendDue();
+
+ boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk
+ ///< of exchanges.
+ boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange
+ /// was initiated.
+
+ uint64_t sent_packets_0_;
+ uint64_t sent_packets_1_;
+};
+
+} // namespace perfdhcp
+} // namespace isc
+
+#endif // __COMMAND_OPTIONS_H
run_unittests_SOURCES += perf_pkt6_unittest.cc
run_unittests_SOURCES += perf_pkt4_unittest.cc
run_unittests_SOURCES += localized_option_unittest.cc
+run_unittests_SOURCES += test_control_unittest.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt4.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/test_control.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
--- /dev/null
+// Copyright (C) 2012 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 <cstddef>
+#include <stdint.h>
+#include <string>
+#include <gtest/gtest.h>
+
+#include "../test_control.h"
+#include "../command_options.h"
+#include "exceptions/exceptions.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::perfdhcp;
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp TestControl class.
+class TestControlTest : public virtual ::testing::Test
+{
+public:
+ /// \brief Default Constructor
+ TestControlTest() { }
+
+protected:
+};
+
+TEST_F(TestControlTest, Run) {
+ // Get the instance of TestControl object.
+ TestControl& test_control = TestControl::instance();
+ // Running test without parsing command line arguments is
+ // expected to cause exception.
+ EXPECT_THROW(test_control.run(), isc::InvalidOperation);
+
+ // The command line is to run single test iteration and exit.
+ // We have to declare argv as const walk around the problem
+ // of deprecated conversion from string to char*.
+ const char* argv[] = { "perfdhcp", "-l", "eth0", "-r", "10", "-n", "1" };
+ const int argc = sizeof(argv) / sizeof(argv[0]);
+ CommandOptions& options = CommandOptions::instance();
+
+ // const_cast is odd but it seems to be the most straight forward way
+ // to achive the goal. In the future we might think about creating
+ // a tokenizing function that would dynamically produce non-const
+ // argv value.
+ ASSERT_NO_THROW(options.parse(argc, const_cast<char**>(argv)));
+ EXPECT_NO_THROW(test_control.run());
+
+ // This is ok to run the test again with the same parameters.
+ // It may trigger exception if TestControl singleton is in
+ // invalid state after test run. We want to make sure it is
+ // safe to rerun the test.
+ EXPECT_NO_THROW(test_control.run());
+}