Merge "hugepages: change default num pages + deallocate"
[vswitchperf.git] / tools / pkt_gen / xena / xena.py
index 1716116..449ef5b 100755 (executable)
@@ -25,6 +25,7 @@ Xena Traffic Generator Model
 # python imports
 import binascii
 import logging
+import os
 import subprocess
 import sys
 from time import sleep
@@ -50,6 +51,7 @@ from tools.pkt_gen.xena.XenaDriver import (
     XenaManager,
     )
 
+
 class Xena(ITrafficGenerator):
     """
     Xena Traffic generator wrapper class
@@ -65,6 +67,19 @@ class Xena(ITrafficGenerator):
         self._duration = None
         self.tx_stats = None
         self.rx_stats = None
+        self._log_handle = None
+
+        user_home = os.path.expanduser('~')
+        self._log_path = '{}/Xena/Xena2544-2G/Logs/xena2544.log'.format(
+                user_home)
+
+        # make the folder and log file if they doesn't exist
+        if not os.path.exists(self._log_path):
+            os.makedirs(os.path.dirname(self._log_path))
+
+        # empty the file contents
+        open(self._log_path, 'w').close()
+
 
     @property
     def traffic_defaults(self):
@@ -92,14 +107,14 @@ class Xena(ITrafficGenerator):
 
         if test_type == 'Throughput':
             results = OrderedDict()
-            results[ResultsConstants.THROUGHPUT_RX_FPS] = int(
-                root[0][1][0][0].get('PortRxPps')) + int(
+            results[ResultsConstants.THROUGHPUT_RX_FPS] = float(
+                root[0][1][0][0].get('PortRxPps')) + float(
                     root[0][1][0][1].get('PortRxPps'))
-            results[ResultsConstants.THROUGHPUT_RX_MBPS] = (int(
-                root[0][1][0][0].get('PortRxBpsL1')) + int(
+            results[ResultsConstants.THROUGHPUT_RX_MBPS] = (float(
+                root[0][1][0][0].get('PortRxBpsL1')) + float(
                     root[0][1][0][1].get('PortRxBpsL1')))/ 1000000
             results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
-                100 - int(root[0][1][0].get('TotalLossRatioPcnt'))) * float(
+                100 - float(root[0][1][0].get('TotalLossRatioPcnt'))) * float(
                     root[0][1][0].get('TotalTxRatePcnt'))/100
             results[ResultsConstants.TX_RATE_FPS] = root[0][1][0].get(
                 'TotalTxRateFps')
@@ -260,10 +275,10 @@ class Xena(ITrafficGenerator):
 
         return result_dict
 
-    def _setup_json_config(self, trials, loss_rate, testtype=None):
+    def _setup_json_config(self, tests, loss_rate, testtype=None):
         """
         Create a 2bUsed json file that will be used for xena2544.exe execution.
-        :param trials: Number of trials
+        :param tests: Number of tests
         :param loss_rate: The acceptable loss rate as float
         :param testtype: Either '2544_b2b' or '2544_throughput' as string
         :return: None
@@ -290,7 +305,7 @@ class Xena(ITrafficGenerator):
             if testtype == '2544_throughput':
                 j_file.set_test_options_tput(
                     packet_sizes=self._params['traffic']['l2']['framesize'],
-                    iterations=trials, loss_rate=loss_rate,
+                    iterations=tests, loss_rate=loss_rate,
                     duration=self._duration, micro_tpld=True if self._params[
                         'traffic']['l2']['framesize'] == 64 else False)
                 j_file.enable_throughput_test()
@@ -298,7 +313,7 @@ class Xena(ITrafficGenerator):
             elif testtype == '2544_b2b':
                 j_file.set_test_options_back2back(
                     packet_sizes=self._params['traffic']['l2']['framesize'],
-                    iterations=trials, duration=self._duration,
+                    iterations=tests, duration=self._duration,
                     startvalue=self._params['traffic']['frame_rate'],
                     endvalue=self._params['traffic']['frame_rate'],
                     micro_tpld=True if self._params[
@@ -451,6 +466,44 @@ class Xena(ITrafficGenerator):
             self.rx_stats = self.xmanager.ports[1].get_rx_stats()
         sleep(1)
 
+    def _start_xena_2544(self):
+        """
+        Start the xena2544 exe.
+        :return: None
+        """
+        args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
+                "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
+                "./tools/pkt_gen/xena", "-u",
+                settings.getValue('TRAFFICGEN_XENA_USER')]
+        # Sometimes Xena2544.exe completes, but mono holds the process without
+        # releasing it, this can cause a deadlock of the main thread. Use the
+        # xena log file as a way to detect this.
+        self._log_handle = open(self._log_path, 'r')
+        # read the contents of the log before we start so the next read in the
+        # wait method are only looking at the text from this test instance
+        self._log_handle.read()
+        self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout)
+
+    def _wait_xena_2544_complete(self):
+        """
+        Wait for Xena2544.exe completion.
+        :return: None
+        """
+        data = ''
+        while True:
+            try:
+                self.mono_pipe.wait(60)
+                self._log_handle.close()
+                break
+            except subprocess.TimeoutExpired:
+                # check the log to see if Xena2544 has completed and mono is
+                # deadlocked.
+                data += self._log_handle.read()
+                if 'TestCompletedSuccessfully' in data:
+                    self._log_handle.close()
+                    self.mono_pipe.terminate()
+                    break
+
     def _stop_api_traffic(self):
         """
         Stop traffic through the socket API
@@ -492,22 +545,16 @@ class Xena(ITrafficGenerator):
     def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
         """Send a burst of traffic.
 
