Merge "Bugfix: heat conext of test case yamls jinja2 bypass sriov type"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / tg_trex.py
1 # Copyright (c) 2016-2017 Intel Corporation
2 #
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
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14 """ Trex acts as traffic generation and vnf definitions based on IETS Spec """
15
16 import logging
17 import os
18
19 import yaml
20
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
26
27
28 LOG = logging.getLogger(__name__)
29
30
31 class TrexDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
32     APP_NAME = "t-rex-64"
33     CFG_CONFIG = ""
34     CFG_SCRIPT = ""
35     PIPELINE_COMMAND = ""
36     VNF_TYPE = "TG"
37
38
39 class TrexResourceHelper(ClientResourceHelper):
40
41     CONF_FILE = '/tmp/trex_cfg.yaml'
42     QUEUE_WAIT_TIME = 1
43     RESOURCE_WORD = 'trex'
44     RUN_DURATION = 0
45
46     ASYNC_PORT = 4500
47     SYNC_PORT = 4501
48
49     def __init__(self, setup_helper):
50         super(TrexResourceHelper, self).__init__(setup_helper)
51         self.port_map = {}
52         self.dpdk_to_trex_port_map = {}
53
54     def generate_cfg(self):
55         port_names = self.vnfd_helper.port_pairs.all_ports
56         vpci_list = []
57         port_list = []
58         self.port_map = {}
59         self.dpdk_to_trex_port_map = {}
60
61         sorted_ports = sorted((self.vnfd_helper.port_num(port_name), port_name) for port_name in
62                               port_names)
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"]
67
68             # this is to check for unused ports, all ports in the topology
69             # will always have dst_mac
70             if not dst_mac:
71                 continue
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"]
75             port_list.append({
76                 "src_mac": mac_address_to_hex_list(local_mac),
77                 "dest_mac": mac_address_to_hex_list(dst_mac),
78             })
79             self.port_map[port_name] = index
80             self.dpdk_to_trex_port_map[port_num] = index
81         trex_cfg = {
82             'interfaces': vpci_list,
83             'port_info': port_list,
84             "port_limit": len(port_names),
85             "version": '2',
86         }
87         cfg_file = [trex_cfg]
88
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)
91
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]
98
99     def port_num(self, intf):
100         # return logical TRex port
101         return self.port_map[intf]
102
103     def check_status(self):
104         status, _, _ = self.ssh_helper.execute("sudo lsof -i:%s" % self.SYNC_PORT)
105         return status
106
107     # temp disable
108     DISABLE_DEPLOY = True
109
110     def setup(self):
111         super(TrexResourceHelper, self).setup()
112         if self.DISABLE_DEPLOY:
113             return
114
115         trex_path = self.ssh_helper.join_bin_path('trex')
116
117         err = self.ssh_helper.execute("which {}".format(trex_path))[0]
118         if err == 0:
119             return
120
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))
127
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))
136
137         self.ssh_helper.execute("sudo pkill -9 rex > /dev/null 2>&1")
138
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)
146
147         trex_path = self.ssh_helper.join_bin_path("trex", "scripts")
148         path = get_nsb_option("trex_path", trex_path)
149
150         cmd = "./t-rex-64 --no-scapy-server -i -c {} --cfg '{}'".format(threads_per_port,
151                                                                         self.CONF_FILE)
152
153         if self.scenario_helper.options.get("trex_server_debug"):
154             # if there are errors we want to see them
155             redir = ""
156         else:
157             redir = ">/dev/null"
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)
160         LOG.debug(trex_cmd)
161         self.ssh_helper.execute(trex_cmd)
162
163     def terminate(self):
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))
167
168     def _get_samples(self, ports, port_pg_id=None):
169         stats = self.get_stats(ports)
170         samples = {}
171         for pname in (intf['name'] for intf in self.vnfd_helper.interfaces):
172             port_num = self.vnfd_helper.port_num(pname)
173             port_stats = stats.get(port_num, {})
174             samples[pname] = {
175                 'rx_throughput_fps': float(port_stats.get('rx_pps', 0.0)),
176                 'tx_throughput_fps': float(port_stats.get('tx_pps', 0.0)),
177                 'rx_throughput_bps': float(port_stats.get('rx_bps', 0.0)),
178                 'tx_throughput_bps': float(port_stats.get('tx_bps', 0.0)),
179                 'in_packets': int(port_stats.get('ipackets', 0)),
180                 'out_packets': int(port_stats.get('opackets', 0)),
181             }
182
183             pg_id_list = port_pg_id.get_pg_ids(port_num)
184             samples[pname]['latency'] = {}
185             for pg_id in pg_id_list:
186                 latency_global = stats.get('latency', {})
187                 pg_latency = latency_global.get(pg_id, {}).get('latency')
188                 samples[pname]['latency'][pg_id] = pg_latency
189
190         return samples
191
192
193 class TrexTrafficGen(SampleVNFTrafficGen):
194     """
195     This class handles mapping traffic profile and generating
196     traffic for given testcase
197     """
198
199     APP_NAME = 'TRex'
200
201     def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
202         if resource_helper_type is None:
203             resource_helper_type = TrexResourceHelper
204
205         if setup_env_helper_type is None:
206             setup_env_helper_type = TrexDpdkVnfSetupEnvHelper
207
208         super(TrexTrafficGen, self).__init__(name, vnfd, setup_env_helper_type,
209                                              resource_helper_type)
210
211     def _check_status(self):
212         return self.resource_helper.check_status()
213
214     def _start_server(self):
215         super(TrexTrafficGen, self)._start_server()
216         self.resource_helper.start()
217
218     def terminate(self):
219         self.resource_helper.terminate()
220
221     def wait_for_instantiate(self):
222         return self._wait_for_process()