Merge "Ansible: fix lowercasing issue with ConfigParser"
[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         cmd = "sudo fuser -n tcp {0.SYNC_PORT} {0.ASYNC_PORT} -k > /dev/null 2>&1"
130         self.ssh_helper.execute(cmd.format(self))
131
132         self.ssh_helper.execute("sudo pkill -9 rex > /dev/null 2>&1")
133
134         # We MUST default to 1 because TRex won't work on single-queue devices with
135         # more than one core per port
136         # We really should be trying to find the number of queues in the driver,
137         # but there doesn't seem to be a way to do this
138         # TRex Error: the number of cores should be 1 when the driver
139         # support only one tx queue and one rx queue. Please use -c 1
140         threads_per_port = try_int(self.scenario_helper.options.get("queues_per_port"), 1)
141
142         trex_path = self.ssh_helper.join_bin_path("trex", "scripts")
143         path = get_nsb_option("trex_path", trex_path)
144
145         cmd = "./t-rex-64 --no-scapy-server -i -c {} --cfg '{}'".format(threads_per_port,
146                                                                         self.CONF_FILE)
147
148         if self.scenario_helper.options.get("trex_server_debug"):
149             # if there are errors we want to see them
150             redir = ""
151         else:
152             redir = ">/dev/null"
153         # we have to sudo cd because the path might be owned by root
154         trex_cmd = """sudo bash -c "cd '{}' ; {}" {}""".format(path, cmd, redir)
155         LOG.debug(trex_cmd)
156         self.ssh_helper.execute(trex_cmd)
157
158     def terminate(self):
159         super(TrexResourceHelper, self).terminate()
160         cmd = "sudo fuser -n tcp %s %s -k > /dev/null 2>&1"
161         self.ssh_helper.execute(cmd % (self.SYNC_PORT, self.ASYNC_PORT))
162
163
164 class TrexTrafficGen(SampleVNFTrafficGen):
165     """
166     This class handles mapping traffic profile and generating
167     traffic for given testcase
168     """
169
170     APP_NAME = 'TRex'
171
172     def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
173         if resource_helper_type is None:
174             resource_helper_type = TrexResourceHelper
175
176         if setup_env_helper_type is None:
177             setup_env_helper_type = TrexDpdkVnfSetupEnvHelper
178
179         super(TrexTrafficGen, self).__init__(name, vnfd, setup_env_helper_type,
180                                              resource_helper_type)
181
182     def _check_status(self):
183         return self.resource_helper.check_status()
184
185     def _start_server(self):
186         super(TrexTrafficGen, self)._start_server()
187         self.resource_helper.start()
188
189     def scale(self, flavor=""):
190         pass
191
192     def terminate(self):
193         self.resource_helper.terminate()
194
195     def wait_for_instantiate(self):
196         return self._wait_for_process()