-        Send a ``numpkts`` packets of traffic, using ``traffic``
-        configuration, with a timeout of ``time``.
-
-        Attributes:
-        :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
-        :param numpkts: Number of packets to send
-        :param duration: Time to wait to receive packets
-
-        :returns: dictionary of strings with following data:
-            - List of Tx Frames,
-            - List of Rx Frames,
-            - List of Tx Bytes,
-            - List of List of Rx Bytes,
-            - Payload Errors and Sequence Errors.
+        See ITrafficGenerator for description
         """
-        raise NotImplementedError('Xena burst traffic not implemented')
+        self._duration = duration
+        self._params.clear()
+        self._params['traffic'] = self.traffic_defaults.copy()
+        if traffic:
+            self._params['traffic'] = merge_spec(self._params['traffic'],
+                                                 traffic)
+        self._start_traffic_api(numpkts)
+        return self._stop_api_traffic()
 
     def send_cont_traffic(self, traffic=None, duration=20):
         """Send a continuous flow of traffic.
@@ -521,7 +568,6 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
-
         self._start_traffic_api(-1)
         return self._stop_api_traffic()
 
@@ -537,7 +583,6 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
-
         self._start_traffic_api(-1)
 
     def stop_cont_traffic(self):
@@ -545,7 +590,7 @@ class Xena(ITrafficGenerator):
         """
         return self._stop_api_traffic()
 
-    def send_rfc2544_throughput(self, traffic=None, trials=3, duration=20,
+    def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
                                 lossrate=0.0):
         """Send traffic per RFC2544 throughput test specifications.
 
@@ -558,19 +603,14 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
+        self._setup_json_config(tests, lossrate, '2544_throughput')
+        self._start_xena_2544()
+        self._wait_xena_2544_complete()
 
-        self._setup_json_config(trials, lossrate, '2544_throughput')
-
-        args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
-                "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
-                "./tools/pkt_gen/xena", "-u",
-                settings.getValue('TRAFFICGEN_XENA_USER')]
-        self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout)
-        self.mono_pipe.communicate()
         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
         return Xena._create_throughput_result(root)
 
-    def start_rfc2544_throughput(self, traffic=None, trials=3, duration=20,
+    def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
                                  lossrate=0.0):
         """Non-blocking version of 'send_rfc2544_throughput'.
 
@@ -582,26 +622,19 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
-
-        self._setup_json_config(trials, lossrate, '2544_throughput')
-
-        args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
-                "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
-                "./tools/pkt_gen/xena", "-u",
-                settings.getValue('TRAFFICGEN_XENA_USER')]
-        self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout)
+        self._setup_json_config(tests, lossrate, '2544_throughput')
+        self._start_xena_2544()
 
     def wait_rfc2544_throughput(self):
         """Wait for and return results of RFC2544 test.
 
         See ITrafficGenerator for description
         """
-        self.mono_pipe.communicate()
-        sleep(2)
+        self._wait_xena_2544_complete()
         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
         return Xena._create_throughput_result(root)
 
-    def send_rfc2544_back2back(self, traffic=None, trials=1, duration=20,
+    def send_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
                                lossrate=0.0):
         """Send traffic per RFC2544 back2back test specifications.
 
@@ -614,47 +647,31 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
-
-        self._setup_json_config(trials, lossrate, '2544_b2b')
-
-        args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
-                "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
-                "./tools/pkt_gen/xena", "-u",
-                settings.getValue('TRAFFICGEN_XENA_USER')]
-        self.mono_pipe = subprocess.Popen(
-            args, stdout=sys.stdout)
-        self.mono_pipe.communicate()
+        self._setup_json_config(tests, lossrate, '2544_b2b')
+        self._start_xena_2544()
+        self._wait_xena_2544_complete()
         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
         return Xena._create_throughput_result(root)
 
-    def start_rfc2544_back2back(self, traffic=None, trials=1, duration=20,
+    def start_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
                                 lossrate=0.0):
         """Non-blocking version of 'send_rfc2544_back2back'.
 
         See ITrafficGenerator for description
         """
         self._duration = duration
-
         self._params.clear()
         self._params['traffic'] = self.traffic_defaults.copy()
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
-
-        self._setup_json_config(trials, lossrate, '2544_b2b')
-
-        args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
-                "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
-                "./tools/pkt_gen/xena", "-u",
-                settings.getValue('TRAFFICGEN_XENA_USER')]
-        self.mono_pipe = subprocess.Popen(
-            args, stdout=sys.stdout)
+        self._setup_json_config(tests, lossrate, '2544_b2b')
+        self._start_xena_2544()
 
     def wait_rfc2544_back2back(self):
         """Wait and set results of RFC2544 test.
         """
-        self.mono_pipe.communicate()
-        sleep(2)
+        self._wait_xena_2544_complete()
         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
         return Xena._create_throughput_result(root)