<div dir="ltr">Reviewed-by: Andrew Bailey <<a href="mailto:abailey@iol.unh.edu" target="_blank">abailey@iol.unh.edu</a>></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Wed, Nov 5, 2025 at 5:37 PM Patrick Robb <<a href="mailto:probb@iol.unh.edu">probb@iol.unh.edu</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">From: Nicholas Pratte <<a href="mailto:npratte@iol.unh.edu" target="_blank">npratte@iol.unh.edu</a>><br>
<br>
Rework TG class hierarchy to include performance traffic generators.<br>
As such, methods specific to capturing traffic have been moved to the<br>
CapturingTrafficGenerator subclass.<br>
<br>
Bugzilla ID: 1697<br>
Signed-off-by: Nicholas Pratte <<a href="mailto:npratte@iol.unh.edu" target="_blank">npratte@iol.unh.edu</a>><br>
Signed-off-by: Patrick Robb <<a href="mailto:probb@iol.unh.edu" target="_blank">probb@iol.unh.edu</a>><br>
Reviewed-by: Dean Marx <<a href="mailto:dmarx@iol.unh.edu" target="_blank">dmarx@iol.unh.edu</a>><br>
Reviewed-by: Andrew Bailey <<a href="mailto:abailey@iol.unh.edu" target="_blank">abailey@iol.unh.edu</a>><br>
---<br>
 .../capturing_traffic_generator.py            | 34 +++++++++++<br>
 .../performance_traffic_generator.py          | 56 +++++++++++++++++++<br>
 .../traffic_generator/traffic_generator.py    | 38 -------------<br>
 3 files changed, 90 insertions(+), 38 deletions(-)<br>
 create mode 100644 dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py<br>
<br>
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py<br>
index ec0993e6b7..734a66d1f3 100644<br>
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py<br>
+++ b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py<br>
@@ -65,6 +65,40 @@ def is_capturing(self) -> bool:<br>
         """This traffic generator can capture traffic."""<br>
         return True<br>
