Patch: Detection of running t-rex instance before initializing
[csit.git] / resources / libraries / python / TrafficGenerator.py
1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Performance testing traffic generator library."""
15
16 from robot.api import logger
17 from robot.libraries.BuiltIn import BuiltIn
18
19 from resources.libraries.python.ssh import SSH
20 from resources.libraries.python.topology import NodeType
21 from resources.libraries.python.topology import NodeSubTypeTG
22 from resources.libraries.python.topology import Topology
23 from resources.libraries.python.DropRateSearch import DropRateSearch
24
25 __all__ = ['TrafficGenerator', 'TGDropRateSearchImpl']
26
27
28 class TGDropRateSearchImpl(DropRateSearch):
29     """Drop Rate Search implementation."""
30
31     def __init__(self):
32         super(TGDropRateSearchImpl, self).__init__()
33
34     def measure_loss(self, rate, frame_size, loss_acceptance,
35                      loss_acceptance_type, traffic_type):
36
37         # we need instance of TrafficGenerator instantiated by Robot Framework
38         # to be able to use trex_stateless_remote_exec method
39         tg_instance = BuiltIn().get_library_instance(
40             'resources.libraries.python.TrafficGenerator')
41
42         if tg_instance._node['subtype'] is None:
43             raise Exception('TG subtype not defined')
44         elif tg_instance._node['subtype'] == NodeSubTypeTG.TREX:
45             unit_rate = str(rate) + self.get_rate_type_str()
46             tg_instance.trex_stateless_remote_exec(self.get_duration(),
47                                                    unit_rate, frame_size,
48                                                    traffic_type)
49
50             # TODO: getters for tg_instance and loss_acceptance_type
51             logger.trace("comparing: {} < {} ".format(tg_instance._loss,
52                                                       loss_acceptance))
53             if float(tg_instance._loss) > float(loss_acceptance):
54                 return False
55             else:
56                 return True
57         else:
58             raise NotImplementedError("TG subtype not supported")
59
60
61 class TrafficGenerator(object):
62     """Traffic Generator."""
63
64     # use one instance of TrafficGenerator for all tests in test suite
65     ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
66
67     def __init__(self):
68         self._result = None
69         self._loss = None
70         self._sent = None
71         self._received = None
72         self._node = None
73         # T-REX interface order mapping
74         self._ifaces_reordered = 0
75
76     def initialize_traffic_generator(self, tg_node, tg_if1, tg_if2,
77                                      dut1_node, dut1_if1, dut1_if2,
78                                      dut2_node, dut2_if1, dut2_if2,
79                                      test_type):
80         """TG initialization.
81
82         :param tg_node: Traffic generator node.
83         :param tg_if1: TG - name of first interface.
84         :param tg_if2: TG - name of second interface.
85         :param dut1_node: DUT1 node.
86         :param dut1_if1: DUT1 - name of first interface.
87         :param dut1_if2: DUT1 - name of second interface.
88         :param dut2_node: DUT2 node.
89         :param dut2_if1: DUT2 - name of first interface.
90         :param dut2_if2: DUT2 - name of second interface.
91         :test_type: 'L2' or 'L3' - src/dst MAC address.
92         :type tg_node: dict
93         :type tg_if1: str
94         :type tg_if2: str
95         :type dut1_node: dict
96         :type dut1_if1: str
97         :type dut1_if2: str
98         :type dut2_node: dict
99         :type dut2_if1: str
100         :type dut2_if2: str
101         :type test_type: str
102         :return: nothing
103         """
104
105         trex_path = "/opt/trex-core-2.00"
106
107         topo = Topology()
108
109         if tg_node['type'] != NodeType.TG:
110             raise Exception('Node type is not a TG')
111         self._node = tg_node
112
113         if tg_node['subtype'] == NodeSubTypeTG.TREX:
114             ssh = SSH()
115             ssh.connect(tg_node)
116
117             if1_pci = topo.get_interface_pci_addr(tg_node, tg_if1)
118             if2_pci = topo.get_interface_pci_addr(tg_node, tg_if2)
119             if1_mac = topo.get_interface_mac(tg_node, tg_if1)
120             if2_mac = topo.get_interface_mac(tg_node, tg_if2)
121
122             if test_type == 'L2':
123                 if1_adj_mac = if2_mac
124                 if2_adj_mac = if1_mac
125             elif test_type == 'L3':
126                 if1_adj_mac = topo.get_interface_mac(dut1_node, dut1_if1)
127                 if2_adj_mac = topo.get_interface_mac(dut2_node, dut2_if2)
128             else:
129                 raise Exception("test_type unknown")
130
131             if min(if1_pci, if2_pci) != if1_pci:
132                 if1_mac, if2_mac = if2_mac, if1_mac
133                 if1_pci, if2_pci = if2_pci, if1_pci
134                 if1_adj_mac, if2_adj_mac = if2_adj_mac, if1_adj_mac
135                 self._ifaces_reordered = 1
136
137             if1_mac_hex = "0x"+if1_mac.replace(":", ",0x")
138             if2_mac_hex = "0x"+if2_mac.replace(":", ",0x")
139             if1_adj_mac_hex = "0x"+if1_adj_mac.replace(":", ",0x")
140             if2_adj_mac_hex = "0x"+if2_adj_mac.replace(":", ",0x")
141
142             (ret, stdout, stderr) = ssh.exec_command(
143                 "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
144                 "- port_limit      : 2\n"
145                 "  version         : 2\n"
146                 "  interfaces      : [\"{}\",\"{}\"]\n"
147                 "  port_bandwidth_gb : 10\n"
148                 "  port_info       :\n"
149                 "          - dest_mac        :   [{}]\n"
150                 "            src_mac         :   [{}]\n"
151                 "          - dest_mac        :   [{}]\n"
152                 "            src_mac         :   [{}]\n"
153                 "EOF'"\
154                 .format(if1_pci, if2_pci,
155                         if1_adj_mac_hex, if1_mac_hex,
156                         if2_adj_mac_hex, if2_mac_hex))
157             if int(ret) != 0:
158                 logger.error("failed to create t-rex config: {}"\
159                 .format(stdout + stderr))
160                 raise RuntimeError('trex config generation error')
161
162             (ret, stdout, stderr) = ssh.exec_command(
163                 "sh -c 'cd {0}/scripts/ && "
164                 "sudo ./trex-cfg'"\
165                 .format(trex_path))
166             if int(ret) != 0:
167                 logger.error('trex-cfg failed: {0}'.format(stdout + stderr))
168                 raise RuntimeError('trex-cfg failed')
169
170             (ret, _, _) = ssh.exec_command(
171                 "sh -c 'pgrep t-rex && sudo pkill t-rex'")
172
173             (ret, _, _) = ssh.exec_command(
174                 "sh -c 'cd {0}/scripts/ && "
175                 "sudo nohup ./t-rex-64 -i -c 7 --iom 0 > /dev/null 2>&1 &'"
176                 "> /dev/null"\
177                 .format(trex_path))
178             if int(ret) != 0:
179                 raise RuntimeError('t-rex-64 startup failed')
180
181     @staticmethod
182     def teardown_traffic_generator(node):
183         """TG teardown.
184
185         :param node: Traffic generator node.
186         :type node: dict
187         :return: nothing
188         """
189         if node['type'] != NodeType.TG:
190             raise Exception('Node type is not a TG')
191         if node['subtype'] == NodeSubTypeTG.TREX:
192             ssh = SSH()
193             ssh.connect(node)
194             (ret, stdout, stderr) = ssh.exec_command(
195                 "sh -c 'sudo pkill t-rex'")
196             if int(ret) != 0:
197                 logger.error('pkill t-rex failed: {0}'.format(stdout + stderr))
198                 raise RuntimeError('pkill t-rex failed')
199
200     def trex_stateless_remote_exec(self, duration, rate, framesize,
201                                    traffic_type):
202         """Execute stateless script on remote node over ssh.
203
204         :param traffic_type: Traffic profile.
205         :type traffic_type: str
206         """
207         ssh = SSH()
208         ssh.connect(self._node)
209
210         _p0 = 1
211         _p1 = 2
212
213         if self._ifaces_reordered != 0:
214             _p0, _p1 = _p1, _p0
215
216         if traffic_type in ["3-node-xconnect", "3-node-bridge"]:
217             (ret, stdout, stderr) = ssh.exec_command(
218                 "sh -c '/tmp/openvpp-testing/resources/tools/t-rex/"
219                 "t-rex-stateless.py "
220                 "-d {0} -r {1} -s {2} "
221                 "--p{3}_src_start_ip 10.10.10.1 "
222                 "--p{3}_src_end_ip 10.10.10.254 "
223                 "--p{3}_dst_start_ip 20.20.20.1 "
224                 "--p{4}_src_start_ip 20.20.20.1 "
225                 "--p{4}_src_end_ip 20.20.20.254 "
226                 "--p{4}_dst_start_ip 10.10.10.1'".\
227                 format(duration, rate, framesize, _p0, _p1),\
228                 timeout=int(duration)+60)
229         elif traffic_type in ["3-node-IPv4"]:
230             (ret, stdout, stderr) = ssh.exec_command(
231                 "sh -c '/tmp/openvpp-testing/resources/tools/t-rex/"
232                 "t-rex-stateless.py "
233                 "-d {0} -r {1} -s {2} "
234                 "--p{3}_src_start_ip 10.10.10.2 "
235                 "--p{3}_src_end_ip 10.10.10.254 "
236                 "--p{3}_dst_start_ip 20.20.20.2 "
237                 "--p{4}_src_start_ip 20.20.20.2 "
238                 "--p{4}_src_end_ip 20.20.20.254 "
239                 "--p{4}_dst_start_ip 10.10.10.2'".\
240                 format(duration, rate, framesize, _p0, _p1),\
241                 timeout=int(duration)+60)
242         elif traffic_type in ["3-node-IPv6"]:
243             (ret, stdout, stderr) = ssh.exec_command(
244                 "sh -c '/tmp/openvpp-testing/resources/tools/t-rex/"
245                 "t-rex-stateless.py "
246                 "-d {0} -r {1} -s {2} -6 "
247                 "--p{3}_src_start_ip 2001:1::2 "
248                 "--p{3}_src_end_ip 2001:1::FE "
249                 "--p{3}_dst_start_ip 2001:2::2 "
250                 "--p{4}_src_start_ip 2001:2::2 "
251                 "--p{4}_src_end_ip 2001:2::FE "
252                 "--p{4}_dst_start_ip 2001:1::2'".\
253                 format(duration, rate, framesize, _p0, _p1),\
254                 timeout=int(duration)+60)
255         else:
256             raise NotImplementedError('Unsupported traffic type')
257
258         logger.trace(ret)
259         logger.trace(stdout)
260         logger.trace(stderr)
261
262         if int(ret) != 0:
263             raise RuntimeError('T-rex stateless runtime error')
264         else:
265             # last line from console output
266             line = stdout.splitlines()[-1]
267
268             self._result = line
269             logger.info('TrafficGen result: {0}'.format(self._result))
270
271             self._received = self._result.split(', ')[1].split('=')[1]
272             self._sent = self._result.split(', ')[2].split('=')[1]
273             self._loss = self._result.split(', ')[3].split('=')[1]
274
275     def send_traffic_on(self, node, duration, rate,
276                         framesize, traffic_type):
277         """Send traffic from all configured interfaces on TG.
278
279         :param node: Dictionary containing TG information.
280         :param duration: Duration of test traffic generation in seconds.
281         :param rate: Offered load per interface (e.g. 1%, 3gbps, 4mpps, ...).
282         :param framesize: Frame size (L2) in Bytes.
283         :param traffic_type: Traffic profile.
284         :type node: dict
285         :type duration: str
286         :type rate: str
287         :type framesize: str
288         :type traffic_type: str
289         :return: TG output.
290         :rtype: str
291         """
292         if node['type'] != NodeType.TG:
293             raise Exception('Node type is not a TG')
294
295         if node['subtype'] is None:
296             raise Exception('TG subtype not defined')
297         elif node['subtype'] == NodeSubTypeTG.TREX:
298             self.trex_stateless_remote_exec(duration, rate, framesize,
299                                             traffic_type)
300         else:
301             raise NotImplementedError("TG subtype not supported")
302
303         return self._result
304
305     def no_traffic_loss_occurred(self):
306         """Fail is loss occurred in traffic run.
307
308         :return: nothing
309         """
310         if self._loss is None:
311             raise Exception('The traffic generation has not been issued')
312         if self._loss != '0':
313             raise Exception('Traffic loss occurred: {0}'.format(self._loss))