Add RFC2544 IXIA latency information
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / tg_trex.py
index 1fe790f..58b7348 100644 (file)
 # limitations under the License.
 """ Trex acts as traffic generation and vnf definitions based on IETS Spec """
 
-from __future__ import absolute_import
-from __future__ import print_function
-
 import logging
 import os
 
 import yaml
 
-from yardstick.common.utils import mac_address_to_hex_list
+from yardstick.common.utils import mac_address_to_hex_list, try_int
 from yardstick.network_services.utils import get_nsb_option
 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen
 from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper
 from yardstick.network_services.vnf_generic.vnf.sample_vnf import DpdkVnfSetupEnvHelper
 
+
 LOG = logging.getLogger(__name__)
 
 
@@ -48,35 +46,59 @@ class TrexResourceHelper(ClientResourceHelper):
     ASYNC_PORT = 4500
     SYNC_PORT = 4501
 
+    def __init__(self, setup_helper):
+        super(TrexResourceHelper, self).__init__(setup_helper)
+        self.port_map = {}
+        self.dpdk_to_trex_port_map = {}
+
     def generate_cfg(self):
-        ext_intf = self.vnfd_helper.interfaces
+        port_names = self.vnfd_helper.port_pairs.all_ports
         vpci_list = []
         port_list = []
-        trex_cfg = {
-            'interfaces': vpci_list,
-            'port_info': port_list,
-            "port_limit": len(ext_intf),
-            "version": '2',
-        }
-        cfg_file = [trex_cfg]
+        self.port_map = {}
+        self.dpdk_to_trex_port_map = {}
 
-        for interface in ext_intf:
+        sorted_ports = sorted((self.vnfd_helper.port_num(port_name), port_name) for port_name in
+                              port_names)
+        for index, (port_num, port_name) in enumerate(sorted_ports):
+            interface = self.vnfd_helper.find_interface(name=port_name)
             virtual_interface = interface['virtual-interface']
-            vpci_list.append(virtual_interface["vpci"])
             dst_mac = virtual_interface["dst_mac"]
 
+            # this is to check for unused ports, all ports in the topology
+            # will always have dst_mac
             if not dst_mac:
                 continue
-
+            # TRex ports are in logical order roughly based on DPDK port number sorting
+            vpci_list.append(virtual_interface["vpci"])
             local_mac = virtual_interface["local_mac"]
             port_list.append({
                 "src_mac": mac_address_to_hex_list(local_mac),
                 "dest_mac": mac_address_to_hex_list(dst_mac),
             })
+            self.port_map[port_name] = index
+            self.dpdk_to_trex_port_map[port_num] = index
+        trex_cfg = {
+            'interfaces': vpci_list,
+            'port_info': port_list,
+            "port_limit": len(port_names),
+            "version": '2',
+        }
+        cfg_file = [trex_cfg]
 
         cfg_str = yaml.safe_dump(cfg_file, default_flow_style=False, explicit_start=True)
         self.ssh_helper.upload_config_file(os.path.basename(self.CONF_FILE), cfg_str)
-        self._vpci_ascending = sorted(vpci_list)
+
+    def _build_ports(self):
+        super(TrexResourceHelper, self)._build_ports()
+        # override with TRex logic port number
+        self.uplink_ports = [self.dpdk_to_trex_port_map[p] for p in self.uplink_ports]
+        self.downlink_ports = [self.dpdk_to_trex_port_map[p] for p in self.downlink_ports]
+        self.all_ports = [self.dpdk_to_trex_port_map[p] for p in self.all_ports]
+
+    def port_num(self, intf):
+        # return logical TRex port
+        return self.port_map[intf]
 
     def check_status(self):
         status, _, _ = self.ssh_helper.execute("sudo lsof -i:%s" % self.SYNC_PORT)
@@ -104,20 +126,38 @@ class TrexResourceHelper(ClientResourceHelper):
         self.ssh_helper.execute(self.MAKE_INSTALL.format(ko_src))
 
     def start(self, ports=None, *args, **kwargs):
+        # pylint: disable=keyword-arg-before-vararg
+        # NOTE(ralonsoh): defining keyworded arguments before variable
+        # positional arguments is a bug. This function definition doesn't work
+        # in Python 2, although it works in Python 3. Reference:
+        # https://www.python.org/dev/peps/pep-3102/
         cmd = "sudo fuser -n tcp {0.SYNC_PORT} {0.ASYNC_PORT} -k > /dev/null 2>&1"
         self.ssh_helper.execute(cmd.format(self))
 
         self.ssh_helper.execute("sudo pkill -9 rex > /dev/null 2>&1")
 