<br>
+    def send_packet(self, packet: Packet, port: Port) -> None:<br>
+        """Send `packet` and block until it is fully sent.<br>
+<br>
+        Send `packet` on `port`, then wait until `packet` is fully sent.<br>
+<br>
+        Args:<br>
+            packet: The packet to send.<br>
+            port: The egress port on the TG node.<br>
+        """<br>
+        self.send_packets([packet], port)<br>
+<br>
+    def send_packets(self, packets: list[Packet], port: Port) -> None:<br>
+        """Send `packets` and block until they are fully sent.<br>
+<br>
+        Send `packets` on `port`, then wait until `packets` are fully sent.<br>
+<br>
+        Args:<br>
+            packets: The packets to send.<br>
+            port: The egress port on the TG node.<br>
+        """<br>
+        self._<a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(f"Sending packet{'s' if len(packets) > 1 else ''}.")<br>
+        self._logger.debug(get_packet_summaries(packets))<br>
+        self._send_packets(packets, port)<br>
+<br>
+    @abstractmethod<br>
+    def _send_packets(self, packets: list[Packet], port: Port) -> None:<br>
+        """The implementation of :method:`send_packets`.<br>
+<br>
+        The subclasses must implement this method which sends `packets` on `port`.<br>
+        The method should block until all `packets` are fully sent.<br>
+<br>
+        What fully sent means is defined by the traffic generator.<br>
+        """<br>
+<br>
     def send_packets_and_capture(<br>
         self,<br>
         packets: list[Packet],<br>
diff --git a/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py b/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py<br>
new file mode 100644<br>
index 0000000000..09abe9a66b<br>
--- /dev/null<br>
+++ b/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py<br>
@@ -0,0 +1,56 @@<br>
+"""Traffic generators for performance tests which can generate a high number of packets."""<br>
+<br>
+from abc import abstractmethod<br>
+from dataclasses import dataclass<br>
+<br>
+from scapy.packet import Packet<br>
+<br>
+from .traffic_generator import TrafficGenerator<br>
+<br>
+<br>
+@dataclass(slots=True)<br>
+class PerformanceTrafficStats:<br>
+    """Data structure to store performance statistics for a given test run.<br>
+<br>
+    Attributes:<br>
+        tx_pps: Recorded tx packets per second.<br>
+        tx_bps: Recorded tx bytes per second.<br>
+        rx_pps: Recorded rx packets per second.<br>
+        rx_bps: Recorded rx bytes per second.<br>
+        frame_size: The total length of the frame.<br>
+    """<br>
+<br>
+    tx_pps: float<br>
+    tx_bps: float<br>
+    rx_pps: float<br>
+    rx_bps: float<br>
+<br>
+    frame_size: int | None = None<br>
+<br>
+<br>
+class PerformanceTrafficGenerator(TrafficGenerator):<br>
+    """An abstract base class for all performance-oriented traffic generators.<br>
+<br>
+    Provides an intermediary interface for performance-based traffic generator.<br>
+    """<br>
+<br>
+    @abstractmethod<br>
+    def calculate_traffic_and_stats(<br>
+        self,<br>
+        packet: Packet,<br>
+        duration: float,<br>
+        send_mpps: int | None = None,<br>
+    ) -> PerformanceTrafficStats:<br>
+        """Send packet traffic and acquire associated statistics.<br>
+<br>
+        If `send_mpps` is provided, attempt to transmit traffic at the `send_mpps` rate.<br>
+        Otherwise, attempt to transmit at line rate.<br>
+<br>
+        Args:<br>
+            packet: The packet to send.<br>
+            duration: Performance test duration (in seconds).<br>
+            send_mpps: The millions packets per second send rate.<br>
+<br>
+        Returns:<br>
+            Performance statistics of the generated test.<br>
+        """<br>
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/framework/testbed_model/traffic_generator/traffic_generator.py<br>
index cac119c183..e5f246df7a 100644<br>
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py<br>
+++ b/dts/framework/testbed_model/traffic_generator/traffic_generator.py<br>
@@ -11,14 +11,10 @@<br>
 from abc import ABC, abstractmethod<br>
 from typing import Any<br>
<br>
-from scapy.packet import Packet<br>
-<br>
 from framework.config.test_run import TrafficGeneratorConfig<br>
 from framework.logger import DTSLogger, get_dts_logger<br>
 from framework.testbed_model.node import Node<br>
-from framework.testbed_model.port import Port<br>
 from framework.testbed_model.topology import Topology<br>
-from framework.utils import get_packet_summaries<br>
<br>
<br>
 class TrafficGenerator(ABC):<br>
@@ -57,40 +53,6 @@ def teardown(self) -> None:<br>
         """Teardown the traffic generator."""<br>
         self.close()<br>
<br>
-    def send_packet(self, packet: Packet, port: Port) -> None:<br>
-        """Send `packet` and block until it is fully sent.<br>
-<br>
-        Send `packet` on `port`, then wait until `packet` is fully sent.<br>
-<br>
-        Args:<br>
-            packet: The packet to send.<br>
-            port: The egress port on the TG node.<br>
-        """<br>
-        self.send_packets([packet], port)<br>
-<br>
-    def send_packets(self, packets: list[Packet], port: Port) -> None:<br>
-        """Send `packets` and block until they are fully sent.<br>
-<br>
-        Send `packets` on `port`, then wait until `packets` are fully sent.<br>
-<br>
-        Args:<br>
-            packets: The packets to send.<br>
-            port: The egress port on the TG node.<br>
-        """<br>
-        self._<a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(f"Sending packet{'s' if len(packets) > 1 else ''}.")<br>
-        self._logger.debug(get_packet_summaries(packets))<br>
-        self._send_packets(packets, port)<br>
-<br>
-    @abstractmethod<br>
-    def _send_packets(self, packets: list[Packet], port: Port) -> None:<br>
-        """The implementation of :method:`send_packets`.<br>
-<br>
-        The subclasses must implement this method which sends `packets` on `port`.<br>
-        The method should block until all `packets` are fully sent.<br>
-<br>
-        What fully sent means is defined by the traffic generator.<br>
-        """<br>
-<br>
     @property<br>
     def is_capturing(self) -> bool:<br>
         """This traffic generator can't capture traffic."""<br>
-- <br>
2.49.0<br>
<br>
</blockquote></div>