1 # Copyright 2016 Cisco Systems, Inc. All rights reserved.
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # 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, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
14 """Driver module for TRex traffic generator."""
22 from itertools import count
23 from nfvbench.log import LOG
24 from nfvbench.traffic_server import TRexTrafficServer
25 from nfvbench.utils import cast_integer
26 from nfvbench.utils import timeout
27 from nfvbench.utils import TimeoutError
28 from traffic_base import AbstractTrafficGenerator
29 from traffic_base import TrafficGeneratorException
30 import traffic_utils as utils
31 from traffic_utils import IMIX_AVG_L2_FRAME_SIZE
32 from traffic_utils import IMIX_L2_SIZES
33 from traffic_utils import IMIX_RATIOS
35 # pylint: disable=import-error
36 from trex_stl_lib.api import bind_layers
37 from trex_stl_lib.api import CTRexVmInsFixHwCs
38 from trex_stl_lib.api import Dot1Q
39 from trex_stl_lib.api import Ether
40 from trex_stl_lib.api import FlagsField
41 from trex_stl_lib.api import IP
42 from trex_stl_lib.api import Packet
43 from trex_stl_lib.api import STLClient
44 from trex_stl_lib.api import STLError
45 from trex_stl_lib.api import STLFlowLatencyStats
46 from trex_stl_lib.api import STLFlowStats
47 from trex_stl_lib.api import STLPktBuilder
48 from trex_stl_lib.api import STLScVmRaw
49 from trex_stl_lib.api import STLStream
50 from trex_stl_lib.api import STLTXCont
51 from trex_stl_lib.api import STLVmFixChecksumHw
52 from trex_stl_lib.api import STLVmFlowVar
53 from trex_stl_lib.api import STLVmFlowVarRepetableRandom
54 from trex_stl_lib.api import STLVmWrFlowVar
55 from trex_stl_lib.api import ThreeBytesField
56 from trex_stl_lib.api import UDP
57 from trex_stl_lib.api import XByteField
58 from trex_stl_lib.services.trex_stl_service_arp import STLServiceARP
61 # pylint: enable=import-error
66 _VXLAN_FLAGS = ['R' * 27] + ['I'] + ['R' * 5]
68 fields_desc = [FlagsField("flags", 0x08000000, 32, _VXLAN_FLAGS),
69 ThreeBytesField("vni", 0),
70 XByteField("reserved", 0x00)]
74 return self.sprintf("VXLAN (vni=%VXLAN.vni%)")
76 class TRex(AbstractTrafficGenerator):
77 """TRex traffic generator driver."""
80 CHAIN_PG_ID_MASK = 0x007F
81 PORT_PG_ID_MASK = 0x0080
82 LATENCY_PG_ID_MASK = 0x0100
84 def __init__(self, traffic_client):
86 AbstractTrafficGenerator.__init__(self, traffic_client)
90 self.chain_count = self.generator_config.service_chain_count
92 self.capture_id = None
95 def get_version(self):
96 """Get the Trex version."""
97 return self.client.get_server_version() if self.client else ''
99 def get_pg_id(self, port, chain_id):
100 """Calculate the packet group IDs to use for a given port/stream type/chain_id.
103 chain_id: identifies to which chain the pg_id is associated (0 to 255)
104 return: pg_id, lat_pg_id
106 We use a bit mask to set up the 3 fields:
107 0x007F: chain ID (8 bits for a max of 128 chains)
111 pg_id = port * TRex.PORT_PG_ID_MASK | chain_id
112 return pg_id, pg_id | TRex.LATENCY_PG_ID_MASK
114 def extract_stats(self, in_stats):
115 """Extract stats from dict returned by Trex API.
117 :param in_stats: dict as returned by TRex api
119 utils.nan_replace(in_stats)
120 # LOG.debug(in_stats)
123 # port_handles should have only 2 elements: [0, 1]
124 # so (1 - ph) will be the index for the far end port
125 for ph in self.port_handle:
127 far_end_stats = in_stats[1 - ph]
130 'total_pkts': cast_integer(stats['opackets']),
131 'total_pkt_bytes': cast_integer(stats['obytes']),
132 'pkt_rate': cast_integer(stats['tx_pps']),
133 'pkt_bit_rate': cast_integer(stats['tx_bps'])
136 'total_pkts': cast_integer(stats['ipackets']),
137 'total_pkt_bytes': cast_integer(stats['ibytes']),
138 'pkt_rate': cast_integer(stats['rx_pps']),
139 'pkt_bit_rate': cast_integer(stats['rx_bps']),
140 # how many pkts were dropped in RX direction
141 # need to take the tx counter on the far end port
142 'dropped_pkts': cast_integer(
143 far_end_stats['opackets'] - stats['ipackets'])
146 self.__combine_latencies(in_stats, result[ph]['rx'], ph)
148 total_tx_pkts = result[0]['tx']['total_pkts'] + result[1]['tx']['total_pkts']
149 result["total_tx_rate"] = cast_integer(total_tx_pkts / self.config.duration_sec)
150 result["flow_stats"] = in_stats["flow_stats"]
151 result["latency"] = in_stats["latency"]
154 def get_stream_stats(self, trex_stats, if_stats, latencies, chain_idx):
155 """Extract the aggregated stats for a given chain.
157 trex_stats: stats as returned by get_stats()
158 if_stats: a list of 2 interface stats to update (port 0 and 1)
159 latencies: a list of 2 Latency instances to update for this chain (port 0 and 1)
160 latencies[p] is the latency for packets sent on port p
161 if there are no latency streams, the Latency instances are not modified
162 chain_idx: chain index of the interface stats
164 The packet counts include normal and latency streams.
166 Trex returns flows stats as follows:
168 'flow_stats': {0: {'rx_bps': {0: 0, 1: 0, 'total': 0},
169 'rx_bps_l1': {0: 0.0, 1: 0.0, 'total': 0.0},
170 'rx_bytes': {0: nan, 1: nan, 'total': nan},
171 'rx_pkts': {0: 0, 1: 15001, 'total': 15001},
172 'rx_pps': {0: 0, 1: 0, 'total': 0},
173 'tx_bps': {0: 0, 1: 0, 'total': 0},
174 'tx_bps_l1': {0: 0.0, 1: 0.0, 'total': 0.0},
175 'tx_bytes': {0: 1020068, 1: 0, 'total': 1020068},
176 'tx_pkts': {0: 15001, 1: 0, 'total': 15001},
177 'tx_pps': {0: 0, 1: 0, 'total': 0}},
178 1: {'rx_bps': {0: 0, 1: 0, 'total': 0},
179 'rx_bps_l1': {0: 0.0, 1: 0.0, 'total': 0.0},
180 'rx_bytes': {0: nan, 1: nan, 'total': nan},
181 'rx_pkts': {0: 0, 1: 15001, 'total': 15001},
182 'rx_pps': {0: 0, 1: 0, 'total': 0},
183 'tx_bps': {0: 0, 1: 0, 'total': 0},
184 'tx_bps_l1': {0: 0.0, 1: 0.0, 'total': 0.0},
185 'tx_bytes': {0: 1020068, 1: 0, 'total': 1020068},
186 'tx_pkts': {0: 15001, 1: 0, 'total': 15001},
187 'tx_pps': {0: 0, 1: 0, 'total': 0}},
188 128: {'rx_bps': {0: 0, 1: 0, 'total': 0},
189 'rx_bps_l1': {0: 0.0, 1: 0.0, 'total': 0.0},
190 'rx_bytes': {0: nan, 1: nan, 'total': nan},
191 'rx_pkts': {0: 15001, 1: 0, 'total': 15001},
192 'rx_pps': {0: 0, 1: 0, 'total': 0},
193 'tx_bps': {0: 0, 1: 0, 'total': 0},
194 'tx_bps_l1': {0: 0.0, 1: 0.0, 'total': 0.0},
195 'tx_bytes': {0: 0, 1: 1020068, 'total': 1020068},
196 'tx_pkts': {0: 0, 1: 15001, 'total': 15001},
197 'tx_pps': {0: 0, 1: 0, 'total': 0}},etc...
199 the pg_id (0, 1, 128,...) is the key of the dict and is obtained using the
201 packet counters for a given stream sent on port p are reported as:
202 - tx_pkts[p] on port p
203 - rx_pkts[1-p] on the far end port
205 This is a tricky/critical counter transposition operation because
206 the results are grouped by port (not by stream):
207 tx_pkts_port(p=0) comes from pg_id(port=0, chain_idx)['tx_pkts'][0]
208 rx_pkts_port(p=0) comes from pg_id(port=1, chain_idx)['rx_pkts'][0]
209 tx_pkts_port(p=1) comes from pg_id(port=1, chain_idx)['tx_pkts'][1]
210 rx_pkts_port(p=1) comes from pg_id(port=0, chain_idx)['rx_pkts'][1]
212 or using a more generic formula:
213 tx_pkts_port(p) comes from pg_id(port=p, chain_idx)['tx_pkts'][p]
214 rx_pkts_port(p) comes from pg_id(port=1-p, chain_idx)['rx_pkts'][p]
216 the second formula is equivalent to
217 rx_pkts_port(1-p) comes from pg_id(port=p, chain_idx)['rx_pkts'][1-p]
219 If there are latency streams, those same counters need to be added in the same way
221 def get_latency(lval):
223 return int(round(lval))
229 for port in range(2):
230 pg_id, lat_pg_id = self.get_pg_id(port, chain_idx)
231 for pid in [pg_id, lat_pg_id]:
233 pg_stats = trex_stats['flow_stats'][pid]
234 if_stats[port].tx += pg_stats['tx_pkts'][port]
235 if_stats[1 - port].rx += pg_stats['rx_pkts'][1 - port]
239 lat = trex_stats['latency'][lat_pg_id]['latency']
240 # dropped_pkts += lat['err_cntrs']['dropped']
241 latencies[port].max_usec = get_latency(lat['total_max'])
242 if math.isnan(lat['total_min']):
243 latencies[port].min_usec = 0
244 latencies[port].avg_usec = 0
246 latencies[port].min_usec = get_latency(lat['total_min'])
247 latencies[port].avg_usec = get_latency(lat['average'])
251 def __combine_latencies(self, in_stats, results, port_handle):
252 """Traverse TRex result dictionary and combines chosen latency stats."""
255 total_min = float("inf")
256 for chain_id in range(self.chain_count):
258 _, lat_pg_id = self.get_pg_id(port_handle, chain_id)
259 lat = in_stats['latency'][lat_pg_id]['latency']
260 # dropped_pkts += lat['err_cntrs']['dropped']
261 total_max = max(lat['total_max'], total_max)
262 total_min = min(lat['total_min'], total_min)
263 average += lat['average']
266 if total_min == float("inf"):
268 results['min_delay_usec'] = total_min
269 results['max_delay_usec'] = total_max
270 results['avg_delay_usec'] = int(average / self.chain_count)
272 def _bind_vxlan(self):
273 bind_layers(UDP, VXLAN, dport=4789)
274 bind_layers(VXLAN, Ether)
276 def _create_pkt(self, stream_cfg, l2frame_size):
277 """Create a packet of given size.
279 l2frame_size: size of the L2 frame in bytes (including the 32-bit FCS)
281 # Trex will add the FCS field, so we need to remove 4 bytes from the l2 frame size
282 frame_size = int(l2frame_size) - 4
284 if stream_cfg['vxlan'] is True:
287 pkt_base = Ether(src=stream_cfg['vtep_src_mac'], dst=stream_cfg['vtep_dst_mac'])
288 if stream_cfg['vtep_vlan'] is not None:
289 pkt_base /= Dot1Q(vlan=stream_cfg['vtep_vlan'])
290 pkt_base /= IP(src=stream_cfg['vtep_src_ip'], dst=stream_cfg['vtep_dst_ip'])
291 pkt_base /= UDP(sport=random.randint(1337, 32767), dport=4789)
292 pkt_base /= VXLAN(vni=stream_cfg['net_vni'])
293 pkt_base /= Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst'])
296 pkt_base = Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst'])
298 if stream_cfg['vlan_tag'] is not None:
299 pkt_base /= Dot1Q(vlan=stream_cfg['vlan_tag'])
302 if stream_cfg['udp_src_port']:
303 udp_args['sport'] = int(stream_cfg['udp_src_port'])
304 if stream_cfg['udp_dst_port']:
305 udp_args['dport'] = int(stream_cfg['udp_dst_port'])
306 pkt_base /= IP() / UDP(**udp_args)
308 if stream_cfg['ip_addrs_step'] == 'random':
309 src_fv = STLVmFlowVarRepetableRandom(
311 min_value=stream_cfg['ip_src_addr'],
312 max_value=stream_cfg['ip_src_addr_max'],
314 seed=random.randint(0, 32767),
315 limit=stream_cfg['ip_src_count'])
316 dst_fv = STLVmFlowVarRepetableRandom(
318 min_value=stream_cfg['ip_dst_addr'],
319 max_value=stream_cfg['ip_dst_addr_max'],
321 seed=random.randint(0, 32767),
322 limit=stream_cfg['ip_dst_count'])
324 src_fv = STLVmFlowVar(
326 min_value=stream_cfg['ip_src_addr'],
327 max_value=stream_cfg['ip_src_addr'],
330 step=stream_cfg['ip_addrs_step'])
331 dst_fv = STLVmFlowVar(
333 min_value=stream_cfg['ip_dst_addr'],
334 max_value=stream_cfg['ip_dst_addr_max'],
337 step=stream_cfg['ip_addrs_step'])
341 STLVmWrFlowVar(fv_name="ip_src", pkt_offset="IP:{}.src".format(encap_level)),
343 STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP:{}.dst".format(encap_level)),
344 STLVmFixChecksumHw(l3_offset="IP:{}".format(encap_level),
345 l4_offset="UDP:{}".format(encap_level),
346 l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP)
348 pad = max(0, frame_size - len(pkt_base)) * 'x'
350 return STLPktBuilder(pkt=pkt_base / pad, vm=STLScVmRaw(vm_param))
352 def generate_streams(self, port, chain_id, stream_cfg, l2frame, latency=True):
353 """Create a list of streams corresponding to a given chain and stream config.
355 port: port where the streams originate (0 or 1)
356 chain_id: the chain to which the streams are associated to
357 stream_cfg: stream configuration
358 l2frame: L2 frame size (including 4-byte FCS) or 'IMIX'
359 latency: if True also create a latency stream
362 pg_id, lat_pg_id = self.get_pg_id(port, chain_id)
363 if l2frame == 'IMIX':
364 for ratio, l2_frame_size in zip(IMIX_RATIOS, IMIX_L2_SIZES):
365 pkt = self._create_pkt(stream_cfg, l2_frame_size)
366 streams.append(STLStream(packet=pkt,
367 flow_stats=STLFlowStats(pg_id=pg_id),
368 mode=STLTXCont(pps=ratio)))
371 # for IMIX, the latency packets have the average IMIX packet size
372 pkt = self._create_pkt(stream_cfg, IMIX_AVG_L2_FRAME_SIZE)
375 l2frame_size = int(l2frame)
376 pkt = self._create_pkt(stream_cfg, l2frame_size)
377 streams.append(STLStream(packet=pkt,
378 flow_stats=STLFlowStats(pg_id=pg_id),
380 # for the latency stream, the minimum payload is 16 bytes even in case of vlan tagging
381 # without vlan, the min l2 frame size is 64
383 # This only applies to the latency stream
384 if latency and stream_cfg['vlan_tag'] and l2frame_size < 68:
385 pkt = self._create_pkt(stream_cfg, 68)
388 streams.append(STLStream(packet=pkt,
389 flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id),
390 mode=STLTXCont(pps=self.LATENCY_PPS)))
394 def __connect(self, client):
397 def __connect_after_start(self):
398 # after start, Trex may take a bit of time to initialize
399 # so we need to retry a few times
400 for it in xrange(self.config.generic_retry_count):
403 self.client.connect()
405 except Exception as ex:
406 if it == (self.config.generic_retry_count - 1):
408 LOG.info("Retrying connection to TRex (%s)...", ex.message)
411 """Connect to the TRex server."""
412 server_ip = self.generator_config.ip
413 LOG.info("Connecting to TRex (%s)...", server_ip)
415 # Connect to TRex server
416 self.client = STLClient(server=server_ip, sync_port=self.generator_config.zmq_rpc_port,
417 async_port=self.generator_config.zmq_pub_port)
419 self.__connect(self.client)
420 except (TimeoutError, STLError) as e:
421 if server_ip == '127.0.0.1':
423 self.__start_server()
424 self.__connect_after_start()
425 except (TimeoutError, STLError) as e:
426 LOG.error('Cannot connect to TRex')
427 LOG.error(traceback.format_exc())
428 logpath = '/tmp/trex.log'
429 if os.path.isfile(logpath):
430 # Wait for TRex to finish writing error message
432 for _ in xrange(self.config.generic_retry_count):
433 size = os.path.getsize(logpath)
434 if size == last_size:
435 # probably not writing anymore
439 with open(logpath, 'r') as f:
443 raise TrafficGeneratorException(message)
445 raise TrafficGeneratorException(e.message)
447 ports = list(self.generator_config.ports)
448 self.port_handle = ports
450 self.client.reset(ports)
451 # Read HW information from each port
452 # this returns an array of dict (1 per port)
454 Example of output for Intel XL710
455 [{'arp': '-', 'src_ipv4': '-', u'supp_speeds': [40000], u'is_link_supported': True,
456 'grat_arp': 'off', 'speed': 40, u'index': 0, 'link_change_supported': 'yes',
457 u'rx': {u'counters': 127, u'caps': [u'flow_stats', u'latency']},
458 u'is_virtual': 'no', 'prom': 'off', 'src_mac': u'3c:fd:fe:a8:24:48', 'status': 'IDLE',
459 u'description': u'Ethernet Controller XL710 for 40GbE QSFP+',
460 'dest': u'fa:16:3e:3c:63:04', u'is_fc_supported': False, 'vlan': '-',
461 u'driver': u'net_i40e', 'led_change_supported': 'yes', 'rx_filter_mode': 'hardware match',
462 'fc': 'none', 'link': 'UP', u'hw_mac': u'3c:fd:fe:a8:24:48', u'pci_addr': u'0000:5e:00.0',
463 'mult': 'off', 'fc_supported': 'no', u'is_led_supported': True, 'rx_queue': 'off',
464 'layer_mode': 'Ethernet', u'numa': 0}, ...]
466 self.port_info = self.client.get_port_info(ports)
467 LOG.info('Connected to TRex')
468 for id, port in enumerate(self.port_info):
469 LOG.info(' Port %d: %s speed=%dGbps mac=%s pci=%s driver=%s',
470 id, port['description'], port['speed'], port['src_mac'],
471 port['pci_addr'], port['driver'])
472 # Make sure the 2 ports have the same speed
473 if self.port_info[0]['speed'] != self.port_info[1]['speed']:
474 raise TrafficGeneratorException('Traffic generator ports speed mismatch: %d/%d Gbps' %
475 (self.port_info[0]['speed'],
476 self.port_info[1]['speed']))
478 def __start_server(self):
479 server = TRexTrafficServer()
480 server.run_server(self.generator_config)
482 def resolve_arp(self):
483 """Resolve all configured remote IP addresses.
485 return: None if ARP failed to resolve for all IP addresses
486 else a dict of list of dest macs indexed by port#
487 the dest macs in the list are indexed by the chain id
489 self.client.set_service_mode(ports=self.port_handle)
490 LOG.info('Polling ARP until successful...')
492 for port, device in zip(self.port_handle, self.generator_config.devices):
493 # there should be 1 stream config per chain
494 stream_configs = device.get_stream_configs()
495 chain_count = len(stream_configs)
496 ctx = self.client.create_service_ctx(port=port)
497 # all dest macs on this port indexed by chain ID
498 dst_macs = [None] * chain_count
500 # the index in the list is the chain id
501 if self.config.vxlan:
504 src_ip=device.vtep_src_ip,
505 dst_ip=device.vtep_dst_ip,
506 vlan=device.vtep_vlan)
507 for cfg in stream_configs
512 src_ip=cfg['ip_src_tg_gw'],
513 dst_ip=cfg['mac_discovery_gw'],
514 # will be None if no vlan tagging
515 vlan=cfg['vlan_tag'])
516 for cfg in stream_configs
519 for attempt in range(self.config.generic_retry_count):
523 LOG.error(traceback.format_exc())
527 for chain_id, mac in enumerate(dst_macs):
529 arp_record = arps[chain_id].get_record()
530 if arp_record.dst_mac:
531 dst_macs[chain_id] = arp_record.dst_mac
533 LOG.info(' ARP: port=%d chain=%d src IP=%s dst IP=%s -> MAC=%s',
536 arp_record.dst_ip, arp_record.dst_mac)
538 unresolved.append(arp_record.dst_ip)
539 if dst_macs_count == chain_count:
540 arp_dest_macs[port] = dst_macs
541 LOG.info('ARP resolved successfully for port %s', port)
545 LOG.info('Retrying ARP for: %s (retry %d/%d)',
546 unresolved, retry, self.config.generic_retry_count)
547 if retry < self.config.generic_retry_count:
548 time.sleep(self.config.generic_poll_sec)
550 LOG.error('ARP timed out for port %s (resolved %d out of %d)',
556 self.client.set_service_mode(ports=self.port_handle, enabled=False)
557 if len(arp_dest_macs) == len(self.port_handle):
561 def __is_rate_enough(self, l2frame_size, rates, bidirectional, latency):
562 """Check if rate provided by user is above requirements. Applies only if latency is True."""
563 intf_speed = self.generator_config.intf_speed
569 r = utils.convert_rates(l2frame_size, rate, intf_speed)
570 total_rate += int(r['rate_pps'])
573 total_rate = utils.convert_rates(l2frame_size, rates[0], intf_speed)
574 # rate must be enough for latency stream and at least 1 pps for base stream per chain
575 required_rate = (self.LATENCY_PPS + 1) * self.config.service_chain_count * mult
576 result = utils.convert_rates(l2frame_size,
577 {'rate_pps': required_rate},
579 result['result'] = total_rate >= required_rate
582 return {'result': True}
584 def create_traffic(self, l2frame_size, rates, bidirectional, latency=True):
585 """Program all the streams in Trex server.
587 l2frame_size: L2 frame size or IMIX
588 rates: a list of 2 rates to run each direction
589 each rate is a dict like {'rate_pps': '10kpps'}
590 bidirectional: True if bidirectional
591 latency: True if latency measurement is needed
593 r = self.__is_rate_enough(l2frame_size, rates, bidirectional, latency)
595 raise TrafficGeneratorException(
596 'Required rate in total is at least one of: \n{pps}pps \n{bps}bps \n{load}%.'
597 .format(pps=r['rate_pps'],
599 load=r['rate_percent']))
600 # a dict of list of streams indexed by port#
601 # in case of fixed size, has self.chain_count * 2 * 2 streams
602 # (1 normal + 1 latency stream per direction per chain)
603 # for IMIX, has self.chain_count * 2 * 4 streams
604 # (3 normal + 1 latency stream per direction per chain)
606 for port in self.port_handle:
607 streamblock[port] = []
608 stream_cfgs = [d.get_stream_configs() for d in self.generator_config.devices]
609 self.rates = [utils.to_rate_str(rate) for rate in rates]
610 for chain_id, (fwd_stream_cfg, rev_stream_cfg) in enumerate(zip(*stream_cfgs)):
611 streamblock[0].extend(self.generate_streams(self.port_handle[0],
616 if len(self.rates) > 1:
617 streamblock[1].extend(self.generate_streams(self.port_handle[1],
621 latency=bidirectional and latency))
623 for port in self.port_handle:
624 self.client.add_streams(streamblock[port], ports=port)
625 LOG.info('Created %d traffic streams for port %s.', len(streamblock[port]), port)
627 def clear_streamblock(self):
628 """Clear all streams from TRex."""
630 self.client.reset(self.port_handle)
631 LOG.info('Cleared all existing streams')
634 """Get stats from Trex."""
635 stats = self.client.get_stats()
636 return self.extract_stats(stats)
639 """Return the Trex local port MAC addresses.
641 return: a list of MAC addresses indexed by the port#
643 return [port['src_mac'] for port in self.port_info]
645 def get_port_speed_gbps(self):
646 """Return the Trex local port MAC addresses.
648 return: a list of speed in Gbps indexed by the port#
650 return [port['speed'] for port in self.port_info]
652 def clear_stats(self):
653 """Clear all stats in the traffic gneerator."""
655 self.client.clear_stats()
657 def start_traffic(self):
658 """Start generating traffic in all ports."""
659 for port, rate in zip(self.port_handle, self.rates):
660 self.client.start(ports=port, mult=rate, duration=self.config.duration_sec, force=True)
662 def stop_traffic(self):
663 """Stop generating traffic."""
664 self.client.stop(ports=self.port_handle)
666 def start_capture(self):
667 """Capture all packets on both ports that are unicast to us."""
670 # Need to filter out unwanted packets so we do not end up counting
671 # src MACs of frames that are not unicast to us
672 src_mac_list = self.get_macs()
673 bpf_filter = "ether dst %s or ether dst %s" % (src_mac_list[0], src_mac_list[1])
674 # ports must be set in service in order to enable capture
675 self.client.set_service_mode(ports=self.port_handle)
676 self.capture_id = self.client.start_capture(rx_ports=self.port_handle,
677 bpf_filter=bpf_filter)
679 def fetch_capture_packets(self):
680 """Fetch capture packets in capture mode."""
682 self.packet_list = []
683 self.client.fetch_capture_packets(capture_id=self.capture_id['id'],
684 output=self.packet_list)
686 def stop_capture(self):
687 """Stop capturing packets."""
689 self.client.stop_capture(capture_id=self.capture_id['id'])
690 self.capture_id = None
691 self.client.set_service_mode(ports=self.port_handle, enabled=False)
694 """Cleanup Trex driver."""
697 self.client.reset(self.port_handle)
698 self.client.disconnect()
700 # TRex does not like a reset while in disconnected state