1 # Copyright 2017 Martin Goldammer, OPNFV, Red Hat Inc.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
16 Trex Traffic Generator Model
18 # pylint: disable=undefined-variable
22 from collections import OrderedDict
23 # pylint: disable=unused-import
25 from conf import settings
26 from conf import merge_spec
27 from core.results.results_constants import ResultsConstants
28 from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
29 # pylint: disable=wrong-import-position, import-error
30 sys.path.append(settings.getValue('PATHS')['trafficgen']['trex']['src']['path'])
31 from trex_stl_lib.api import *
33 class Trex(ITrafficGenerator):
34 """Trex Traffic generator wrapper."""
35 _logger = logging.getLogger(__name__)
38 """Trex class constructor."""
40 self._logger.info("In trex __init__ method")
42 self._trex_host_ip_addr = (
43 settings.getValue('TRAFFICGEN_TREX_HOST_IP_ADDR'))
44 self._trex_base_dir = (
45 settings.getValue('TRAFFICGEN_TREX_BASE_DIR'))
46 self._trex_user = settings.getValue('TRAFFICGEN_TREX_USER')
47 self._stlclient = None
50 '''Connect to Trex traffic generator
52 Verify that Trex is on the system indicated by
53 the configuration file
55 self._stlclient = STLClient()
56 self._logger.info("TREX: In Trex connect method...")
57 if self._trex_host_ip_addr:
58 cmd_ping = "ping -c1 " + self._trex_host_ip_addr
60 raise RuntimeError('TREX: Trex host not defined')
62 ping = subprocess.Popen(cmd_ping, shell=True, stderr=subprocess.PIPE)
63 output, error = ping.communicate()
66 self._logger.error(error)
67 self._logger.error(output)
68 raise RuntimeError('TREX: Cannot ping Trex host at ' + \
69 self._trex_host_ip_addr)
71 connect_trex = "ssh " + self._trex_user + \
72 "@" + self._trex_host_ip_addr
74 cmd_find_trex = connect_trex + " ls " + \
75 self._trex_base_dir + "t-rex-64"
78 find_trex = subprocess.Popen(cmd_find_trex,
80 stderr=subprocess.PIPE)
81 output, error = find_trex.communicate()
83 if find_trex.returncode:
84 self._logger.error(error)
85 self._logger.error(output)
87 'TREX: Cannot locate Trex program at %s within %s' \
88 % (self._trex_host_ip_addr, self._trex_base_dir))
90 self._stlclient = STLClient(username=self._trex_user, server=self._trex_host_ip_addr,
92 self._stlclient.connect()
93 self._logger.info("TREX: Trex host successfully found...")
96 """Disconnect from the traffic generator.
98 As with :func:`connect`, this function is optional.
100 Where implemented, this function should raise an exception on
105 self._logger.info("TREX: In trex disconnect method")
106 self._stlclient.disconnect(stop_traffic=True, release_ports=True)
109 def create_packets(traffic, ports_info):
110 """Create base packet according to traffic specification.
111 If traffic haven't specified srcmac and dstmac fields
112 packet will be create with mac address of trex server.
114 mac_add = [li['hw_mac'] for li in ports_info]
116 if traffic and traffic['l2']['framesize'] > 0:
117 if traffic['l2']['dstmac'] == '00:00:00:00:00:00' and \
118 traffic['l2']['srcmac'] == '00:00:00:00:00:00':
119 base_pkt_a = Ether(src=mac_add[0], dst=mac_add[1])/ \
120 IP(proto=traffic['l3']['proto'], src=traffic['l3']['srcip'],
121 dst=traffic['l3']['dstip'])/ \
122 UDP(dport=traffic['l4']['dstport'], sport=traffic['l4']['srcport'])
123 base_pkt_b = Ether(src=mac_add[1], dst=mac_add[0])/ \
124 IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
125 dst=traffic['l3']['srcip'])/ \
126 UDP(dport=traffic['l4']['srcport'], sport=traffic['l4']['dstport'])
128 base_pkt_a = Ether(src=traffic['l2']['srcmac'], dst=traffic['l2']['dstmac'])/ \
129 IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
130 dst=traffic['l3']['srcip'])/ \
131 UDP(dport=traffic['l4']['dstport'], sport=traffic['l4']['srcport'])
133 base_pkt_b = Ether(src=traffic['l2']['dstmac'], dst=traffic['l2']['srcmac'])/ \
134 IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
135 dst=traffic['l3']['srcip'])/ \
136 UDP(dport=traffic['l4']['srcport'], sport=traffic['l4']['dstport'])
138 return (base_pkt_a, base_pkt_b)
141 def create_streams(base_pkt_a, base_pkt_b, traffic):
142 """Add the base packet to the streams. Erase FCS and add payload
143 according to traffic specification
147 frame_size = int(traffic['l2']['framesize'])
148 fsize_no_fcs = frame_size - 4
149 payload_a = max(0, fsize_no_fcs - len(base_pkt_a)) * 'x'
150 payload_b = max(0, fsize_no_fcs - len(base_pkt_b)) * 'x'
151 pkt_a = STLPktBuilder(pkt=base_pkt_a/payload_a)
152 pkt_b = STLPktBuilder(pkt=base_pkt_b/payload_b)
153 stream_1 = STLStream(packet=pkt_a,
155 mode=STLTXCont(percentage=traffic['frame_rate']))
156 stream_2 = STLStream(packet=pkt_b,
158 mode=STLTXCont(percentage=traffic['frame_rate']))
159 lat_pps = settings.getValue('TRAFFICGEN_TREX_LATENCY_PPS')
161 stream_1_lat = STLStream(packet=pkt_a,
162 flow_stats=STLFlowLatencyStats(pg_id=0),
164 mode=STLTXCont(pps=lat_pps))
165 stream_2_lat = STLStream(packet=pkt_b,
166 flow_stats=STLFlowLatencyStats(pg_id=1),
168 mode=STLTXCont(pps=lat_pps))
170 return (stream_1, stream_2, stream_1_lat, stream_2_lat)
172 def generate_traffic(self, traffic, duration):
173 """The method that generate a stream
176 self._stlclient.reset(my_ports)
177 ports_info = self._stlclient.get_port_info(my_ports)
178 packet_1, packet_2 = Trex.create_packets(traffic, ports_info)
179 stream_1, stream_2, stream_1_lat, stream_2_lat = Trex.create_streams(packet_1, packet_2, traffic)
180 self._stlclient.add_streams(stream_1, ports=[0])
181 self._stlclient.add_streams(stream_2, ports=[1])
183 if stream_1_lat is not None:
184 self._stlclient.add_streams(stream_1_lat, ports=[0])
185 self._stlclient.add_streams(stream_2_lat, ports=[1])
187 self._stlclient.clear_stats()
188 self._stlclient.start(ports=[0, 1], force=True, duration=duration)
189 self._stlclient.wait_on_traffic(ports=[0, 1])
190 stats = self._stlclient.get_stats(sync_now=True)
194 def calculate_results(stats):
195 """Calculate results from Trex statistic
197 result = OrderedDict()
198 result[ResultsConstants.TX_RATE_FPS] = (
200 float(stats["total"]["tx_pps"])))
202 result[ResultsConstants.THROUGHPUT_RX_FPS] = (
204 float(stats["total"]["rx_pps"])))
206 result[ResultsConstants.TX_RATE_MBPS] = (
208 float(stats["total"]["tx_bps"] / 1000000)))
209 result[ResultsConstants.THROUGHPUT_RX_MBPS] = (
211 float(stats["total"]["rx_bps"] / 1000000)))
213 result[ResultsConstants.TX_RATE_PERCENT] = 'Unknown'
215 result[ResultsConstants.THROUGHPUT_RX_PERCENT] = 'Unknown'
217 result[ResultsConstants.FRAME_LOSS_PERCENT] = (
219 float((stats["total"]["opackets"] - stats["total"]["ipackets"]) * 100 /
220 stats["total"]["opackets"])))
222 if settings.getValue('TRAFFICGEN_TREX_LATENCY_PPS') > 0:
223 result[ResultsConstants.MIN_LATENCY_NS] = (
225 (float(min(stats["latency"][0]["latency"]["total_min"],
226 stats["latency"][1]["latency"]["total_min"])))))
228 result[ResultsConstants.MAX_LATENCY_NS] = (
230 (float(max(stats["latency"][0]["latency"]["total_max"],
231 stats["latency"][1]["latency"]["total_max"])))))
233 result[ResultsConstants.AVG_LATENCY_NS] = (
235 float((stats["latency"][0]["latency"]["average"]+
236 stats["latency"][1]["latency"]["average"])/2)))
239 result[ResultsConstants.MIN_LATENCY_NS] = 'Unknown'
240 result[ResultsConstants.MAX_LATENCY_NS] = 'Unknown'
241 result[ResultsConstants.AVG_LATENCY_NS] = 'Unknown'
244 def send_cont_traffic(self, traffic=None, duration=30):
245 """See ITrafficGenerator for description
247 self._logger.info("In Trex send_cont_traffic method")
250 self._params['traffic'] = self.traffic_defaults.copy()
252 self._params['traffic'] = merge_spec(
253 self._params['traffic'], traffic)
255 stats = self.generate_traffic(traffic, duration)
257 return self.calculate_results(stats)
259 def start_cont_traffic(self, traffic=None, duration=30):
260 raise NotImplementedError(
261 'Trex start cont traffic not implemented')
263 def stop_cont_traffic(self):
264 """See ITrafficGenerator for description
266 raise NotImplementedError(
267 'Trex stop_cont_traffic method not implemented')
269 def send_rfc2544_throughput(self, traffic=None, duration=60,
270 lossrate=0.0, tests=10):
271 """See ITrafficGenerator for description
273 self._logger.info("In Trex send_rfc2544_throughput method")
278 self._params['traffic'] = self.traffic_defaults.copy()
280 self._params['traffic'] = merge_spec(
281 self._params['traffic'], traffic)
282 new_params = copy.deepcopy(traffic)
283 stats = self.generate_traffic(traffic, duration)
284 right = traffic['frame_rate']
285 center = traffic['frame_rate']
287 while num_test <= tests:
288 test_lossrate = ((stats["total"]["opackets"] - stats["total"]
289 ["ipackets"]) * 100) / stats["total"]["opackets"]
290 self._logger.debug("Iteration: %s, frame rate: %s, throughput_rx_fps: %s, frame_loss_percent: %s",
291 num_test, "{:.3f}".format(new_params['frame_rate']), stats['total']['rx_pps'],
292 "{:.3f}".format(test_lossrate))
293 if test_lossrate == 0.0 and new_params['frame_rate'] == traffic['frame_rate']:
295 elif test_lossrate > lossrate:
297 center = (left+right) / 2
298 new_params = copy.deepcopy(traffic)
299 new_params['frame_rate'] = center
300 stats = self.generate_traffic(new_params, duration)
303 center = (left+right) / 2
304 new_params = copy.deepcopy(traffic)
305 new_params['frame_rate'] = center
306 stats = self.generate_traffic(new_params, duration)
308 return self.calculate_results(stats)
310 def start_rfc2544_throughput(self, traffic=None, tests=1, duration=60,
312 raise NotImplementedError(
313 'Trex start rfc2544 throughput not implemented')
315 def wait_rfc2544_throughput(self):
316 raise NotImplementedError(
317 'Trex wait rfc2544 throughput not implemented')
319 def send_burst_traffic(self, traffic=None, numpkts=100, duration=5):
320 raise NotImplementedError(
321 'Trex send burst traffic not implemented')
323 def send_rfc2544_back2back(self, traffic=None, tests=1, duration=30,
325 raise NotImplementedError(
326 'Trex send rfc2544 back2back not implemented')
328 def start_rfc2544_back2back(self, traffic=None, tests=1, duration=30,
330 raise NotImplementedError(
331 'Trex start rfc2544 back2back not implemented')
333 def wait_rfc2544_back2back(self):
334 raise NotImplementedError(
335 'Trex wait rfc2544 back2back not implemented')
337 if __name__ == "__main__":