ptf/config/port_configer.py (320 lines of code) (raw):

from collections import OrderedDict from ptf import config from sai_utils import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] from sai_thrift.sai_adapter import * from typing import TYPE_CHECKING from data_module.port import Port from typing import Dict, List from data_module.port_config import PortConfig if TYPE_CHECKING: from sai_test_base import SaiHelperBase class PortConfiger(object): """ Class use to make all the basic configurations. """ def __init__(self, test_obj: 'SaiHelperBase') -> None: """ Init the Port configer. Args: test_obj: the test object """ self.test_obj = test_obj self.client = test_obj.client def create_bridge_ports(self, bridge_id, port_list: List['Port']): """ Create bridge ports base on port_list. Args: bridge_id: bridge object id port_list: port list oid Returns: list: bridge port list """ print("Create bridge ports...") bp_list = [] for index, item in enumerate(port_list): port_bp = sai_thrift_create_bridge_port( self.client, bridge_id=bridge_id, port_id=item.oid, type=SAI_BRIDGE_PORT_TYPE_PORT, admin_state=True) bp_list.append(port_bp) item.bridge_port_oid = port_bp #print("create bridge port list : {}".format(bp_list)) self.set_test_bridge_port_attr(bp_list) return bp_list def get_bridge_port_all_attribute(self, bridge_port_id): ''' Gets all the attrbute from bridge port. Args: bridge_port_id: bridge port object id Returns: dict: bridge attributes ''' # Cannot get those three attributes from sai_thrift_get_bridge_port_attribute # ingress_filtering=True, # egress_filtering=True, # isolation_group=True # sai_thrift_get_bridge_port_attribute( # self.client, # bridge_port_oid=bridge_port_id, # # ingress_filtering=True, # 11 # # egress_filtering=True # 12 # ) # Todo check the attribute before use attr = sai_thrift_get_bridge_port_attribute( self.client, bridge_port_oid=bridge_port_id, type=True, port_id=True, tagging_mode=True, # 2 vlan_id=True, # 3 rif_id=True, # 4 tunnel_id=True, # 5 bridge_id=True, fdb_learning_mode=True, max_learned_addresses=True, # 8 fdb_learning_limit_violation_packet_action=True, # 9 admin_state=True # Cannot get those three # ingress_filtering=True, # egress_filtering=True, # isolation_group=True ) self.test_obj.assertEqual(self.test_obj.status(), SAI_STATUS_SUCCESS) return attr def load_default_active_1q_bridge_ports(self): """ Loads default 1q bridge ports, bind to port object and set as class attribute. Needs the following class attributes: self.default_1q_bridge - default_1q_bridge oid self.active_ports_no - number of active ports self.portX objects for all active ports Sets the following class attributes: self.default_1q_bridge_port_list - list of all 1q bridge port objects self.portX_bp - objects for all 1q bridge ports """ bridge_id = self.test_obj.default_1q_bridge default_1q_bridge_port_list = self.get_all_bridge_ports(bridge_id) #try to binding the bridge port with the port index here print("Assign bridge to port objects...") port_list = self.test_obj.active_port_obj_list active_1q_bridge_ports = [] for index in range(0, len(port_list)): for bp in default_1q_bridge_port_list: attr = self.get_bridge_port_all_attribute(bp) port_id = port_list[index].oid if port_id == attr['port_id']: port_list[index].bridge_port_oid = bp active_1q_bridge_ports.append(bp) break return active_1q_bridge_ports def get_all_bridge_ports(self, bridge_id): """ Loads default 1q bridge ports. /* Get bridge ports in default 1Q bridge * By default, there will be (m_portCount + m_systemPortCount) number of SAI_BRIDGE_PORT_TYPE_PORT * ports and one SAI_BRIDGE_PORT_TYPE_1Q_ROUTER port. The former type of * ports will be removed. */ vector<sai_object_id_t> bridge_port_list(m_portCount + m_systemPortCount + 1); """ print("Get bridge ports...") bridge_size = self.test_obj.system_port_no + self.test_obj.active_ports_no + 1 attr = sai_thrift_get_bridge_attribute( self.client, bridge_oid=bridge_id, port_list=sai_thrift_object_list_t( idlist=[], count=bridge_size)) default_1q_bridge_port_list = attr['port_list'].idlist self.test_obj.assertEqual(self.test_obj.status(), SAI_STATUS_SUCCESS) return default_1q_bridge_port_list def remove_all_bridge_ports(self): """ Remove bridge ports (bridge will not be removed). """ print("Remove all bridge ports...") bp_ports = self.test_obj.def_bridge_port_list removed_list = [] for index, port in enumerate(bp_ports): sai_thrift_remove_bridge_port(self.client, port) #print("Removed bridge port {}".format(port)) removed_list.append(port) for bridge_id in removed_list: self.test_obj.def_bridge_port_list.remove(bridge_id) print("Removed bridge {}, left {}".format(len(removed_list), len(self.test_obj.def_bridge_port_list))) self.test_obj.assertEqual(self.test_obj.status(), SAI_STATUS_SUCCESS) def reset_1q_bridge_ports(self): ''' Reset all the 1Q bridge ports. Needs the following class attributes: self.default_1q_bridge - default_1q_bridge oid self.active_ports_no - number of active ports self.portX objects for all active ports ''' #In case the bridge port will be initalized by default, clear them bridge_id = self.test_obj.default_1q_bridge self.test_obj.def_bridge_port_list \ = self.get_all_bridge_ports(bridge_id) self.remove_all_bridge_ports() # self.test_obj.def_bridge_port_list \ # = self.create_bridge_ports( # bridge_id, self.test_obj.active_port_obj_list) def remove_1q_bridge_port(self, default_1q_bridge_port_list): ''' Removes all the bridge ports. ''' for index in range(0, len(default_1q_bridge_port_list)): port_bp = getattr(self.test_obj, 'port%s_bp' % index) sai_thrift_remove_bridge_port(self.client, port_bp) delattr(self.test_obj, 'port%s_bp' % index) # Local methods def get_port_id_list(self, port_obj_list:List[Port]): port_id_list = [] for item in port_obj_list: port_id_list.append(item.oid) return port_id_list def create_bridge_ports_by_bridge_and_ports(self, bridge_id, port_list: List['Port']): """ Create bridge ports base on port_list. Args: bridge_id: bridge object id port_list: port list oid Returns: list: bridge port list """ print("Create bridge ports...") bp_list = [] for index, item in enumerate(port_list): port_bp = sai_thrift_create_bridge_port( self.client, bridge_id=bridge_id, port_id=item.oid, type=SAI_BRIDGE_PORT_TYPE_PORT, admin_state=True) bp_list.append(port_bp) item.bridge_port_oid = port_bp return bp_list def create_host_intf(self, port_list: List['Port'], trap_group=None): """ Create host interface. Steps: 1. create host table entry 2. create host interface trap 3. set host interface base on the port_config.int (this file contains the lanes, name and index information.) Args: ports_config: port configs, which is got from the local config. trap_group: host interface trap group(optional) port_list: port list Returns: host_intf_table_id hostif_list """ print("Create Host intfs...") host_intf_table_id = sai_thrift_create_hostif_table_entry( self.client, type=SAI_HOSTIF_TABLE_ENTRY_TYPE_WILDCARD, channel_type=SAI_HOSTIF_TABLE_ENTRY_CHANNEL_TYPE_NETDEV_PHYSICAL_PORT) sai_thrift_create_hostif_trap( self.client, trap_type=SAI_HOSTIF_TRAP_TYPE_TTL_ERROR, packet_action=SAI_PACKET_ACTION_TRAP, trap_group=trap_group, trap_priority=0) hostif_list = [None]*len(port_list) for index, item in enumerate(port_list): try: hostif = sai_thrift_create_hostif( self.client, type=SAI_HOSTIF_TYPE_NETDEV, obj_id=item.oid, name=item.config.name) sai_thrift_set_hostif_attribute( self.client, hostif_oid=hostif, oper_status=False) hostif_list[index] = hostif item.host_itf_id = hostif except BaseException as e: print("Cannot create hostif, error : {}".format(e)) return host_intf_table_id, hostif_list def create_port_hostif_by_port_config_ini(self, port_list: List['Port'], trap_group=None): print("Create Host intfs...") host_intf_table_id = sai_thrift_create_hostif_table_entry( self.client, type=SAI_HOSTIF_TABLE_ENTRY_TYPE_WILDCARD, channel_type=SAI_HOSTIF_TABLE_ENTRY_CHANNEL_TYPE_NETDEV_PHYSICAL_PORT) sai_thrift_create_hostif_trap( self.client, trap_type=SAI_HOSTIF_TRAP_TYPE_TTL_ERROR, packet_action=SAI_PACKET_ACTION_TRAP, trap_group=trap_group, trap_priority=0) hostif_list = [None]*len(port_list) for index, port in enumerate(port_list): try: hostif = sai_thrift_create_hostif( self.client, type=SAI_HOSTIF_TYPE_NETDEV, obj_id=port.oid, name=port.config.name) sai_thrift_set_hostif_attribute( self.client, hostif_oid=hostif, oper_status=False) hostif_list[index] = hostif port.host_itf_id = hostif # print("Create hostitf: name:{} port hardIdx: {} port lane: {}".format( # port.config.name, port.port_index, port.config.lanes) # ) except BaseException as e: print("Cannot create hostif, error : {}".format(e)) return host_intf_table_id, hostif_list def get_default_1q_bridge(self): """ Get defaule 1Q bridge. Returns: default_1q_bridge_id """ print("Get default 1Q bridge...") def_attr = sai_thrift_get_switch_attribute( self.client, default_1q_bridge_id=True) return def_attr['default_1q_bridge_id'] def generate_port_obj_list_by_interface_config(self): """ Get device port numbers from ptf config. Those port number should map to the remote DUT port base on the configuration file. Following the sequence of the parameters: --interface '0-16@eth0' --interface '0-18@eth1' item[0]=16, item[1]=18 Returns: list: port numbers """ dev_port_list = [] for index, item in enumerate(config['interfaces']): device, port, eth = item # device, the device index, will be used in multi devices # Port, local port index # eth, eth name self.test_obj.active_port_obj_list[index].dev_port_index = port self.test_obj.active_port_obj_list[index].dev_port_eth = eth dev_port_list.append(port) return dev_port_list def get_port_default_lane(self) -> List[Port]: """ Get Port default lane. Returns: port_list """ port_obj_list_with_lane: List[Port] = [] # the active number must match the actual account, or might return null port_list = sai_thrift_object_list_t( idlist=[], count=self.test_obj.active_ports_no) p_list = sai_thrift_get_switch_attribute(self.client, port_list=port_list) for index, item in enumerate(p_list['port_list'].idlist): port: Port = Port(oid=item, port_index=index) temp_list = sai_thrift_object_list_t( count=self.test_obj.active_ports_no) attr = sai_thrift_get_port_attribute( self.client, port_oid=port.oid, hw_lane_list=temp_list) port.default_lane_list = attr['hw_lane_list'].uint32list port_obj_list_with_lane.append(port) return port_obj_list_with_lane def get_lane_sorted_port_list(self, port_obj_list_with_lane: List[Port]): """ Get the port list sorted by lanes name(defined in config_db.json). This method will cut the ports list base on the lanes. i.e. If device has 64 ports but just define 32 in lanes, then just generate 32 ports Args: port_obj_list_with_lane: port obj with default lane info Returns: port_list """ port_id_list = [] self.test_obj.active_port_obj_list = self.sort_port_list_by_config(self.test_obj.ports_config, port_obj_list_with_lane) for item in self.test_obj.active_port_obj_list: port_id_list.append(item.oid) print("Base on lanes config file[{}], init {} ports from {} device ports" .format("configdb.json", len(self.test_obj.active_port_obj_list), len(port_obj_list_with_lane) )) return port_id_list def sort_port_list_by_config(self, ports_config: List[PortConfig], port_list: List[Port]): """ Sort the port list base on the config_db.json. This method will match the default_lane_list in the port object with the lane defined in port config for a ordered port list. This method will create the port list base on the mini number of interfaces from command parameters or config_db.json Attrs: ports_config: port config, which gets from the config_db.json port_list: port list """ sorted_port_list: List[Port] = [] for index, item in enumerate(ports_config): # index: interface name, item: PortConfig lane_list = item.lanes for port in port_list: if lane_list == port.default_lane_list: sorted_port_list.append(port) port.config = ports_config[index] break return sorted_port_list def remove_bridge_port(self): """ Remove bridge ports base on the active port list.(bridge will not be removed). Bridge relation will be removed from port as well. """ print("Remove bridge ports...") bp_ports = self.test_obj.def_bridge_port_list for index, port in enumerate(bp_ports): sai_thrift_remove_bridge_port(self.client, port) #print("Removed bridge port {}".format(port)) if self.test_obj.active_port_obj_list[index].bridge_port_oid != port: print("WARN! BUG! Bridge not as expected, not equals to port record.") self.test_obj.active_port_obj_list[index].bridge_port_oid = None self.test_obj.def_bridge_port_list.remove(port) self.test_obj.assertEqual(self.test_obj.status(), SAI_STATUS_SUCCESS) def remove_host_inf(self, host_intf_table_id, hostif_list): """ Remove host interface. Steps: 2. remove host interface 1. remove host table entry Args: host_intf_table_id hostif_list """ for _, hostif in enumerate(hostif_list): sai_thrift_remove_hostif(self.client, hostif) sai_thrift_remove_hostif_table_entry(self.client, host_intf_table_id) def set_port_attribute(self, port_list: List['Port']): """ Turn on port admin state Args: post_list: post list """ print("Set port...") for index, port in enumerate(port_list): #self.log_port_state(port, index) sai_thrift_set_port_attribute( self.client, port_oid=port.oid, mtu=port.config.mtu, fec_mode=self.get_fec_mode(port), speed=port.config.speed) def turn_up_and_get_checked_ports(self, port_list: List['Port']): ''' Method to turn up the ports. In case some device not init the port after start the switch. Args: port_list - list of all active port objects Return: Port list for testing. ''' # For brcm devices, need to init and setup the ports at once after start the switch. retries = 10 down_port_list = [] test_port_list:List[Port] = [] print("Turn up ports...") for index, port in enumerate(port_list): if not port.dev_port_index and index != 0: print("Skip turn up port {} , local index {} id {} name{}.".format( index, port.port_index, port.oid, port.config.name)) continue test_port_list.append(port) sai_thrift_set_port_attribute( self.client, port_oid=port.oid, admin_state=True) for index, port in enumerate(test_port_list): port_attr = sai_thrift_get_port_attribute( self.client, port.oid, oper_status=True) print("Turn up port {}".format(index)) port_up = True if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP: port_up = False for num_of_tries in range(retries): port_attr = sai_thrift_get_port_attribute( self.client, port.oid, oper_status=True) if port_attr['oper_status'] == SAI_PORT_OPER_STATUS_UP: port_up = True break time.sleep(3) self.log_port_state(port, index) print("port {} , local index {} id {} is not up, status: {}. Retry. Reset Admin State.".format( index, port.port_index, port.oid, port_attr['oper_status'])) sai_thrift_set_port_attribute( self.client, port_oid=port.oid, admin_state=True) if not port_up: down_port_list.append(index) if down_port_list: print("Ports {} are down after retries.".format(down_port_list)) return test_port_list def get_fec_mode(self, port: Port): ''' get fec mode from config_db.json RETURN: int: SAI_PORT_FEC_MODE_X ''' fec_change = { 'None': SAI_PORT_FEC_MODE_NONE, 'rs': SAI_PORT_FEC_MODE_RS, 'fc': SAI_PORT_FEC_MODE_FC, } return fec_change[port.config.fec] def log_port_state(self, port:Port, index): print("port index:{} hardIdx: {} ptf_dev_idx: {} name:{} config lane:{} mtu:{} fec:{} speed:{}".format( index, port.port_index, port.dev_port_index, port.config.name, port.config.lanes, port.config.mtu, self.get_fec_mode(port), port.config.speed)) # Method to compatiable with test structure def set_test_port_attr(self, port_list): for index, oid in enumerate(port_list): setattr(self.test_obj, 'port%s' % index, oid) def set_test_bridge_port_attr(self, bridge_port_list): for index, oid in enumerate(bridge_port_list): setattr(self.test_obj, 'port%s_bp' % index, oid)