def create_ipv4_traffic(session, name, source, destination, bidirectional=False, fullmesh=False, pkt_size=64, pkt_count=None, duration=None, rate_percent=100, start_delay=0, dscp_list=None, lossless_prio_list=None, ecn_capable=False): """ Create an IPv4 traffic item on IxNetwork. Args: session (obj): IxNetwork session object. name (str): Name of traffic item source (obj list): Source endpoints - list of IxNetwork vport objects. destination (obj list): Destination endpoints - list of IxNetwork vport objects. bidirectional (bool): if traffic item is bidirectional. fullmesh (bool): if traffic pattern is full mesh pkt_size (int): Packet size. pkt_count (int): Packet count. duration (int): Traffic duration in second (positive integer only!) rate_percent (int): Percentage of line rate. start_delay (int): Start delay in second. dscp_list(int list): List of DSCPs. lossless_prio_list (int list): List of lossless priorities. ecn_capable (bool): If packets can get ECN marked. Returns: The created traffic item or None in case of error. """ ixnetwork = session.Ixnetwork if fullmesh: traffic_item = ixnetwork.Traffic.TrafficItem.add( Name=name, SrcDestMesh='fullMesh', TrafficType='ipv4') if source != destination: logger.error( 'Source and destination must be same under full mesh traffic pattern' ) return None else: traffic_item.EndpointSet.add(FullyMeshedEndpoints=destination) else: traffic_item = ixnetwork.Traffic.TrafficItem.add( Name=name, BiDirectional=bidirectional, TrafficType='ipv4') traffic_item.EndpointSet.add(Sources=source, Destinations=destination) traffic_config = traffic_item.ConfigElement.find()[0] traffic_config.FrameRate.update(Type='percentLineRate', Rate=rate_percent) traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' traffic_config.FrameSize.FixedSize = pkt_size if pkt_count is not None and duration is not None: logger.error('You can only specify either pkt_count or duration') return None if pkt_count is not None: traffic_config.TransmissionControl.update(Type='fixedFrameCount', FrameCount=pkt_count) elif duration is not None: if type(duration) != int or duration <= 0: logger.error( 'Invalid duration value {} (positive integer only)'.format( duration)) return None else: traffic_config.TransmissionControl.update(Type='fixedDuration', Duration=duration) else: traffic_config.TransmissionControl.update(Type='continuous') if start_delay > 0: traffic_config.TransmissionControl.update( StartDelayUnits='nanoseconds', StartDelay=start_delay * (10**9)) if dscp_list is not None and len(dscp_list) > 0: phb_field = traffic_item.ConfigElement.find().Stack.find( 'IPv4').Field.find(DisplayName='Default PHB') phb_field.ActiveFieldChoice = True phb_field.ValueType = 'valueList' phb_field.ValueList = dscp_list """ Set ECN bits to 10 (ECN capable) """ if ecn_capable: phb_field = traffic_item.ConfigElement.find().Stack.find('IPv4').Field.\ find(FieldTypeId='ipv4.header.priority.ds.phb.defaultPHB.unused') phb_field.ActiveFieldChoice = True phb_field.ValueType = 'singleValue' phb_field.SingleValue = 2 if lossless_prio_list is not None and len(lossless_prio_list) > 0: eth_stack = traffic_item.ConfigElement.find()[0].Stack.find( DisplayName='Ethernet II') pfc_queue = eth_stack.Field.find(DisplayName='PFC Queue') pfc_queue.ValueType = 'valueList' pfc_queue.ValueList = lossless_prio_list traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] """ Push ConfigElement settings down to HighLevelStream resources """ traffic_item.Generate() return traffic_item
def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, duration=None, start_delay=0, global_pause=False, pause_prio_list=[]): """ Create a pause traffic item. Args: session (obj): IxNetwork session object. name (str): Name of traffic item. source (obj list): Source endpoints - list of IxNetwork vport objects. pkt_per_sec (int): Packets per second. pkt_count (int): Packet count. duration (int): Traffic duration in second (positive integer only!). start_delay (int): Start delay in second. global_pause (bool): If the generated packets are global pause (IEEE 802.3X PAUSE). pause_prio_list: list of priorities to pause. Only valid when global_pause is False. Returns: The created traffic item or None if any errors happen. """ if pause_prio_list is not None: for prio in pause_prio_list: if prio < 0 or prio > 7: logger.error( 'Invalid pause priorities {}'.format(pause_prio_list)) return None ixnetwork = session.Ixnetwork traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, BiDirectional=False, TrafficType='raw') # Since PFC packets will not be forwarded by the switch, so # destinations are actually not used. traffic_item.EndpointSet.add(Sources=source.Protocols.find(), Destinations=source.Protocols.find()) traffic_config = traffic_item.ConfigElement.find()[0] traffic_config.FrameRate.update(Type='framesPerSecond', Rate=pkt_per_sec) traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' traffic_config.FrameSize.FixedSize = 64 if pkt_count is not None and duration is not None: logger.error('You can only specify either pkt_count or duration') return None if pkt_count is not None: traffic_config.TransmissionControl.update(Type='fixedFrameCount', FrameCount=pkt_count) elif duration is not None: if type(duration) != int or duration <= 0: logger.error( 'Invalid duration value {} (positive integer only)'.format( duration)) return None else: traffic_config.TransmissionControl.update(Type='fixedDuration', Duration=duration) else: traffic_config.TransmissionControl.update(Type='continuous') if start_delay > 0: traffic_config.TransmissionControl.update( StartDelayUnits='nanoseconds', StartDelay=start_delay * (10**9)) # Add PFC header pfc_stack_obj = __create_pkt_hdr(ixnetwork=ixnetwork, traffic_item=traffic_item, pkt_hdr_to_add='^PFC PAUSE \(802.1Qbb\)', append_to_stack='Ethernet II') # Construct global pause and PFC packets. if global_pause: __set_global_pause_fields(pfc_stack_obj) else: __set_pfc_fields(pfc_stack_obj, pause_prio_list) # Remove Ethernet header. traffic_item.ConfigElement.find()[0].Stack.\ find(DisplayName="Ethernet II").Remove() traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] # Push ConfigElement settings down to HighLevelStream resources. traffic_item.Generate() return traffic_item
def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, test_dscp_list, bg_dscp_list, exp_dur, start_delay=START_DELAY, test_traffic_pause_expected=True, send_pause_frame=True): """ Run a PFC experiment. 1. IXIA sends test traffic and background traffic from tx_port. 2. IXIA sends PFC pause frames from rx_port to pause priorities. 3. Background traffic should not be interruped - all background traffic will be received at the rx_port. 4. No test traffic will be received at the rx_port when pause priority is equal to test traffic priority. Note: PFC pause frames should always be dropped, regardless of their pause priorities. Args: session (IxNetwork Session object): IxNetwork session. dut (object): Ansible instance of SONiC device under test (DUT). tx_port (object Ixia vport): IXIA port to transmit traffic. rx_port (object Ixia vport): IXIA port to receive traffic. port_bw (int): bandwidth (in Mbps) of tx_port and rx_port. test_prio_list (list of integers): PFC priorities of test traffic and PFC pause frames. test_dscp_list (list of integers): DSCP values of test traffic. bg_dscp_list (list of integers): DSCP values of background traffic. exp_dur (integer): experiment duration in second. start_delay (float): approximated initial delay to start the traffic. test_traffic_pause_expected (bool): Do you expect test traffic to be stopped? If yes, this should be true; false otherwise. send_pause_frame (bool): True/False depending on whether you want to send pause frame Rx port or not. Returns: This function returns nothing. """ # Disable DUT's PFC watchdog. dut.shell('sudo pfcwd stop') vlan_subnet = get_vlan_subnet(dut) pytest_assert(vlan_subnet is not None, "Fail to get Vlan subnet information") gw_addr = vlan_subnet.split('/')[0] # One for sender and the other one for receiver. vlan_ip_addrs = get_addrs_in_subnet(vlan_subnet, 2) topo_receiver = create_topology(session=session, name="Receiver", ports=list(rx_port), ip_start=vlan_ip_addrs[0], ip_incr_step='0.0.0.1', gw_start=gw_addr, gw_incr_step='0.0.0.0') # Assumption: Line rate percentage of background data traffic # is equal to Line rate percentage of test data traffic. pytest_assert(2 * RATE_PERCENTAGE <= 100, "Value of RATE_PERCENTAGE should not be more than 50!") topo_sender = create_topology(session=session, name="Sender", ports=list(tx_port), ip_start=vlan_ip_addrs[1], ip_incr_step='0.0.0.1', gw_start=gw_addr, gw_incr_step='0.0.0.0') start_protocols(session) test_traffic = create_ipv4_traffic(session=session, name='Test Data Traffic', source=topo_sender, destination=topo_receiver, pkt_size=DATA_PKT_SIZE, duration=exp_dur, rate_percent=RATE_PERCENTAGE, start_delay=start_delay, dscp_list=test_dscp_list, lossless_prio_list=test_prio_list) bg_priority_list = [b for b in range(8) if b not in test_prio_list] background_traffic = create_ipv4_traffic( session=session, name='Background Data Traffic', source=topo_sender, destination=topo_receiver, pkt_size=DATA_PKT_SIZE, duration=exp_dur, rate_percent=RATE_PERCENTAGE, start_delay=start_delay, dscp_list=bg_dscp_list, lossless_prio_list=bg_priority_list) # Pause time duration (in second) for each PFC pause frame. pause_dur_per_pkt = 65535 * 64 * 8.0 / (port_bw * 1000000) # Do not specify duration here as we want it keep running. if send_pause_frame: pfc_traffic = create_pause_traffic(session=session, name='PFC Pause Storm', source=rx_port, pkt_per_sec=1.1 / pause_dur_per_pkt, start_delay=0, global_pause=False, pause_prio_list=test_prio_list) start_traffic(session) # Wait for test and background traffic to finish. time.sleep(exp_dur + start_delay + 1) # Capture traffic statistics. flow_statistics = get_traffic_statistics(session) logger.info(flow_statistics) exp_tx_bytes = (exp_dur * port_bw * 1000000 * (RATE_PERCENTAGE / 100.0)) / 8 for row_number, flow_stat in enumerate(flow_statistics.Rows): tx_frames = int(flow_stat['Tx Frames']) rx_frames = int(flow_stat['Rx Frames']) rx_bytes = int(flow_stat['Rx Bytes']) tolerance_ratio = rx_bytes / exp_tx_bytes if 'Test' in flow_stat['Traffic Item']: if test_traffic_pause_expected: pytest_assert(tx_frames > 0 and rx_frames == 0, "Test traffic should be fully paused") else: pytest_assert(tx_frames > 0 and tx_frames == rx_frames, "Test traffic packets should not be dropped") if ((tolerance_ratio < TOLERANCE_THRESHOLD) or (tolerance_ratio > 1)): logger.error("Expected Tx/Rx = %s actual Rx = %s" % (exp_tx_bytes, rx_bytes)) logger.error("tolerance_ratio = %s" % (tolerance_ratio)) pytest_assert( False, "expected % of packets not received at the RX port") elif 'PFC' in flow_stat['Traffic Item']: pytest_assert(tx_frames > 0 and rx_frames == 0, "PFC packets should be dropped") else: pytest_assert(tx_frames > 0 and tx_frames == rx_frames, "Background traffic should not be impacted") if ((tolerance_ratio < TOLERANCE_THRESHOLD) or (tolerance_ratio > 1)): logger.error("Expected Tx/Rx = %s actual Rx = %s" % (exp_tx_bytes, rx_bytes)) logger.error("tolerance_ratio = %s" % (tolerance_ratio)) pytest_assert( False, "expected % of packets not received at the RX port") stop_traffic(session)