Merge "Enable heat context to support existing network"
[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 import logging
18 import os
19
20 import yaml
21
22 from yardstick.common.utils import mac_address_to_hex_list, try_int
23 from yardstick.network_services.utils import get_nsb_option
24 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen
25 from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper
26 from yardstick.network_services.vnf_generic.vnf.sample_vnf import DpdkVnfSetupEnvHelper
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
169 class TrexTrafficGen(SampleVNFTrafficGen):
170     """
171     This class handles mapping traffic profile and generating
172     traffic for given testcase
173     """
174
175     APP_NAME = 'TRex'
176
177     def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
178         if resource_helper_type is None:
179             resource_helper_type = TrexResourceHelper
180
181         if setup_env_helper_type is None:
182             setup_env_helper_type = TrexDpdkVnfSetupEnvHelper
183
184         super(TrexTrafficGen, self).__init__(name, vnfd, setup_env_helper_type,
185                                              resource_helper_type)
186
187     def _check_status(self):
188         return self.resource_helper.check_status()
189
190     def _start_server(self):
191         super(TrexTrafficGen, self)._start_server()
192         self.resource_helper.start()
193
194     def terminate(self):
195         self.resource_helper.terminate()
196
197     def wait_for_instantiate(self):
198         return self._wait_for_process()