1 # Copyright (c) 2016-2019 Intel Corporation
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
21 from yardstick.common.utils import mac_address_to_hex_list, try_int
22 from yardstick.network_services.utils import get_nsb_option
23 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen
24 from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper
25 from yardstick.network_services.vnf_generic.vnf.sample_vnf import DpdkVnfSetupEnvHelper
28 LOG = logging.getLogger(__name__)
31 class TrexDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
39 class TrexResourceHelper(ClientResourceHelper):
41 CONF_FILE = '/tmp/trex_cfg.yaml'
43 RESOURCE_WORD = 'trex'
49 def __init__(self, setup_helper):
50 super(TrexResourceHelper, self).__init__(setup_helper)
52 self.dpdk_to_trex_port_map = {}
54 def generate_cfg(self):
55 port_names = self.vnfd_helper.port_pairs.all_ports
59 self.dpdk_to_trex_port_map = {}
61 sorted_ports = sorted((self.vnfd_helper.port_num(port_name), port_name) for port_name in
63 for index, (port_num, port_name) in enumerate(sorted_ports):
64 interface = self.vnfd_helper.find_interface(name=port_name)
65 virtual_interface = interface['virtual-interface']
66 dst_mac = virtual_interface["dst_mac"]
68 # this is to check for unused ports, all ports in the topology
69 # will always have dst_mac
72 # TRex ports are in logical order roughly based on DPDK port number sorting
73 vpci_list.append(virtual_interface["vpci"])
74 local_mac = virtual_interface["local_mac"]
76 "src_mac": mac_address_to_hex_list(local_mac),
77 "dest_mac": mac_address_to_hex_list(dst_mac),
79 self.port_map[port_name] = index
80 self.dpdk_to_trex_port_map[port_num] = index
82 'interfaces': vpci_list,
83 'port_info': port_list,
84 "port_limit": len(port_names),
89 cfg_str = yaml.safe_dump(cfg_file, default_flow_style=False, explicit_start=True)
90 self.ssh_helper.upload_config_file(os.path.basename(self.CONF_FILE), cfg_str)
92 def _build_ports(self):
93 super(TrexResourceHelper, self)._build_ports()
94 # override with TRex logic port number
95 self.uplink_ports = [self.dpdk_to_trex_port_map[p] for p in self.uplink_ports]
96 self.downlink_ports = [self.dpdk_to_trex_port_map[p] for p in self.downlink_ports]
97 self.all_ports = [self.dpdk_to_trex_port_map[p] for p in self.all_ports]
99 def port_num(self, intf):
100 # return logical TRex port
101 return self.port_map[intf]
103 def check_status(self):
104 status, _, _ = self.ssh_helper.execute("sudo lsof -i:%s" % self.SYNC_PORT)
108 DISABLE_DEPLOY = True
111 super(TrexResourceHelper, self).setup()
112 if self.DISABLE_DEPLOY:
115 trex_path = self.ssh_helper.join_bin_path('trex')
117 err = self.ssh_helper.execute("which {}".format(trex_path))[0]
121 LOG.info("Copying %s to destination...", self.RESOURCE_WORD)
122 self.ssh_helper.run("sudo mkdir -p '{}'".format(os.path.dirname(trex_path)))
123 self.ssh_helper.put("~/.bash_profile", "~/.bash_profile")
124 self.ssh_helper.put(trex_path, trex_path, True)
125 ko_src = os.path.join(trex_path, "scripts/ko/src/")
126 self.ssh_helper.execute(self.MAKE_INSTALL.format(ko_src))
128 def start(self, ports=None, *args, **kwargs):
129 # pylint: disable=keyword-arg-before-vararg
130 # NOTE(ralonsoh): defining keyworded arguments before variable
131 # positional arguments is a bug. This function definition doesn't work
132 # in Python 2, although it works in Python 3. Reference:
133 # https://www.python.org/dev/peps/pep-3102/
134 cmd = "sudo fuser -n tcp {0.SYNC_PORT} {0.ASYNC_PORT} -k > /dev/null 2>&1"
135 self.ssh_helper.execute(cmd.format(self))
137 self.ssh_helper.execute("sudo pkill -9 rex > /dev/null 2>&1")
139 # We MUST default to 1 because TRex won't work on single-queue devices with
140 # more than one core per port
141 # We really should be trying to find the number of queues in the driver,
142 # but there doesn't seem to be a way to do this
143 # TRex Error: the number of cores should be 1 when the driver
144 # support only one tx queue and one rx queue. Please use -c 1
145 threads_per_port = try_int(self.scenario_helper.options.get("queues_per_port"), 1)
147 trex_path = self.ssh_helper.join_bin_path("trex", "scripts")
148 path = get_nsb_option("trex_path", trex_path)
150 cmd = "./t-rex-64 --no-scapy-server -i -c {} --cfg '{}'".format(threads_per_port,
153 if self.scenario_helper.options.get("trex_server_debug"):
154 # if there are errors we want to see them
158 # we have to sudo cd because the path might be owned by root
159 trex_cmd = """sudo bash -c "cd '{}' ; {}" {}""".format(path, cmd, redir)
161 self.ssh_helper.execute(trex_cmd)
164 super(TrexResourceHelper, self).terminate()
165 cmd = "sudo fuser -n tcp %s %s -k > /dev/null 2>&1"
166 self.ssh_helper.execute(cmd % (self.SYNC_PORT, self.ASYNC_PORT))
168 def _get_samples(self, ports, port_pg_id=None):
169 stats = self.get_stats(ports)
170 timestamp = datetime.datetime.now()
172 for pname in (intf['name'] for intf in self.vnfd_helper.interfaces):
173 port_num = self.vnfd_helper.port_num(pname)
174 port_stats = stats.get(port_num, {})
176 'rx_throughput_fps': float(port_stats.get('rx_pps', 0.0)),
177 'tx_throughput_fps': float(port_stats.get('tx_pps', 0.0)),
178 'rx_throughput_bps': float(port_stats.get('rx_bps', 0.0)),
179 'tx_throughput_bps': float(port_stats.get('tx_bps', 0.0)),
180 'in_packets': int(port_stats.get('ipackets', 0)),
181 'out_packets': int(port_stats.get('opackets', 0)),
182 'in_bytes': int(port_stats.get('ibytes', 0)),
183 'out_bytes': int(port_stats.get('obytes', 0)),
184 'timestamp': timestamp
187 pg_id_list = port_pg_id.get_pg_ids(port_num)
188 samples[pname]['latency'] = {}
189 for pg_id in pg_id_list:
190 latency_global = stats.get('latency', {})
191 pg_latency = latency_global.get(pg_id, {}).get('latency')
192 samples[pname]['latency'][pg_id] = pg_latency
197 class TrexTrafficGen(SampleVNFTrafficGen):
199 This class handles mapping traffic profile and generating
200 traffic for given testcase
205 def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
206 if resource_helper_type is None:
207 resource_helper_type = TrexResourceHelper
209 if setup_env_helper_type is None:
210 setup_env_helper_type = TrexDpdkVnfSetupEnvHelper
212 super(TrexTrafficGen, self).__init__(name, vnfd, setup_env_helper_type,
213 resource_helper_type)
215 def _check_status(self):
216 return self.resource_helper.check_status()
218 def _start_server(self):
219 super(TrexTrafficGen, self)._start_server()
220 self.resource_helper.start()
223 self.resource_helper.terminate()
225 def wait_for_instantiate(self):
226 return self._wait_for_process()