+        # We MUST default to 1 because TRex won't work on single-queue devices with
+        # more than one core per port
+        # We really should be trying to find the number of queues in the driver,
+        # but there doesn't seem to be a way to do this
+        # TRex Error: the number of cores should be 1 when the driver
+        # support only one tx queue and one rx queue. Please use -c 1
+        threads_per_port = try_int(self.scenario_helper.options.get("queues_per_port"), 1)
+
         trex_path = self.ssh_helper.join_bin_path("trex", "scripts")
         path = get_nsb_option("trex_path", trex_path)
 
-        # cmd = "sudo ./t-rex-64 -i --cfg %s > /dev/null 2>&1" % self.CONF_FILE
-        cmd = "./t-rex-64 -i --cfg '{}'".format(self.CONF_FILE)
+        cmd = "./t-rex-64 --no-scapy-server -i -c {} --cfg '{}'".format(threads_per_port,
+                                                                        self.CONF_FILE)
 
-        # if there are errors we want to see them
+        if self.scenario_helper.options.get("trex_server_debug"):
+            # if there are errors we want to see them
+            redir = ""
+        else:
+            redir = ">/dev/null"
         # we have to sudo cd because the path might be owned by root
-        trex_cmd = """sudo bash -c "cd '{}' ; {}" >/dev/null""".format(path, cmd)
+        trex_cmd = """sudo bash -c "cd '{}' ; {}" {}""".format(path, cmd, redir)
+        LOG.debug(trex_cmd)
         self.ssh_helper.execute(trex_cmd)
 
     def terminate(self):
@@ -125,6 +165,30 @@ class TrexResourceHelper(ClientResourceHelper):
         cmd = "sudo fuser -n tcp %s %s -k > /dev/null 2>&1"
         self.ssh_helper.execute(cmd % (self.SYNC_PORT, self.ASYNC_PORT))
 
+    def _get_samples(self, ports, port_pg_id=None):
+        stats = self.get_stats(ports)
+        samples = {}
+        for pname in (intf['name'] for intf in self.vnfd_helper.interfaces):
+            port_num = self.vnfd_helper.port_num(pname)
+            port_stats = stats.get(port_num, {})
+            samples[pname] = {
+                'rx_throughput_fps': float(port_stats.get('rx_pps', 0.0)),
+                'tx_throughput_fps': float(port_stats.get('tx_pps', 0.0)),
+                'rx_throughput_bps': float(port_stats.get('rx_bps', 0.0)),
+                'tx_throughput_bps': float(port_stats.get('tx_bps', 0.0)),
+                'in_packets': int(port_stats.get('ipackets', 0)),
+                'out_packets': int(port_stats.get('opackets', 0)),
+            }
+
+            pg_id_list = port_pg_id.get_pg_ids(port_num)
+            samples[pname]['latency'] = {}
+            for pg_id in pg_id_list:
+                latency_global = stats.get('latency', {})
+                pg_latency = latency_global.get(pg_id, {}).get('latency')
+                samples[pname]['latency'][pg_id] = pg_latency
+
+        return samples
+
 
 class TrexTrafficGen(SampleVNFTrafficGen):
     """
@@ -134,15 +198,14 @@ class TrexTrafficGen(SampleVNFTrafficGen):
 
     APP_NAME = 'TRex'
 
-    def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
+    def __init__(self, name, vnfd, task_id, setup_env_helper_type=None,
+                 resource_helper_type=None):
         if resource_helper_type is None:
             resource_helper_type = TrexResourceHelper
-
         if setup_env_helper_type is None:
             setup_env_helper_type = TrexDpdkVnfSetupEnvHelper
-
-        super(TrexTrafficGen, self).__init__(name, vnfd, setup_env_helper_type,
-                                             resource_helper_type)
+        super(TrexTrafficGen, self).__init__(
+            name, vnfd, task_id, setup_env_helper_type, resource_helper_type)
 
     def _check_status(self):
         return self.resource_helper.check_status()
@@ -151,11 +214,8 @@ class TrexTrafficGen(SampleVNFTrafficGen):
         super(TrexTrafficGen, self)._start_server()
         self.resource_helper.start()
 
-    def scale(self, flavor=""):
-        pass
-
-    def listen_traffic(self, traffic_profile):
-        pass
-
     def terminate(self):
         self.resource_helper.terminate()
+
+    def wait_for_instantiate(self):
+        return self._wait_for_process()