Separate files needing GPL license
[csit.git] / resources / libraries / python / TrafficGenerator.py
index 1b519d5..c63dc2d 100644 (file)
@@ -75,7 +75,7 @@ class TGDropRateSearchImpl(DropRateSearch):
         :param loss_acceptance: Permitted drop ratio or frames count.
         :param loss_acceptance_type: Type of permitted loss.
         :param traffic_profile: Module name as a traffic profile identifier.
-            See resources/traffic_profiles/trex for implemented modules.
+            See GPL/traffic_profiles/trex for implemented modules.
         :param skip_warmup: Start TRex without warmup traffic if true.
         :type rate: float
         :type frame_size: str
@@ -151,6 +151,8 @@ class TrafficGenerator(AbstractMeasurer):
         self._sent = None
         self._latency = None
         self._received = None
+        self._approximated_rate = None
+        self._approximated_duration = None
         # Measurement input fields, needed for async stop result.
         self._start_time = None
         self._rate = None
@@ -204,6 +206,15 @@ class TrafficGenerator(AbstractMeasurer):
         """
         return self._latency
 
+    def get_approximated_rate(self):
+        """Return approximated rate computed as ratio of transmited packets over
+        duration of trial.
+
+        :returns: Approximated rate.
+        :rtype: str
+        """
+        return self._approximated_rate
+
     # TODO: pylint says disable=too-many-locals.
     # A fix is developed in https://gerrit.fd.io/r/c/csit/+/22221
     def initialize_traffic_generator(
@@ -335,71 +346,82 @@ class TrafficGenerator(AbstractMeasurer):
             else:
                 raise ValueError(u"Unknown Test Type!")
 
-            self._startup_trex(osi_layer)
+            TrafficGenerator.startup_trex(
+                self._node, osi_layer, subtype=subtype
+            )
 
-    def _startup_trex(self, osi_layer):
+    @staticmethod
+    def startup_trex(tg_node, osi_layer, subtype=None):
         """Startup sequence for the TRex traffic generator.
 
+        :param tg_node: Traffic generator node.
         :param osi_layer: 'L2', 'L3' or 'L7' - OSI Layer testing type.
+        :param subtype: Traffic generator sub-type.
+        :type tg_node: dict
         :type osi_layer: str
