From 6a12ca9332be9ff91ec6b0a52e9d857375a78751 Mon Sep 17 00:00:00 2001 From: mssonicbld <79238446+mssonicbld@users.noreply.github.com> Date: Wed, 22 Feb 2023 22:14:09 +0800 Subject: [PATCH] [Mellanox] [ECMP calculator] Add support for 4600/4600C/2201 platforms with different interface naming method (#13814) (#13931) --- .../ecmp_calculator/ecmp_calc.py | 48 ++++++----- .../docker-syncd-mlnx/lib/port_utils.py | 83 ++++++++++++++++--- 2 files changed, 99 insertions(+), 32 deletions(-) diff --git a/platform/mellanox/docker-syncd-mlnx/ecmp_calculator/ecmp_calc.py b/platform/mellanox/docker-syncd-mlnx/ecmp_calculator/ecmp_calc.py index 280275529b73..2fbcef86993e 100755 --- a/platform/mellanox/docker-syncd-mlnx/ecmp_calculator/ecmp_calc.py +++ b/platform/mellanox/docker-syncd-mlnx/ecmp_calculator/ecmp_calc.py @@ -23,14 +23,18 @@ PORT, VPORT, VLAN, SX_ENTRY_NOT_FOUND from packet_scheme import PACKET_SCHEME from port_utils import sx_get_ports_map, is_lag +from swsscommon.swsscommon import ConfigDBConnector, DBConnector, Table IP_VERSION_IPV4 = 1 IP_VERSION_IPV6 = 2 -PORT_CHANNEL_IDX = 1 +PORT_CHANNEL_IDX = 0 VRF_NAME_IDX = 1 IP_VERSION_MAX_MASK_LEN = {IP_VERSION_IPV4: 32, IP_VERSION_IPV6: 128} +APPL_DB_NAME = 'APPL_DB' INTF_TABLE = 'INTF_TABLE' +VRF_TABLE = 'VRF_TABLE' +LAG_MEMBER_TABLE = 'LAG_MEMBER_TABLE' HASH_CALC_PATH = '/usr/bin/sx_hash_calculator' HASH_CALC_INPUT_FILE = "/tmp/hash_calculator_input.json" HASH_CALC_OUTPUT_FILE = "/tmp/hash_calculator_output.json" @@ -113,6 +117,8 @@ def __init__(self): self.egress_ports = [] self.debug = False + self.config_db = ConfigDBConnector() + self.appl_db = DBConnector(APPL_DB_NAME, 0) self.open_sdk_connection() self.init_ports_map() self.get_active_vrids() @@ -137,7 +143,7 @@ def debug_print(self, *args, **kwargs): print(*args, **kwargs) def init_ports_map(self): - self.ports_map = sx_get_ports_map(self.handle) + self.ports_map = sx_get_ports_map(self.handle, self.config_db) def validate_ingress_port(self, interface): if interface not in self.ports_map.values(): @@ -156,16 +162,12 @@ def validate_args(self, interface, packet, vrf, debug): if not self.validate_vrf(): raise ValueError("VRF validation failed: VRF {} does not exist".format(self.user_vrf)) - def validate_vrf(self): - query_output = exec_cmd(['/usr/bin/redis-cli', '-n', '0', 'keys','*VRF*']).strip() - if not query_output: - return False + def validate_vrf(self): + vrf_table = Table(self.appl_db, VRF_TABLE) + vrf_table_keys = vrf_table.getKeys() - vrf_entries= query_output.split('\n') - for entry in vrf_entries: - vrf = entry.split(':')[VRF_NAME_IDX] - if vrf == self.user_vrf: - return True + if self.user_vrf in vrf_table_keys: + return True return False @@ -289,26 +291,30 @@ def print_egress_port(self): def is_port_bind_to_user_vrf(self, port_type, port, vlan_id = 0): if port_type == PORT: # INTF_TABLE:Ethernet0 - entry = '{}:{}'.format(INTF_TABLE, port) + entry = '{}'.format(port) elif port_type == VPORT: # INTF_TABLE:Ethernet0.300 - entry = '{}:{}.{}'.format(INTF_TABLE, port, vlan_id) + entry = '{}.{}'.format(port, vlan_id) elif port_type == VLAN: # INTF_TABLE:Vlan300 - entry = '{}:Vlan{}'.format(INTF_TABLE, vlan_id) + entry = 'Vlan{}'.format(vlan_id) + + vrf_table = Table(self.appl_db, INTF_TABLE) + (_, port_vrf) = vrf_table.hget(entry, 'vrf_name') - port_vrf = exec_cmd(['/usr/bin/redis-cli', '-n', '0', 'hget', entry, 'vrf_name']) if self.user_vrf == port_vrf.strip(): return True return False # Get port-channel name for given port-channel member port - def get_port_channel_name(self, port): - query_output = exec_cmd(['/usr/bin/redis-cli', '-n', '0', 'keys','*LAG_MEMBER_TABLE*']) - for line in query_output.split('\n'): - if str(port) in line: - port_channel = line.split(':')[PORT_CHANNEL_IDX] + def get_port_channel_name(self, port): + lag_member_table = Table(self.appl_db, LAG_MEMBER_TABLE) + lag_member_table_keys = lag_member_table.getKeys() + + for key in lag_member_table_keys: + if port in key: + port_channel = key.split(':')[PORT_CHANNEL_IDX] return port_channel raise KeyError("Failed to get port-channel name for interface {}".format(port)) @@ -368,7 +374,7 @@ def get_lag_member(self, logical, flood_case = False): member_index = self.get_lag_member_index(len(lag_members), flood_case) lag_member = lag_members[member_index] - self.debug_print("Lag member from which trafic will egress: {}".format(lag_member)) + self.debug_print("Lag members: {}\nLag member from which trafic will egress: {}".format(lag_members, lag_member)) return lag_member def call_hash_calculator(self, input_dict): diff --git a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py index cce63298cf9a..f00d22d5426c 100755 --- a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py +++ b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py @@ -2,27 +2,86 @@ from python_sdk_api.sx_api import * import inspect +import re DEVICE_ID = 1 SWITCH_ID = 0 +PORT_TABLE = 'PORT' +FIRST_LANE_INDEX = 0 ETHERNET_PREFIX = 'Ethernet' -ASIC_MAX_LANES = {SX_CHIP_TYPE_SPECTRUM: 4, SX_CHIP_TYPE_SPECTRUM2: 4, - SX_CHIP_TYPE_SPECTRUM3: 8, SX_CHIP_TYPE_SPECTRUM4: 8} -def sx_get_ports_map(handle): +def get_ports_lanes_map(config_db): + """ Get lane number of the first lane in use by port for all existing ports. + + Args: + config_db (ConfigDBConnector): Config DB connector + + Returns: + dict: key is lane number of the first lane in use by port, value is SONiC port index (124 for Ethernet124) + """ + lanes_map = {} + config_db.connect() + + ports_table = config_db.get_table(PORT_TABLE) + if ports_table is None: + raise Exception("Can't read {} table".format(PORT_TABLE)) + + ports_table_keys = config_db.get_keys(PORT_TABLE) + for port in ports_table_keys: + port_data = ports_table.get(port) + if port_data is not None: + lanes = port_data.get('lanes') + first_lane = lanes.split(',')[FIRST_LANE_INDEX] + port_idx = re.sub(r"\D", "", port) + lanes_map[int(first_lane)] = int(port_idx) + + return lanes_map + +def get_port_max_width(handle): + """ Get max number of lanes in port according to chip type + + Args: + handle (sx_api_handle_t): SDK handle + + Returns: + int: max lanes in port + """ + # Get chip type + chip_type = sx_get_chip_type(handle) + + limits = rm_resources_t() + modes = rm_modes_t() + + rc = rm_chip_limits_get(chip_type, limits) + sx_check_rc(rc) + max_width = limits.port_map_width_max + + # SPC2 ports have 8 lanes but SONiC is using 4 + if chip_type == SX_CHIP_TYPE_SPECTRUM2: + max_width = 4 + + return max_width + +def sx_get_ports_map(handle, config_db): """ Get ports map from SDK logical index to SONiC index Args: handle (sx_api_handle_t): SDK handle - + config_db (ConfigDBConnector): Config DB connector + Returns: - dict : Dictionary of ports indices. Key is SDK logical index, value is SONiC index (4 for Ethernet4) + dict: key is SDK logical index, value is SONiC index (4 for Ethernet4) """ try: ports_map = {} + port_attributes_list = None + port_cnt_p = None - # Get chip type - chip_type = sx_get_chip_type(handle) + # Get lanes map + lanes_map = get_ports_lanes_map(config_db) + + # Get max number of lanes in port + port_max_width = get_port_max_width(handle) # Get ports count port_cnt_p = new_uint32_t_p() @@ -45,10 +104,12 @@ def sx_get_ports_map(handle): continue # Calculate sonic index (sonic index=4 for Ethernet4) - lane_index = get_lane_index(lane_bmap, ASIC_MAX_LANES[chip_type]) + lane_index = get_lane_index(lane_bmap, port_max_width) assert lane_index != -1, "Failed to calculate port index" - sonic_index = label_port * ASIC_MAX_LANES[chip_type] + lane_index; + first_lane = label_port * port_max_width + lane_index; + sonic_index = lanes_map[first_lane] + sonic_interface = ETHERNET_PREFIX + str(sonic_index) ports_map[logical_port] = sonic_interface @@ -65,7 +126,7 @@ def sx_get_chip_type(handle): handle (sx_api_handle_t): SDK handle Returns: - sx_chip_types_t : Chip type + sx_chip_types_t: Chip type """ try: device_info_cnt_p = new_uint32_t_p() @@ -95,7 +156,7 @@ def get_lane_index(lane_bmap, max_lanes): max_lanes (int): Max lanes in module Returns: - int : index of the first bit set to 1 in lane_bmap + int: index of the first bit set to 1 in lane_bmap """ for lane_idx in range(0, max_lanes): if (lane_bmap & 0x1 == 1):