Browse Source

[3183] Created a container class to hold received packets.

Marcin Siodelski 11 years ago
parent
commit
2c20227ed4

+ 1 - 0
tests/tools/perfdhcp/Makefile.am

@@ -23,6 +23,7 @@ perfdhcp_SOURCES += command_options.cc command_options.h
 perfdhcp_SOURCES += localized_option.h
 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 += stats_mgr.h
 perfdhcp_SOURCES += test_control.cc test_control.h

+ 146 - 0
tests/tools/perfdhcp/packet_storage.h

@@ -0,0 +1,146 @@
+// 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 PACKET_STORAGE_H
+#define PACKET_STORAGE_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <stdint.h>
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief Represents a list of packets with a sequential and random access to
+/// list elements.
+///
+/// The main purpose of this class is to support sending Renew and Release
+/// messages from perfdhcp. The Renew and Release messages are sent for existing
+/// leases only. Therefore, the typical use case for this class is that it holds
+/// a list of Rely messages sent by the server in response to Request messages.
+/// The Request messages hold addresses and/or IPv6 prefixes acquired so they
+/// can be used to identify existing leases. When perfdhcp needs to send Renew
+/// or Release message, it will access one of the elements on this list and
+/// will create the Renew or Release message based on its content. Once the
+/// element (packet) is returned it is also deleted from the list, so as it is
+/// not used again. This class provide either sequential access to the packets
+/// or random access. The random access algorithm is much slower but at least
+/// it allows to simulate more real scenario when the renewing or releasing
+/// client is random.
+///
+/// \tparam Pkt4 or Pkt6 class, which represents DHCPv4 or DHCPv6 message
+/// respectively.
+///
+/// \note Although the class is intended to hold Pkt4 and Pkt6 objects, the
+/// current implementation is generic enough to holds any object wrapped in the
+/// boost::shared_ptr.
+template<typename T>
+class PacketStorage : public boost::noncopyable {
+public:
+    /// A type which represents the pointer to a packet.
+    typedef boost::shared_ptr<T> PacketPtr;
+
+private:
+    /// An internal container actually holding packets.
+    typedef typename std::list<PacketPtr> PacketContainer;
+    /// An iterator to the element in the internal container.
+    typedef typename PacketContainer::iterator PacketContainerIterator;
+
+public:
+
+    /// \brief Constructor.
+    PacketStorage() { }
+
+    /// \brief Appends the new packet object to the collection.
+    ///
+    /// \param packet A pointer to an object representing a packet.
+    void append(const PacketPtr& packet) {
+        storage_.push_back(packet);
+        if (storage_.size() == 1) {
+            current_pointer_ = storage_.begin();
+        }
+    }
+
+    /// \brief Removes all packets from the storage.
+    void clear() {
+        storage_.clear();
+    }
+
+    /// \brief Checks if the storage has no packets.
+    ///
+    /// \return true if storage is empty, false otherwise.
+    bool empty() const {
+        return (storage_.empty());
+    }
+
+    /// \brief Returns next packet from the storage..
+    ///
+    /// This function returns packets sequentially (in the same order
+    /// in which they have been appended). The returned packet is
+    /// instantly removed from the storage.
+    ///
+    /// \return next packet from the storage.
+    PacketPtr getNext() {
+        if (storage_.empty()) {
+            return (PacketPtr());
+        } else if (current_pointer_ == storage_.end()) {
+            current_pointer_ = storage_.begin();
+        }
+        PacketPtr packet = *current_pointer_;
+        current_pointer_ = storage_.erase(current_pointer_);
+        return (packet);
+    }
+
+    /// \brief Returns random packet from the storage.
+    ///
+    /// This function picks random packet from the storage and returns
+    /// it. It is way slower than the @c getNext function because it has to
+    /// iterate over all existing entries from the beginning of the storage
+    /// to the random packet's position. Therefore, care should be taken
+    /// when using this function to access elements when storage is large.
+    ///
+    /// \return random packet from the storage.
+    PacketPtr getRandom() {
+        if (empty()) {
+            return (PacketPtr());
+        }
+        current_pointer_ = storage_.begin();
+        if (size() > 1) {
+            std::advance(current_pointer_, rand() % (size() - 1));
+        }
+        PacketPtr packet = *current_pointer_;
+        current_pointer_ = storage_.erase(current_pointer_);
+        return (packet);
+    }
+
+    /// \brief Returns number of packets in the storage.
+    ///
+    /// \return number of packets in the storage.
+    uint32_t size() const {
+        return (storage_.size());
+    }
+
+private:
+
+    std::list<PacketPtr> storage_;            ///< Holds all appended packets.
+    PacketContainerIterator current_pointer_; ///< Holds the iterator to the
+                                              ///< next element returned.
+
+};
+
+} // namespace perfdhcp
+} // namespace isc
+
+#endif // PACKET_STORAGE_H

+ 1 - 0
tests/tools/perfdhcp/tests/Makefile.am

@@ -25,6 +25,7 @@ run_unittests_SOURCES += command_options_unittest.cc
 run_unittests_SOURCES += perf_pkt6_unittest.cc
 run_unittests_SOURCES += perf_pkt4_unittest.cc
 run_unittests_SOURCES += localized_option_unittest.cc
+run_unittests_SOURCES += packet_storage_unittest.cc
 run_unittests_SOURCES += stats_mgr_unittest.cc
 run_unittests_SOURCES += test_control_unittest.cc
 run_unittests_SOURCES += command_options_helper.h

+ 145 - 0
tests/tools/perfdhcp/tests/packet_storage_unittest.cc