+        :type subtype: NodeSubTypeTG
         :raises RuntimeError: If node subtype is not a TREX or startup failed.
         """
-        # No need to check subtype, we know it is TREX.
-        for _ in range(0, 3):
-            # Kill TRex only if it is already running.
-            cmd = u"sh -c \"pgrep t-rex && pkill t-rex && sleep 3 || true\""
-            exec_cmd_no_error(
-                self._node, cmd, sudo=True, message=u"Kill TRex failed!"
-            )
-
-            # Configure TRex.
-            ports = ''
-            for port in self._node[u"interfaces"].values():
-                ports += f" {port.get(u'pci_address')}"
-
-            cmd = f"sh -c \"cd {Constants.TREX_INSTALL_DIR}/scripts/ && " \
-                f"./dpdk_nic_bind.py -u {ports} || true\""
-            exec_cmd_no_error(
-                self._node, cmd, sudo=True,
-                message=u"Unbind PCI ports from driver failed!"
-            )
-
-            # Start TRex.
-            cd_cmd = f"cd '{Constants.TREX_INSTALL_DIR}/scripts/'"
-            trex_cmd = OptionString([u"nohup", u"./t-rex-64"])
-            trex_cmd.add(u"-i")
-            trex_cmd.add(u"--prefix $(hostname)")
-            trex_cmd.add(u"--hdrh")
-            trex_cmd.add(u"--no-scapy-server")
-            trex_cmd.add_if(u"--astf", osi_layer == u"L7")
-            # OptionString does not create double space if extra is empty.
-            trex_cmd.add(f"{Constants.TREX_EXTRA_CMDLINE}")
-            inner_command = f"{cd_cmd} && {trex_cmd} > /tmp/trex.log 2>&1 &"
-            cmd = f"sh -c \"{inner_command}\" > /dev/null"
-            try:
-                exec_cmd_no_error(self._node, cmd, sudo=True)
-            except RuntimeError:
-                cmd = u"sh -c \"cat /tmp/trex.log\""
+        if not subtype:
+            subtype = check_subtype(tg_node)
+        if subtype == NodeSubTypeTG.TREX:
+            for _ in range(0, 3):
+                # Kill TRex only if it is already running.
+                cmd = u"sh -c \"pgrep t-rex && pkill t-rex && sleep 3 || true\""
                 exec_cmd_no_error(
-                    self._node, cmd, sudo=True, message=u"Get TRex logs failed!"
+                    tg_node, cmd, sudo=True, message=u"Kill TRex failed!"
                 )
-                raise RuntimeError(u"Start TRex failed!")
 
-            # Test if TRex starts successfuly.
-            cmd = f"sh -c \"{Constants.REMOTE_FW_DIR}/resources/tools/trex/" \
-                f"trex_server_info.py\""
-            try:
+                # Configure TRex.
+                ports = ''
+                for port in tg_node[u"interfaces"].values():
+                    ports += f" {port.get(u'pci_address')}"
+
+                cmd = f"sh -c \"cd {Constants.TREX_INSTALL_DIR}/scripts/ && " \
+                    f"./dpdk_nic_bind.py -u {ports} || true\""
                 exec_cmd_no_error(
-                    self._node, cmd, sudo=True, message=u"Test TRex failed!",
-                    retries=20
+                    tg_node, cmd, sudo=True,
+                    message=u"Unbind PCI ports from driver failed!"
                 )
-            except RuntimeError:
-                continue
-            return
-        # After max retries TRex is still not responding to API critical error
-        # occurred.
-        exec_cmd(self._node, u"cat /tmp/trex.log", sudo=True)
-        raise RuntimeError(u"Start TRex failed after multiple retries!")
+
+                # Start TRex.
+                cd_cmd = f"cd '{Constants.TREX_INSTALL_DIR}/scripts/'"
+                trex_cmd = OptionString([u"nohup", u"./t-rex-64"])
+                trex_cmd.add(u"-i")
+                trex_cmd.add(u"--prefix $(hostname)")
+                trex_cmd.add(u"--hdrh")
+                trex_cmd.add(u"--no-scapy-server")
+                trex_cmd.add_if(u"--astf", osi_layer == u"L7")
+                # OptionString does not create double space if extra is empty.
+                trex_cmd.add(f"{Constants.TREX_EXTRA_CMDLINE}")
+                inner_command = f"{cd_cmd} && {trex_cmd} > /tmp/trex.log 2>&1 &"
+                cmd = f"sh -c \"{inner_command}\" > /dev/null"
+                try:
+                    exec_cmd_no_error(tg_node, cmd, sudo=True)
+                except RuntimeError:
+                    cmd = u"sh -c \"cat /tmp/trex.log\""
+                    exec_cmd_no_error(
+                        tg_node, cmd, sudo=True,
+                        message=u"Get TRex logs failed!"
+                    )
+                    raise RuntimeError(u"Start TRex failed!")
+
+                # Test if TRex starts successfuly.
+                command_line = OptionString().add(u"python3")
+                dirname = f"{Constants.REMOTE_FW_DIR}/GPL/tools/trex"
+                command_line.add(f"'{dirname}/trex_server_info.py'")
+                try:
+                    exec_cmd_no_error(
+                        tg_node, command_line, sudo=True,
+                        message=u"Test TRex failed!", retries=20
+                    )
+                except RuntimeError:
+                    continue
+                return
+            # After max retries TRex is still not responding to API critical
+            # error occurred.
+            exec_cmd(tg_node, u"cat /tmp/trex.log", sudo=True)
+            raise RuntimeError(u"Start TRex failed after multiple retries!")
 
     @staticmethod
     def is_trex_running(node):
@@ -411,7 +433,7 @@ class TrafficGenerator(AbstractMeasurer):
         :rtype: bool
         :raises RuntimeError: If node type is not a TG.
         """
-        ret, _, _ = exec_cmd(node, u"pidof t-rex", sudo=True)
+        ret, _, _ = exec_cmd(node, u"pgrep t-rex", sudo=True)
         return bool(int(ret) == 0)
 
     @staticmethod
