Merge "hugepages: change default num pages + deallocate"
[vswitchperf.git] / tools / pkt_gen / xena / xena.py
index 99da458..449ef5b 100755 (executable)
@@ -25,11 +25,14 @@ Xena Traffic Generator Model
 # python imports
 import binascii
 import logging
 # python imports
 import binascii
 import logging
+import os
 import subprocess
 import sys
 from time import sleep
 import xml.etree.ElementTree as ET
 from collections import OrderedDict
 import subprocess
 import sys
 from time import sleep
 import xml.etree.ElementTree as ET
 from collections import OrderedDict
+# scapy imports
+import scapy.layers.inet as inet
 
 # VSPerf imports
 from conf import settings
 
 # VSPerf imports
 from conf import settings
@@ -48,9 +51,6 @@ from tools.pkt_gen.xena.XenaDriver import (
     XenaManager,
     )
 
     XenaManager,
     )
 
-# scapy imports
-import scapy.layers.inet as inet
-
 
 class Xena(ITrafficGenerator):
     """
 
 class Xena(ITrafficGenerator):
     """
@@ -67,6 +67,19 @@ class Xena(ITrafficGenerator):
         self._duration = None
         self.tx_stats = None
         self.rx_stats = None
         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):
 
     @property
     def traffic_defaults(self):
@@ -94,12 +107,14 @@ class Xena(ITrafficGenerator):
 
         if test_type == 'Throughput':
             results = OrderedDict()
 
         if test_type == 'Throughput':
             results = OrderedDict()
-            results[ResultsConstants.THROUGHPUT_RX_FPS] = int(
-                root[0][1][0][1].get('PortRxPps'))
-            results[ResultsConstants.THROUGHPUT_RX_MBPS] = int(
-                root[0][1][0][1].get('PortRxBpsL1')) / 1000000
+            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] = (float(
+                root[0][1][0][0].get('PortRxBpsL1')) + float(
+                    root[0][1][0][1].get('PortRxBpsL1')))/ 1000000
             results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
             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')
                     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
 
 
         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.
         """
         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
         :param loss_rate: The acceptable loss rate as float
         :param testtype: Either '2544_b2b' or '2544_throughput' as string
         :return: None
@@ -286,14 +301,23 @@ class Xena(ITrafficGenerator):
                 1, settings.getValue("TRAFFICGEN_XENA_PORT1_IP"),
                 settings.getValue("TRAFFICGEN_XENA_PORT1_CIDR"),
                 settings.getValue("TRAFFICGEN_XENA_PORT1_GATEWAY"))
                 1, settings.getValue("TRAFFICGEN_XENA_PORT1_IP"),
                 settings.getValue("TRAFFICGEN_XENA_PORT1_CIDR"),
                 settings.getValue("TRAFFICGEN_XENA_PORT1_GATEWAY"))
-            j_file.set_test_options(
-                packet_sizes=self._params['traffic']['l2']['framesize'],
-                iterations=trials, loss_rate=loss_rate,
-                duration=self._duration, micro_tpld=True if self._params[
-                    'traffic']['l2']['framesize'] == 64 else False)
+
             if testtype == '2544_throughput':
             if testtype == '2544_throughput':
+                j_file.set_test_options_tput(
+                    packet_sizes=self._params['traffic']['l2']['framesize'],
+                    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()
                 j_file.enable_throughput_test()
+
             elif testtype == '2544_b2b':
             elif testtype == '2544_b2b':
+                j_file.set_test_options_back2back(
+                    packet_sizes=self._params['traffic']['l2']['framesize'],
+                    iterations=tests, duration=self._duration,
+                    startvalue=self._params['traffic']['frame_rate'],
+                    endvalue=self._params['traffic']['frame_rate'],
+                    micro_tpld=True if self._params[
+                        'traffic']['l2']['framesize'] == 64 else False)
                 j_file.enable_back2back_test()
 
             j_file.set_header_layer2(
                 j_file.enable_back2back_test()
 
             j_file.set_header_layer2(
@@ -442,6 +466,44 @@ class Xena(ITrafficGenerator):
             self.rx_stats = self.xmanager.ports[1].get_rx_stats()
         sleep(1)
 
             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
     def _stop_api_traffic(self):
         """
         Stop traffic through the socket API
@@ -483,22 +545,16 @@ class Xena(ITrafficGenerator):
     def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
         """Send a burst of traffic.
 
     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.
 
     def send_cont_traffic(self, traffic=None, duration=20):
         """Send a continuous flow of traffic.
@@ -512,7 +568,6 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
-
         self._start_traffic_api(-1)
         return self._stop_api_traffic()
 
         self._start_traffic_api(-1)
         return self._stop_api_traffic()
 
@@ -528,7 +583,6 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
-
         self._start_traffic_api(-1)
 
     def stop_cont_traffic(self):
         self._start_traffic_api(-1)
 
     def stop_cont_traffic(self):
@@ -536,7 +590,7 @@ class Xena(ITrafficGenerator):
         """
         return self._stop_api_traffic()
 
         """
         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.
 
                                 lossrate=0.0):
         """Send traffic per RFC2544 throughput test specifications.
 
@@ -549,19 +603,14 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
         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)
 
         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'.
 
                                  lossrate=0.0):
         """Non-blocking version of 'send_rfc2544_throughput'.
 
@@ -573,26 +622,19 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
         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
         """
 
     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)
 
         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.
 
                                lossrate=0.0):
         """Send traffic per RFC2544 back2back test specifications.
 
@@ -605,47 +647,31 @@ class Xena(ITrafficGenerator):
         if traffic:
             self._params['traffic'] = merge_spec(self._params['traffic'],
                                                  traffic)
         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)
 
         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
                                 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._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.
         """
 
     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)
 
         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
         return Xena._create_throughput_result(root)