@@ -0,0 +1,145 @@
+// 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 "../packet_storage.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace perfdhcp;
+
+/// The number of packets in the test storage.
+const unsigned int STORAGE_SIZE = 20;
+
+/// Test fixture class for PacketStorage container testing.
+class PacketStorageTest : public ::testing::Test {
+public:
+    /// \brief Constructor, initializes the storage for each test.
+    PacketStorageTest() {
+        for (uint32_t i = 0; i < STORAGE_SIZE; ++i) {
+            storage_.append(createPacket6(DHCPV6_REPLY, i));
+        }
+    }
+
+    /// \brief Creates an instance of the Pkt6.
+    ///
+    /// \param packet_type A type of the packet.
+    /// \param transid Transaction id.
+    /// \return An instance of the Pkt6.
+    Pkt6Ptr createPacket6(const uint16_t packet_type,
+                          const uint32_t transid) {
+        return (Pkt6Ptr(new Pkt6(packet_type, transid)));
+    }
+
+    /// Packet storage under test.
+    PacketStorage<Pkt6> storage_;
+
+};
+
+// This test verifies that the packets in the storage can be accessed
+// sequentially and when a packet is returned, it is removed from the
+// storage. It also verifies the correctness of the behaviour of the
+// empty() and size() functions.
+TEST_F(PacketStorageTest, getNext) {
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    for (int i = 0; i < STORAGE_SIZE; ++i) {
+        Pkt6Ptr packet = storage_.getNext();
+        ASSERT_TRUE(packet) << "NULL packet returned by storage_.getNext() for"
+                            << " iteration number " << i;
+        EXPECT_EQ(i, packet->getTransid());
+        EXPECT_EQ(STORAGE_SIZE - i - 1, storage_.size());
+    }
+    EXPECT_TRUE(storage_.empty());
+    EXPECT_FALSE(storage_.getNext());
+    EXPECT_FALSE(storage_.getNext());
+
+    storage_.append(createPacket6(DHCPV6_REPLY, 100));
+    ASSERT_EQ(1, storage_.size());
+    Pkt6Ptr packet = storage_.getNext();
+    EXPECT_EQ(100, packet->getTransid());
+    EXPECT_FALSE(storage_.getNext());
+}
+
+// This test verifies that the packets in the storage can be accessed
+// randomly and when a packet is returned, it is removed from the
+// storage. It also verifies the correctness of the behaviour of the
+// empty() and size() functions.
+TEST_F(PacketStorageTest, getRandom) {
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    for (int i = 0; i < STORAGE_SIZE; ++i) {
+        Pkt6Ptr packet = storage_.getRandom();
+        ASSERT_TRUE(packet) << "NULL packet returned by storage_.getRandom()"
+            " for iteration number " << i;
+        EXPECT_EQ(STORAGE_SIZE - i - 1, storage_.size());
+    }
+    EXPECT_TRUE(storage_.empty());
+    EXPECT_FALSE(storage_.getRandom());
+    EXPECT_FALSE(storage_.getRandom());
+
+    storage_.append(createPacket6(DHCPV6_REPLY, 100));
+    ASSERT_EQ(1, storage_.size());
+    Pkt6Ptr packet = storage_.getRandom();
+    ASSERT_TRUE(packet);
+    EXPECT_EQ(100, packet->getTransid());
+    EXPECT_FALSE(storage_.getRandom());
+}
+
+// This test verifies that the packets in the storage can be accessed
+// either randomly or sequentially in the same time. It verifies that
+// each returned packet is removed from the storage.
+TEST_F(PacketStorageTest, getNextAndRandom) {
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    for (int i = 0; i < STORAGE_SIZE / 2; ++i) {
+        Pkt6Ptr packet_random = storage_.getRandom();
+        ASSERT_TRUE(packet_random) << "NULL packet returned by"
+            " storage_.getRandom() for iteration number " << i;
+        EXPECT_EQ(STORAGE_SIZE - 2 * i - 1, storage_.size());
+        uint32_t random_packet_transid = packet_random->getTransid();
+        Pkt6Ptr packet_seq = storage_.getNext();
+        ASSERT_TRUE(packet_seq) << "NULL packet returned by"
+            " storage_.getNext()  for iteration number " << i;
+        EXPECT_EQ(STORAGE_SIZE - 2 * i - 2, storage_.size());
+    }
+    EXPECT_TRUE(storage_.empty());
+    EXPECT_FALSE(storage_.getRandom());
+    EXPECT_FALSE(storage_.getNext());
+
+    storage_.append(createPacket6(DHCPV6_REPLY, 100));
+    storage_.append(createPacket6(DHCPV6_REPLY, 101));
+    ASSERT_EQ(2, storage_.size());
+    Pkt6Ptr packet_random = storage_.getRandom();
+    ASSERT_TRUE(packet_random);
+    EXPECT_EQ(100, packet_random->getTransid());
+    Pkt6Ptr packet_next = storage_.getNext();
+    ASSERT_TRUE(packet_next);
+    EXPECT_EQ(101, packet_next->getTransid());
+    EXPECT_FALSE(storage_.getRandom());
+    EXPECT_FALSE(storage_.getNext());
+}
+
+// This test verifies that all packets are removed from the storage when
+// clear() function is invoked.
+TEST_F(PacketStorageTest, clear) {
+    ASSERT_EQ(STORAGE_SIZE, storage_.size());
+    ASSERT_NO_THROW(storage_.clear());
+    EXPECT_TRUE(storage_.empty());
+}
+
+
+} // anonymous namespace