@@ -427,8 +449,11 @@ class TrafficGenerator(AbstractMeasurer):
         subtype = check_subtype(node)
         if subtype == NodeSubTypeTG.TREX:
             exec_cmd_no_error(
-                node, u"sh -c \"sudo pkill t-rex && sleep 3\"",
-                sudo=False, message=u"pkill t-rex failed"
+                node,
+                u"sh -c "
+                u"\"if pgrep t-rex; then sudo pkill t-rex && sleep 3; fi\"",
+                sudo=False,
+                message=u"pkill t-rex failed"
             )
 
     def _parse_traffic_results(self, stdout):
@@ -447,9 +472,12 @@ class TrafficGenerator(AbstractMeasurer):
         self._received = self._result.split(u", ")[1].split(u"=", 1)[1]
         self._sent = self._result.split(u", ")[2].split(u"=", 1)[1]
         self._loss = self._result.split(u", ")[3].split(u"=", 1)[1]
+        self._approximated_duration = \
+            self._result.split(u", ")[5].split(u"=", 1)[1]
+        self._approximated_rate = self._result.split(u", ")[6].split(u"=", 1)[1]
         self._latency = list()
-        self._latency.append(self._result.split(u", ")[4].split(u"=", 1)[1])
-        self._latency.append(self._result.split(u", ")[5].split(u"=", 1)[1])
+        self._latency.append(self._result.split(u", ")[7].split(u"=", 1)[1])
+        self._latency.append(self._result.split(u", ")[8].split(u"=", 1)[1])
 
     def trex_stl_stop_remote_exec(self, node):
         """Execute script on remote node over ssh to stop running traffic.
@@ -461,16 +489,16 @@ class TrafficGenerator(AbstractMeasurer):
         :raises RuntimeError: If stop traffic script fails.
         """
         # No need to check subtype, we know it is TREX.
-        x_args = u""
+        command_line = OptionString().add(u"python3")
+        dirname = f"{Constants.REMOTE_FW_DIR}/GPL/tools/trex"
+        command_line.add(f"'{dirname}/trex_stateless_stop.py'")
+        command_line.change_prefix(u"--")
         for index, value in enumerate(self._xstats):
             if value is not None:
-                # Nested quoting is fun.
                 value = value.replace(u"'", u"\"")
-                x_args += f" --xstat{index}='\"'\"'{value}'\"'\"'"
+                command_line.add_equals(f"xstat{index}", f"'{value}'")
         stdout, _ = exec_cmd_no_error(
-            node, f"sh -c '{Constants.REMOTE_FW_DIR}/resources/tools/trex/"
-            f"trex_stateless_stop.py{x_args}'",
-            message=u"TRex stateless runtime error"
+            node, command_line, message=u"TRex stateless runtime error"
         )
         self._parse_traffic_results(stdout)
 
@@ -487,7 +515,7 @@ class TrafficGenerator(AbstractMeasurer):
         :param rate: Traffic rate expressed with units (pps, %)
         :param frame_size: L2 frame size to send (without padding and IPG).
         :param traffic_profile: Module name as a traffic profile identifier.
-            See resources/traffic_profiles/trex for implemented modules.
+            See GPL/traffic_profiles/trex for implemented modules.
         :param async_call: If enabled then don't wait for all incomming trafic.
         :param latency: With latency measurement.
         :param warmup_time: Warmup time period.
@@ -512,28 +540,31 @@ class TrafficGenerator(AbstractMeasurer):
         # No need to check subtype, we know it is TREX.
         reorder = self._ifaces_reordered  # Just to make the next line fit.
         p_0, p_1 = (rx_port, tx_port) if reorder else (tx_port, rx_port)
-
         if not isinstance(duration, (float, int)):
             duration = float(duration)
         if not isinstance(warmup_time, (float, int)):
             warmup_time = float(warmup_time)
