else:
latencies[port].min_usec = get_latency(lat['total_min'])
latencies[port].avg_usec = get_latency(lat['average'])
+ # pick up the HDR histogram if present (otherwise will raise KeyError)
+ latencies[port].hdrh = lat['hdrh']
except KeyError:
pass
def __combine_latencies(self, in_stats, results, port_handle):
- """Traverse TRex result dictionary and combines chosen latency stats."""
+ """Traverse TRex result dictionary and combines chosen latency stats.
+
+ example of latency dict returned by trex (2 chains):
+ 'latency': {256: {'err_cntrs': {'dropped': 0,
+ 'dup': 0,
+ 'out_of_order': 0,
+ 'seq_too_high': 0,
+ 'seq_too_low': 0},
+ 'latency': {'average': 26.5,
+ 'hdrh': u'HISTFAAAAEx4nJNpmSgz...bFRgxi',
+ 'histogram': {20: 303,
+ 30: 320,
+ 40: 300,
+ 50: 73,
+ 60: 4,
+ 70: 1},
+ 'jitter': 14,
+ 'last_max': 63,
+ 'total_max': 63,
+ 'total_min': 20}},
+ 257: {'err_cntrs': {'dropped': 0,
+ 'dup': 0,
+ 'out_of_order': 0,
+ 'seq_too_high': 0,
+ 'seq_too_low': 0},
+ 'latency': {'average': 29.75,
+ 'hdrh': u'HISTFAAAAEV4nJN...CALilDG0=',
+ 'histogram': {20: 261,
+ 30: 431,
+ 40: 3,
+ 50: 80,
+ 60: 225},
+ 'jitter': 23,
+ 'last_max': 67,
+ 'total_max': 67,
+ 'total_min': 20}},
+ 384: {'err_cntrs': {'dropped': 0,
+ 'dup': 0,
+ 'out_of_order': 0,
+ 'seq_too_high': 0,
+ 'seq_too_low': 0},
+ 'latency': {'average': 18.0,
+ 'hdrh': u'HISTFAAAADR4nJNpm...MjCwDDxAZG',
+ 'histogram': {20: 987, 30: 14},
+ 'jitter': 0,
+ 'last_max': 34,
+ 'total_max': 34,
+ 'total_min': 20}},
+ 385: {'err_cntrs': {'dropped': 0,
+ 'dup': 0,
+ 'out_of_order': 0,
+ 'seq_too_high': 0,
+ 'seq_too_low': 0},
+ 'latency': {'average': 19.0,
+ 'hdrh': u'HISTFAAAADR4nJNpm...NkYmJgDdagfK',
+ 'histogram': {20: 989, 30: 11},
+ 'jitter': 0,
+ 'last_max': 38,
+ 'total_max': 38,
+ 'total_min': 20}},
+ 'global': {'bad_hdr': 0, 'old_flow': 0}},
+ """
total_max = 0
average = 0
total_min = float("inf")
"""
# Trex will add the FCS field, so we need to remove 4 bytes from the l2 frame size
frame_size = int(l2frame_size) - 4
-
+ vm_param = []
if stream_cfg['vxlan'] is True:
self._bind_vxlan()
encap_level = '1'
pkt_base /= UDP(sport=random.randint(1337, 32767), dport=4789)
pkt_base /= VXLAN(vni=stream_cfg['net_vni'])
pkt_base /= Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst'])
+ # need to randomize the outer header UDP src port based on flow
+ vxlan_udp_src_fv = STLVmFlowVar(
+ name="vxlan_udp_src",
+ min_value=1337,
+ max_value=32767,
+ size=2,
+ op="random")
+ vm_param = [vxlan_udp_src_fv,
+ STLVmWrFlowVar(fv_name="vxlan_udp_src", pkt_offset="UDP.sport")]
else:
encap_level = '0'
pkt_base = Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst'])
op="inc",
step=stream_cfg['ip_addrs_step'])
- vm_param = [
+ vm_param.extend([
src_fv,
STLVmWrFlowVar(fv_name="ip_src", pkt_offset="IP:{}.src".format(encap_level)),
dst_fv,
- STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP:{}.dst".format(encap_level)),
- STLVmFixChecksumHw(l3_offset="IP:{}".format(encap_level),
- l4_offset="UDP:{}".format(encap_level),
- l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP)
- ]
+ STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP:{}.dst".format(encap_level))
+ ])
+
+ for encap in range(int(encap_level), -1, -1):
+ # Fixing the checksums for all encap levels
+ vm_param.append(STLVmFixChecksumHw(l3_offset="IP:{}".format(encap),
+ l4_offset="UDP:{}".format(encap),
+ l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP))
pad = max(0, frame_size - len(pkt_base)) * 'x'
- return STLPktBuilder(pkt=pkt_base / pad, vm=STLScVmRaw(vm_param))
+ return STLPktBuilder(pkt=pkt_base / pad,
+ vm=STLScVmRaw(vm_param, cache_size=int(self.config.cache_size)))
def generate_streams(self, port, chain_id, stream_cfg, l2frame, latency=True,
e2e=False):
"""
streams = []
pg_id, lat_pg_id = self.get_pg_id(port, chain_id)
+ if self.config.no_flow_stats:
+ LOG.info("Traffic flow statistics are disabled.")
if l2frame == 'IMIX':
for ratio, l2_frame_size in zip(IMIX_RATIOS, IMIX_L2_SIZES):
pkt = self._create_pkt(stream_cfg, l2_frame_size)
if stream_cfg['vxlan'] is True:
streams.append(STLStream(packet=pkt,
flow_stats=STLFlowStats(pg_id=pg_id,
- vxlan=True),
+ vxlan=True)
+ if not self.config.no_flow_stats else None,
mode=STLTXCont(pps=ratio)))
else:
streams.append(STLStream(packet=pkt,
- flow_stats=STLFlowStats(pg_id=pg_id),
+ flow_stats=STLFlowStats(pg_id=pg_id)
+ if not self.config.no_flow_stats else None,
mode=STLTXCont(pps=ratio)))
if latency:
if stream_cfg['vxlan'] is True:
streams.append(STLStream(packet=pkt,
flow_stats=STLFlowStats(pg_id=pg_id,
- vxlan=True),
+ vxlan=True)
+ if not self.config.no_flow_stats else None,
mode=STLTXCont()))
else:
streams.append(STLStream(packet=pkt,
- flow_stats=STLFlowStats(pg_id=pg_id),
+ flow_stats=STLFlowStats(pg_id=pg_id)
+ if not self.config.no_flow_stats else None,
mode=STLTXCont()))
# for the latency stream, the minimum payload is 16 bytes even in case of vlan tagging
# without vlan, the min l2 frame size is 64
pkt = self._create_pkt(stream_cfg, 68)
if latency:
- # TRex limitation: VXLAN skip is not supported for latency stream
- streams.append(STLStream(packet=pkt,
- flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id),
- mode=STLTXCont(pps=self.LATENCY_PPS)))
+ if self.config.no_latency_stats:
+ LOG.info("Latency flow statistics are disabled.")
+ if stream_cfg['vxlan'] is True:
+ streams.append(STLStream(packet=pkt,
+ flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id,
+ vxlan=True)
+ if not self.config.no_latency_stats else None,
+ mode=STLTXCont(pps=self.LATENCY_PPS)))
+ else:
+ streams.append(STLStream(packet=pkt,
+ flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id)
+ if not self.config.no_latency_stats else None,
+ mode=STLTXCont(pps=self.LATENCY_PPS)))
return streams
@timeout(5)
async_port=self.generator_config.zmq_pub_port)
try:
self.__connect(self.client)
+ if server_ip == '127.0.0.1':
+ config_updated = self.__check_config()
+ if config_updated or self.config.restart:
+ self.__restart()
except (TimeoutError, STLError) as e:
if server_ip == '127.0.0.1':
- try:
- self.__start_server()
- self.__connect_after_start()
- except (TimeoutError, STLError) as e:
- LOG.error('Cannot connect to TRex')
- LOG.error(traceback.format_exc())
- logpath = '/tmp/trex.log'
- if os.path.isfile(logpath):
- # Wait for TRex to finish writing error message
- last_size = 0
- for _ in xrange(self.config.generic_retry_count):
- size = os.path.getsize(logpath)
- if size == last_size:
- # probably not writing anymore
- break
- last_size = size
- time.sleep(1)
- with open(logpath, 'r') as f:
- message = f.read()
- else:
- message = e.message
- raise TrafficGeneratorException(message)
+ self.__start_local_server()
else:
raise TrafficGeneratorException(e.message)
(self.port_info[0]['speed'],
self.port_info[1]['speed']))
+ def __start_local_server(self):
+ try:
+ LOG.info("Starting TRex ...")
+ self.__start_server()
+ self.__connect_after_start()
+ except (TimeoutError, STLError) as e:
+ LOG.error('Cannot connect to TRex')
+ LOG.error(traceback.format_exc())
+ logpath = '/tmp/trex.log'
+ if os.path.isfile(logpath):
+ # Wait for TRex to finish writing error message
+ last_size = 0
+ for _ in xrange(self.config.generic_retry_count):
+ size = os.path.getsize(logpath)
+ if size == last_size:
+ # probably not writing anymore
+ break
+ last_size = size
+ time.sleep(1)
+ with open(logpath, 'r') as f:
+ message = f.read()
+ else:
+ message = e.message
+ raise TrafficGeneratorException(message)
+
def __start_server(self):
server = TRexTrafficServer()
server.run_server(self.generator_config)
+ def __check_config(self):
+ server = TRexTrafficServer()
+ return server.check_config_updated(self.generator_config)
+
+ def __restart(self):
+ LOG.info("Restarting TRex ...")
+ self.__stop_server()
+ # Wait for server stopped
+ for _ in xrange(self.config.generic_retry_count):
+ time.sleep(1)
+ if not self.client.is_connected():
+ LOG.info("TRex is stopped...")
+ break
+ self.__start_local_server()
+
+ def __stop_server(self):
+ if self.generator_config.ip == '127.0.0.1':
+ ports = self.client.get_acquired_ports()
+ LOG.info('Release ports %s and stopping TRex...', ports)
+ try:
+ if ports:
+ self.client.release(ports=ports)
+ self.client.server_shutdown()
+ except STLError as e:
+ LOG.warn('Unable to stop TRex. Error: %s', e)
+ else:
+ LOG.info('Using remote TRex. Unable to stop TRex')
+
def resolve_arp(self):
"""Resolve all configured remote IP addresses.
chain_count)
break
- self.client.set_service_mode(ports=self.port_handle, enabled=False)
+ # if the capture from the TRex console was started before the arp request step,
+ # it keeps 'service_mode' enabled, otherwise, it disables the 'service_mode'
+ if not self.config.service_mode:
+ self.client.set_service_mode(ports=self.port_handle, enabled=False)
if len(arp_dest_macs) == len(self.port_handle):
return arp_dest_macs
return None
if self.capture_id:
self.client.stop_capture(capture_id=self.capture_id['id'])
self.capture_id = None
- self.client.set_service_mode(ports=self.port_handle, enabled=False)
+ # if the capture from TRex console was started before the connectivity step,
+ # it keeps 'service_mode' enabled, otherwise, it disables the 'service_mode'
+ if not self.config.service_mode:
+ self.client.set_service_mode(ports=self.port_handle, enabled=False)
def cleanup(self):
"""Cleanup Trex driver."""
except STLError:
# TRex does not like a reset while in disconnected state
pass
+
+ def set_service_mode(self, enabled=True):
+ """Enable/disable the 'service_mode'."""
+ self.client.set_service_mode(ports=self.port_handle, enabled=enabled)