<div dir="ltr"><div dir="ltr"><div class="gmail_default" style="font-family:arial,sans-serif"><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Nov 23, 2023 at 10:14 AM Juraj Linkeš <juraj.linkes@pantheon.tech> 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">Format according to the Google format and PEP257, with slight<br>
deviations.<br>
<br>
Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech><br>
---<br>
 .../traffic_generator/__init__.py             | 22 ++++++++-<br>
 .../capturing_traffic_generator.py            | 45 +++++++++++--------<br>
 .../traffic_generator/traffic_generator.py    | 33 ++++++++------<br>
 3 files changed, 67 insertions(+), 33 deletions(-)<br>
<br>
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/framework/testbed_model/traffic_generator/__init__.py<br>
index 52888d03fa..11e2bd7d97 100644<br>
--- a/dts/framework/testbed_model/traffic_generator/__init__.py<br>
+++ b/dts/framework/testbed_model/traffic_generator/__init__.py<br>
@@ -1,6 +1,19 @@<br>
 # SPDX-License-Identifier: BSD-3-Clause<br>
 # Copyright(c) 2023 PANTHEON.tech s.r.o.<br>
<br>
+"""DTS traffic generators.<br>
+<br>
+A traffic generator is capable of generating traffic and then monitor returning traffic.<br>
+All traffic generators must count the number of received packets. Some may additionally capture<br>
+individual packets.<br>
+<br>
+A traffic generator may be software running on generic hardware or it could be specialized hardware.<br>
+<br>
+The traffic generators that only count the number of received packets are suitable only for<br>
+performance testing. In functional testing, we need to be able to dissect each arrived packet<br>
+and a capturing traffic generator is required.<br>
+"""<br>
+<br>
 from framework.config import ScapyTrafficGeneratorConfig, TrafficGeneratorType<br>
 from framework.exception import ConfigurationError<br>
 from framework.testbed_model.node import Node<br>
@@ -12,8 +25,15 @@<br>
 def create_traffic_generator(<br>
     tg_node: Node, traffic_generator_config: ScapyTrafficGeneratorConfig<br>
 ) -> CapturingTrafficGenerator:<br>
-    """A factory function for creating traffic generator object from user config."""<br>
+    """The factory function for creating traffic generator objects from the test run configuration.<br>
+<br>
+    Args:<br>
+        tg_node: The traffic generator node where the created traffic generator will be running.<br>
+        traffic_generator_config: The traffic generator config.<br>
<br>
+    Returns:<br>
+        A traffic generator capable of capturing received packets.<br>
+    """<br>
     match traffic_generator_config.traffic_generator_type:<br>
         case TrafficGeneratorType.SCAPY:<br>
             return ScapyTrafficGenerator(tg_node, traffic_generator_config)<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 1fc7f98c05..0246590333 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>
@@ -23,19 +23,21 @@<br>
<br>
<br>
 def _get_default_capture_name() -> str:<br>
-    """<br>
-    This is the function used for the default implementation of capture names.<br>
-    """<br>
     return str(uuid.uuid4())<br>
