Rework TG class hierarchy to include performance traffic generators, in addition to capturing traffic generators. As such, methods garnered to capturing traffic have been moved to the CapturingTrafficGenerator subclass.
Bugzilla ID: 1697 Signed-off-by: Nicholas Pratte <npra...@iol.unh.edu> --- .../capturing_traffic_generator.py | 34 ++++++++++ .../performance_traffic_generator.py | 62 +++++++++++++++++++ .../traffic_generator/traffic_generator.py | 43 +------------ 3 files changed, 97 insertions(+), 42 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 e31ba2a9b7..41b70f7f48 100644 --- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py +++ b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py @@ -63,6 +63,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..7a384cf6e0 --- /dev/null +++ b/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py @@ -0,0 +1,62 @@ +"""Performance testing capable traffic generatiors.""" + +from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Callable + +from scapy.packet import Packet + +from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator + + +@dataclass(slots=True) +class PerformanceTrafficStats(ABC): + """Data structure for stats offered by a given traffic generator.""" + + frame_size: int + + +class PerformanceTrafficGenerator(TrafficGenerator): + """An Abstract Base Class for all performance-oriented traffic generators. + + Provides an intermediary interface for performance-based traffic generator. + """ + + _test_stats: list[PerformanceTrafficStats] + + @property + def is_capturing(self) -> bool: + """Used for synchronization.""" + return False + + @property + def last_results(self) -> PerformanceTrafficStats | None: + """Get the latest set of results from TG instance. + + Returns: + The most recent set of traffic statistics. + """ + return self._test_stats.pop(0) + + def generate_traffic_and_stats( + self, + packet: Packet, + duration: float, # Default of 60 (in seconds). + ) -> PerformanceTrafficStats: + """Send packet traffic and acquire associated statistics.""" + return self._calculate_traffic_stats(packet, duration, self._generate_traffic) + + def setup(self, ports): + """Preliminary port setup prior to TG execution.""" + for port in self._tg_node.ports: + self._tg_node.main_session.configure_port_mtu(2000, port) + + @abstractmethod + def _calculate_traffic_stats( + self, packet: Packet, duration: float, traffic_gen_callback: Callable[[Packet, float], str] + ) -> PerformanceTrafficStats: + """Calculate packet traffic stats based on TG output.""" + + @abstractmethod + def _generate_traffic(self, packet: Packet, duration: float) -> str: + """Implementation for :method:`generate_traffic_and_stats`.""" diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/framework/testbed_model/traffic_generator/traffic_generator.py index 804662e114..12b9568d1a 100644 --- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py +++ b/dts/framework/testbed_model/traffic_generator/traffic_generator.py @@ -11,13 +11,11 @@ from abc import ABC, abstractmethod from typing import Iterable -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.utils import MultiInheritanceBaseClass, get_packet_summaries +from framework.utils import MultiInheritanceBaseClass class TrafficGenerator(MultiInheritanceBaseClass, ABC): @@ -56,45 +54,6 @@ def setup(self, ports: Iterable[Port]): def teardown(self, ports: Iterable[Port]): """Teardown the traffic generator.""" - 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.""" - return False - @abstractmethod def close(self) -> None: """Free all resources used by the traffic generator.""" -- 2.47.1