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