<br>
<br>
 class CapturingTrafficGenerator(TrafficGenerator):<br>
     """Capture packets after sending traffic.<br>
<br>
-    A mixin interface which enables a packet generator to declare that it can capture<br>
+    The intermediary interface which enables a packet generator to declare that it can capture<br>
     packets and return them to the user.<br>
<br>
+    Similarly to :class:`~.traffic_generator.TrafficGenerator`, this class exposes<br>
+    the public methods specific to capturing traffic generators and defines a private method<br>
+    that must implement the traffic generation and capturing logic in subclasses.<br>
+<br>
     The methods of capturing traffic generators obey the following workflow:<br>
+<br>
         1. send packets<br>
         2. capture packets<br>
         3. write the capture to a .pcap file<br>
@@ -44,6 +46,7 @@ class CapturingTrafficGenerator(TrafficGenerator):<br>
<br>
     @property<br>
     def is_capturing(self) -> bool:<br>
+        """This traffic generator can capture traffic."""<br>
         return True<br>
<br>
     def send_packet_and_capture(<br>
@@ -54,11 +57,12 @@ def send_packet_and_capture(<br>
         duration: float,<br>
         capture_name: str = _get_default_capture_name(),<br>
     ) -> list[Packet]:<br>
-        """Send a packet, return received traffic.<br>
+        """Send `packet` and capture received traffic.<br>
+<br>
+        Send `packet` on `send_port` and then return all traffic captured<br>
+        on `receive_port` for the given `duration`.<br>
<br>
-        Send a packet on the send_port and then return all traffic captured<br>
-        on the receive_port for the given duration. Also record the captured traffic<br>
-        in a pcap file.<br>
+        The captured traffic is recorded in the `capture_name`.pcap file.<br>
<br>
         Args:<br>
             packet: The packet to send.<br>
@@ -68,7 +72,7 @@ def send_packet_and_capture(<br>
             capture_name: The name of the .pcap file where to store the capture.<br>
<br>
         Returns:<br>
-             A list of received packets. May be empty if no packets are captured.<br>
+             The received packets. May be empty if no packets are captured.<br>
         """<br>
         return self.send_packets_and_capture(<br>
             [packet], send_port, receive_port, duration, capture_name<br>
@@ -82,11 +86,14 @@ def send_packets_and_capture(<br>
         duration: float,<br>
         capture_name: str = _get_default_capture_name(),<br>
     ) -> list[Packet]:<br>
-        """Send packets, return received traffic.<br>
+        """Send `packets` and capture received traffic.<br>
<br>
-        Send packets on the send_port and then return all traffic captured<br>
-        on the receive_port for the given duration. Also record the captured traffic<br>
-        in a pcap file.<br>
+        Send `packets` on `send_port` and then return all traffic captured<br>
+        on `receive_port` for the given `duration`.<br>
+<br>
+        The captured traffic is recorded in the `capture_name`.pcap file. The target directory<br>
+        can be configured with the :option:`--output-dir` command line argument or<br>
+        the :envvar:`DTS_OUTPUT_DIR` environment variable.<br>
<br>
         Args:<br>
             packets: The packets to send.<br>
@@ -96,7 +103,7 @@ def send_packets_and_capture(<br>
             capture_name: The name of the .pcap file where to store the capture.<br>
<br>
         Returns:<br>
-             A list of received packets. May be empty if no packets are captured.<br>
+             The received packets. May be empty if no packets are captured.<br>
         """<br>
         self._logger.debug(get_packet_summaries(packets))<br>
         self._logger.debug(<br>
@@ -121,10 +128,12 @@ def _send_packets_and_capture(<br>
         receive_port: Port,<br>
         duration: float,<br>
     ) -> list[Packet]:<br>
-        """<br>
-        The extended classes must implement this method which<br>
-        sends packets on send_port and receives packets on the receive_port<br>
-        for the specified duration. It must be able to handle no received packets.<br>
+        """The implementation of :method:`send_packets_and_capture`.<br>
+<br>
+        The subclasses must implement this method which sends `packets` on `send_port`<br>
+        and receives packets on `receive_port` for the specified `duration`.<br>
+<br>
+        It must be able to handle receiving no packets.<br>
         """<br>
<br>
     def _write_capture_from_packets(self, capture_name: str, packets: list[Packet]) -> None:<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 0d9902ddb7..5fb9824568 100644<br>
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py<br>
+++ b/dts/framework/testbed_model/traffic_generator/traffic_generator.py<br>
@@ -22,7 +22,8 @@<br>
 class TrafficGenerator(ABC):<br>
     """The base traffic generator.<br>
<br>
-    Defines the few basic methods that each traffic generator must implement.<br>
+    Exposes the common public methods of all traffic generators and defines private methods<br>
+    that must implement the traffic generation logic in subclasses.<br>
     """<br>
<br>
     _config: TrafficGeneratorConfig<br>
@@ -30,14 +31,20 @@ class TrafficGenerator(ABC):<br>
     _logger: DTSLOG<br>
<br>
     def __init__(self, tg_node: Node, config: TrafficGeneratorConfig):<br>
+        """Initialize the traffic generator.<br>
+<br>
+        Args:<br>
+            tg_node: The traffic generator node where the created traffic generator will be running.<br>
+            config: The traffic generator's test run configuration.<br>
+        """<br>
         self._config = config<br>
         self._tg_node = tg_node<br>
         self._logger = getLogger(f"{self._<a href="http://tg_node.name" rel="noreferrer" target="_blank">tg_node.name</a>} {self._config.traffic_generator_type}")<br>
<br>
     def send_packet(self, packet: Packet, port: Port) -> None:<br>
-        """Send a packet and block until it is fully sent.<br>
+        """Send `packet` and block until it is fully sent.<br>
<br>
-        What fully sent means is defined by the traffic generator.<br>
+        Send `packet` on `port`, then wait until `packet` is fully sent.<br>
<br>
         Args:<br>
             packet: The packet to send.<br>
@@ -46,9 +53,9 @@ def send_packet(self, packet: Packet, port: Port) -> None:<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>
+        """Send `packets` and block until they are fully sent.<br>
<br>
-        What fully sent means is defined by the traffic generator.<br>
+        Send `packets` on `port`, then wait until `packets` are fully sent.<br>
<br>
         Args:<br>
             packets: The packets to send.<br>
@@ -60,19 +67,17 @@ def send_packets(self, packets: list[Packet], port: Port) -> None:<br>
<br>
     @abstractmethod<br>
     def _send_packets(self, packets: list[Packet], port: Port) -> None:<br>
-        """<br>
-        The extended classes must implement this method which<br>
-        sends packets on send_port. The method should block until all packets<br>
-        are fully sent.<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 full sent means is defined by the traffic generator.<br>
         """<br></blockquote><div><br></div><div><div style="font-family:arial,sans-serif" class="gmail_default">I think this should be "what fully sent means"<br></div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
     @property<br>
     def is_capturing(self) -> bool:<br>
-        """Whether this traffic generator can capture traffic.<br>
-<br>
-        Returns:<br>
-            True if the traffic generator can capture traffic, False otherwise.<br>
-        """<br>
+        """This traffic generator can't capture traffic."""<br>
         return False<br>
<br>
     @abstractmethod<br>
-- <br>
2.34.1<br>
<br>
</blockquote></div></div>