Merge "Add vfw ixload testcase for heat"
[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 from __future__ import absolute_import
17 from __future__ import print_function
18
19 import logging
20 import os
21
22 import yaml
23
24 from yardstick.common.utils import mac_address_to_hex_list, try_int
25 from yardstick.network_services.utils import get_nsb_option
26 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen
27 from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper
28 from yardstick.network_services.vnf_generic.vnf.sample_vnf import DpdkVnfSetupEnvHelper
29
30 LOG = logging.getLogger(__name__)
31
32
33 class TrexDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
34     APP_NAME = "t-rex-64"
35     CFG_CONFIG = ""
36     CFG_SCRIPT = ""
37     PIPELINE_COMMAND = ""
38     VNF_TYPE = "TG"
39
40
41 class TrexResourceHelper(ClientResourceHelper):
42
43     CONF_FILE = '/tmp/trex_cfg.yaml'
44     QUEUE_WAIT_TIME = 1
45     RESOURCE_WORD = 'trex'
46     RUN_DURATION = 0
47
48     ASYNC_PORT = 4500
49     SYNC_PORT = 4501
50
51     def __init__(self, setup_helper):
52         super(TrexResourceHelper, self).__init__(setup_helper)
53         self.port_map = {}
54         self.dpdk_to_trex_port_map = {}
55
56     def generate_cfg(self):
57         port_names = self.vnfd_helper.port_pairs.all_ports
58         vpci_list = []
59         port_list = []
60         self.port_map = {}
61         self.dpdk_to_trex_port_map = {}
62
63         sorted_ports = sorted((self.vnfd_helper.port_num(port_name), port_name) for port_name in
64                               port_names)
65         for index, (port_num, port_name) in enumerate(sorted_ports):
66             interface = self.vnfd_helper.find_interface(name=port_name)
67             virtual_interface = interface['virtual-interface']
68             dst_mac = virtual_interface["dst_mac"]
69
70             # this is to check for unused ports, all ports in the topology
71             # will always have dst_mac
72             if not dst_mac:
73                 continue
74             # TRex ports are in logical order roughly based on DPDK port number sorting
75             vpci_list.append(virtual_interface["vpci"])
76             local_mac = virtual_interface["local_mac"]
77             port_list.append({
78                 "src_mac": mac_address_to_hex_list(local_mac),
79                 "dest_mac": mac_address_to_hex_list(dst_mac),
80             })
81             self.port_map[port_name] = index
82             self.dpdk_to_trex_port_map[port_num] = index
83         trex_cfg = {
84             'interfaces': vpci_list,
85             'port_info': port_list,
86             "port_limit": len(port_names),
87             "version": '2',
88         }
89         cfg_file = [trex_cfg]
90
91         cfg_str = yaml.safe_dump(cfg_file, default_flow_style=False, explicit_start=True)
92         self.ssh_helper.upload_config_file(os.path.basename(self.CONF_FILE), cfg_str)
93
94     def _build_ports(self):
95         super(TrexResourceHelper, self)._build_ports()
96         # override with TRex logic port number
97         self.uplink_ports = [self.dpdk_to_trex_port_map[p] for p in self.uplink_ports]
98         self.downlink_ports = [self.dpdk_to_trex_port_map[p] for p in self.downlink_ports]
99         self.all_ports = [self.dpdk_to_trex_port_map[p] for p in self.all_ports]
100
101     def port_num(self, intf):
102         # return logical TRex port
103         return self.port_map[intf]
104
105     def check_status(self):
106         status, _, _ = self.ssh_helper.execute("sudo lsof -i:%s" % self.SYNC_PORT)
107         return status
108
109     # temp disable
110     DISABLE_DEPLOY = True
111
112     def setup(self):
113         super(TrexResourceHelper, self).setup()
114         if self.DISABLE_DEPLOY:
115             return
116
117         trex_path = self.ssh_helper.join_bin_path('trex')
118
119         err = self.ssh_helper.execute("which {}".format(trex_path))[0]
120         if err == 0:
121             return
122
123         LOG.info("Copying %s to destination...", self.RESOURCE_WORD)
124         self.ssh_helper.run("sudo mkdir -p '{}'".format(os.path.dirname(trex_path)))
125         self.ssh_helper.put("~/.bash_profile", "~/.bash_profile")
126         self.ssh_helper.put(trex_path, trex_path, True)
127         ko_src = os.path.join(trex_path, "scripts/ko/src/")
128         self.ssh_helper.execute(self.MAKE_INSTALL.format(ko_src))
129
130     def start(self, ports=None, *args, **kwargs):
131         cmd = "sudo fuser -n tcp {0.SYNC_PORT} {0.ASYNC_PORT} -k > /dev/null 2>&1"
132         self.ssh_helper.execute(cmd.format(self))
133
134         self.ssh_helper.execute("sudo pkill -9 rex > /dev/null 2>&1")
135
136         # We MUST default to 1 because TRex won't work on single-queue devices with
137         # more than one core per port
138         # We really should be trying to find the number of queues in the driver,
139         # but there doesn't seem to be a way to do this
140         # TRex Error: the number of cores should be 1 when the driver
141         # support only one tx queue and one rx queue. Please use -c 1
142         threads_per_port = try_int(self.scenario_helper.options.get("queues_per_port"), 1)
143
144         trex_path = self.ssh_helper.join_bin_path("trex", "scripts")
145         path = get_nsb_option("trex_path", trex_path)
146
147         cmd = "./t-rex-64 --no-scapy-server -i -c {} --cfg '{}'".format(threads_per_port,
148                                                                         self.CONF_FILE)
149
150         if self.scenario_helper.options.get("trex_server_debug"):
151             # if there are errors we want to see them
152             redir = ""
153         else:
154             redir = ">/dev/null"
155         # we have to sudo cd because the path might be owned by root
156         trex_cmd = """sudo bash -c "cd '{}' ; {}" {}""".format(path, cmd, redir)
157         LOG.debug(trex_cmd)
158         self.ssh_helper.execute(trex_cmd)
159
160     def terminate(self):
161         super(TrexResourceHelper, self).terminate()
162         cmd = "sudo fuser -n tcp %s %s -k > /dev/null 2>&1"
163         self.ssh_helper.execute(cmd % (self.SYNC_PORT, self.ASYNC_PORT))
164
165
166 class TrexTrafficGen(SampleVNFTrafficGen):
167     """
168     This class handles mapping traffic profile and generating
169     traffic for given testcase
170     """
171
172     APP_NAME = 'TRex'
173
174     def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
175         if resource_helper_type is None:
176             resource_helper_type = TrexResourceHelper
177
178         if setup_env_helper_type is None:
179             setup_env_helper_type = TrexDpdkVnfSetupEnvHelper
180
181         super(TrexTrafficGen, self).__init__(name, vnfd, setup_env_helper_type,
182                                              resource_helper_type)
183
184     def _check_status(self):
185         return self.resource_helper.check_status()
186
187     def _start_server(self):
188         super(TrexTrafficGen, self)._start_server()
189         self.resource_helper.start()
190
191     def scale(self, flavor=""):
192         pass
193
194     def listen_traffic(self, traffic_profile):
195         pass
196
197     def terminate(self):
198         self.resource_helper.terminate()