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:
6 # http://www.apache.org/licenses/LICENSE-2.0
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.
14 """Performance testing traffic generator library."""
16 from robot.api import logger
17 from robot.libraries.BuiltIn import BuiltIn
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
25 __all__ = ['TrafficGenerator', 'TGDropRateSearchImpl']
28 class TGDropRateSearchImpl(DropRateSearch):
29 """Drop Rate Search implementation."""
32 super(TGDropRateSearchImpl, self).__init__()
34 def measure_loss(self, rate, frame_size, loss_acceptance,
35 loss_acceptance_type, traffic_type):
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')
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,
50 # TODO: getters for tg_instance and loss_acceptance_type
51 logger.trace("comparing: {} < {} ".format(tg_instance._loss,
53 if float(tg_instance._loss) > float(loss_acceptance):
58 raise NotImplementedError("TG subtype not supported")
61 class TrafficGenerator(object):
62 """Traffic Generator."""
64 # use one instance of TrafficGenerator for all tests in test suite
65 ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
73 # T-REX interface order mapping
74 self._ifaces_reordered = 0
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,
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.
104 trex_path = "/opt/trex-core-1.91"
108 if tg_node['type'] != NodeType.TG:
109 raise Exception('Node type is not a TG')
112 if tg_node['subtype'] == NodeSubTypeTG.TREX:
116 if1_pci = topo.get_interface_pci_addr(tg_node, tg_if1)
117 if2_pci = topo.get_interface_pci_addr(tg_node, tg_if2)
118 if1_mac = topo.get_interface_mac(tg_node, tg_if1)
119 if2_mac = topo.get_interface_mac(tg_node, tg_if2)
121 if test_type == 'L2':
122 if1_adj_mac = if2_mac
123 if2_adj_mac = if1_mac
124 elif test_type == 'L3':
125 if1_adj_mac = topo.get_interface_mac(dut1_node, dut1_if1)
126 if2_adj_mac = topo.get_interface_mac(dut2_node, dut2_if2)
128 raise Exception("test_type unknown")
130 if min(if1_pci, if2_pci) != if1_pci:
131 if1_mac, if2_mac = if2_mac, if1_mac
132 if1_pci, if2_pci = if2_pci, if1_pci
133 if1_adj_mac, if2_adj_mac = if2_adj_mac, if1_adj_mac
134 self._ifaces_reordered = 1
136 if1_mac_hex = "0x"+if1_mac.replace(":", ",0x")
137 if2_mac_hex = "0x"+if2_mac.replace(":", ",0x")
138 if1_adj_mac_hex = "0x"+if1_adj_mac.replace(":", ",0x")
139 if2_adj_mac_hex = "0x"+if2_adj_mac.replace(":", ",0x")
141 (ret, stdout, stderr) = ssh.exec_command(
142 "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
145 " interfaces : [\"{}\",\"{}\"]\n"
146 " port_bandwidth_gb : 10\n"
148 " - dest_mac : [{}]\n"
150 " - dest_mac : [{}]\n"
153 .format(if1_pci, if2_pci,
154 if1_adj_mac_hex, if1_mac_hex,
155 if2_adj_mac_hex, if2_mac_hex))
157 logger.error("failed to create t-rex config: {}"\
158 .format(stdout + stderr))
159 raise RuntimeError('trex config generation error')
161 (ret, stdout, stderr) = ssh.exec_command(
162 "sh -c 'cd {0}/scripts/ && "
166 logger.error('trex-cfg failed: {0}'.format(stdout + stderr))
167 raise RuntimeError('trex-cfg failed')
169 (ret, _, _) = ssh.exec_command(
170 "sh -c 'cd {0}/scripts/ && "
171 "sudo nohup ./t-rex-64 -i -c 4 --iom 0 > /dev/null 2>&1 &'"
175 raise RuntimeError('t-rex-64 startup failed')
178 def teardown_traffic_generator(node):
181 :param node: Traffic generator node.
185 if node['type'] != NodeType.TG:
186 raise Exception('Node type is not a TG')
187 if node['subtype'] == NodeSubTypeTG.TREX:
190 (ret, stdout, stderr) = ssh.exec_command(
191 "sh -c 'sudo pkill t-rex'")
193 logger.error('pkill t-rex failed: {0}'.format(stdout + stderr))
194 raise RuntimeError('pkill t-rex failed')
196 def trex_stateless_remote_exec(self, duration, rate, framesize,
198 """Execute stateless script on remote node over ssh.
200 :param traffic_type: Traffic profile.
201 :type traffic_type: str
204 ssh.connect(self._node)
209 if self._ifaces_reordered != 0:
212 if traffic_type in ["3-node-xconnect", "3-node-bridge"]:
213 (ret, stdout, stderr) = ssh.exec_command(
214 "sh -c '/tmp/openvpp-testing/resources/tools/t-rex/"
215 "t-rex-stateless.py "
216 "-d {0} -r {1} -s {2} "
217 "--p{3}_src_start_ip 10.10.10.1 "
218 "--p{3}_src_end_ip 10.10.10.254 "
219 "--p{3}_dst_start_ip 20.20.20.1 "
220 "--p{4}_src_start_ip 20.20.20.1 "
221 "--p{4}_src_end_ip 20.20.20.254 "
222 "--p{4}_dst_start_ip 10.10.10.1'".\
223 format(duration, rate, framesize, _p0, _p1),\
224 timeout=int(duration)+60)
225 elif traffic_type in ["3-node-IPv4"]:
226 (ret, stdout, stderr) = ssh.exec_command(
227 "sh -c '/tmp/openvpp-testing/resources/tools/t-rex/"
228 "t-rex-stateless.py "
229 "-d {0} -r {1} -s {2} "
230 "--p{3}_src_start_ip 10.10.10.2 "
231 "--p{3}_src_end_ip 10.10.10.254 "
232 "--p{3}_dst_start_ip 20.20.20.2 "
233 "--p{4}_src_start_ip 20.20.20.2 "
234 "--p{4}_src_end_ip 20.20.20.254 "
235 "--p{4}_dst_start_ip 10.10.10.2'".\
236 format(duration, rate, framesize, _p0, _p1),\
237 timeout=int(duration)+60)
239 raise NotImplementedError('Unsupported traffic type')
245 # last line from console output
246 line = stdout.splitlines()[-1]
249 logger.info('TrafficGen result: {0}'.format(self._result))
251 self._received = self._result.split(', ')[1].split('=')[1]
252 self._sent = self._result.split(', ')[2].split('=')[1]
253 self._loss = self._result.split(', ')[3].split('=')[1]
255 def send_traffic_on(self, node, duration, rate,
256 framesize, traffic_type):
257 """Send traffic from all configured interfaces on TG.
259 :param node: Dictionary containing TG information.
260 :param duration: Duration of test traffic generation in seconds.
261 :param rate: Offered load per interface (e.g. 1%, 3gbps, 4mpps, ...).
262 :param framesize: Frame size (L2) in Bytes.
263 :param traffic_type: Traffic profile.
268 :type traffic_type: str
272 if node['type'] != NodeType.TG:
273 raise Exception('Node type is not a TG')
275 if node['subtype'] is None:
276 raise Exception('TG subtype not defined')
277 elif node['subtype'] == NodeSubTypeTG.TREX:
278 self.trex_stateless_remote_exec(duration, rate, framesize,
281 raise NotImplementedError("TG subtype not supported")
285 def no_traffic_loss_occurred(self):
286 """Fail is loss occurred in traffic run.
290 if self._loss is None:
291 raise Exception('The traffic generation has not been issued')
292 if self._loss != '0':
293 raise Exception('Traffic loss occurred: {0}'.format(self._loss))