-        command = f"sh -c \"" \
-            f"{Constants.REMOTE_FW_DIR}/resources/tools/trex/" \
-            f"trex_stateless_profile.py" \
-            f" --profile {Constants.REMOTE_FW_DIR}/resources/" \
-            f"traffic_profiles/trex/{traffic_profile}.py" \
-            f" --duration {duration!r} --frame_size {frame_size} " \
-            f"--rate {rate!r} --warmup_time {warmup_time!r} " \
-            f"--port_0 {p_0} --port_1 {p_1}" \
-            f" --traffic_directions {traffic_directions}"
-        if async_call:
-            command += u" --async_start"
-        if latency:
-            command += u" --latency"
-        command += u"\""
+
+        command_line = OptionString().add(u"python3")
+        dirname = f"{Constants.REMOTE_FW_DIR}/GPL/tools/trex"
+        command_line.add(f"'{dirname}/trex_stateless_profile.py'")
+        command_line.change_prefix(u"--")
+        dirname = f"{Constants.REMOTE_FW_DIR}/GPL/traffic_profiles/trex"
+        quoted_path = f"'{dirname}/{traffic_profile}.py'"
+        command_line.add_with_value(u"profile", quoted_path)
+        command_line.add_with_value(u"duration", f"{duration!r}")
+        command_line.add_with_value(u"frame_size", frame_size)
+        command_line.add_with_value(u"rate", f"{rate!r}")
+        command_line.add_with_value(u"warmup_time", f"{warmup_time!r}")
+        command_line.add_with_value(u"port_0", p_0)
+        command_line.add_with_value(u"port_1", p_1)
+        command_line.add_with_value(u"traffic_directions", traffic_directions)
+        command_line.add_if(u"async_start", async_call)
+        command_line.add_if(u"latency", latency)
+        command_line.add_if(u"force", Constants.TREX_SEND_FORCE)
 
         stdout, _ = exec_cmd_no_error(
-            self._node, command, timeout=float(duration) + 60,
+            self._node, command_line, timeout=float(duration) + 60,
             message=u"TRex stateless runtime error"
         )
 
@@ -601,7 +632,7 @@ class TrafficGenerator(AbstractMeasurer):
         :param rate: Offered load per interface (e.g. 1%, 3gbps, 4mpps, ...).
         :param frame_size: Frame size (L2) in Bytes.
         :param traffic_profile: Module name as a traffic profile identifier.
-            See resources/traffic_profiles/trex for implemented modules.
+            See GPL/traffic_profiles/trex for implemented modules.
         :param warmup_time: Warmup phase in seconds.
         :param async_call: Async mode.
         :param latency: With latency measurement.
@@ -691,7 +722,7 @@ class TrafficGenerator(AbstractMeasurer):
 
         :param frame_size: Frame size identifier or value [B].
         :param traffic_profile: Module name as a traffic profile identifier.
-            See resources/traffic_profiles/trex for implemented modules.
+            See GPL/traffic_profiles/trex for implemented modules.
         :param warmup_time: Traffic duration before measurement starts [s].
         :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
             Default: 2
@@ -733,6 +764,7 @@ class TrafficGenerator(AbstractMeasurer):
             duration, transmit_rate, transmit_count, loss_count
         )
         measurement.latency = self.get_latency_int()
+        measurement.approximated_rate = self.get_approximated_rate()
         return measurement
 
     def measure(self, duration, transmit_rate):
@@ -781,7 +813,7 @@ class OptimizedSearch:
 
         :param frame_size: Frame size identifier or value [B].
         :param traffic_profile: Module name as a traffic profile identifier.
-            See resources/traffic_profiles/trex for implemented modules.
+            See GPL/traffic_profiles/trex for implemented modules.
         :param minimum_transmit_rate: Minimal uni-directional
             target transmit rate [pps].
         :param maximum_transmit_rate: Maximal uni-directional
@@ -849,7 +881,7 @@ class OptimizedSearch:
 
         :param frame_size: Frame size identifier or value [B].
         :param traffic_profile: Module name as a traffic profile identifier.
-            See resources/traffic_profiles/trex for implemented modules.
+            See GPL/traffic_profiles/trex for implemented modules.
         :param minimum_transmit_rate: Minimal uni-directional
             target transmit rate [pps].
         :param maximum_transmit_rate: Maximal uni-directional