[Mellanox] [ECMP calculator] Add support for 4600/4600C/2201 platforms with different interface naming method (#13814)

- Why I did it
Add support for systems 4600/4600C/2201 that are using sonic interface names aligned to 4 instead of 8 (which is the max number of lanes per port).
Improve DB access calls, now we use Python library functions.

- How I did it
Use addition information taken from Config DB in order to create map from SDK logical index to sonic interface name.

- How to verify it
Run ECMP calculator on 4600, 4600C and 2201 platforms.
This commit is contained in:
Lior Avramov 2023-02-21 08:52:51 +02:00 committed by GitHub
parent 331b97e2aa
commit fd122efb40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 32 deletions

View File

@ -23,14 +23,18 @@ from ecmp_calc_sdk import sx_open_sdk_connection, sx_get_active_vrids, sx_router
PORT, VPORT, VLAN, SX_ENTRY_NOT_FOUND PORT, VPORT, VLAN, SX_ENTRY_NOT_FOUND
from packet_scheme import PACKET_SCHEME from packet_scheme import PACKET_SCHEME
from port_utils import sx_get_ports_map, is_lag from port_utils import sx_get_ports_map, is_lag
from swsscommon.swsscommon import ConfigDBConnector, DBConnector, Table
IP_VERSION_IPV4 = 1 IP_VERSION_IPV4 = 1
IP_VERSION_IPV6 = 2 IP_VERSION_IPV6 = 2
PORT_CHANNEL_IDX = 1 PORT_CHANNEL_IDX = 0
VRF_NAME_IDX = 1 VRF_NAME_IDX = 1
IP_VERSION_MAX_MASK_LEN = {IP_VERSION_IPV4: 32, IP_VERSION_IPV6: 128} IP_VERSION_MAX_MASK_LEN = {IP_VERSION_IPV4: 32, IP_VERSION_IPV6: 128}
APPL_DB_NAME = 'APPL_DB'
INTF_TABLE = 'INTF_TABLE' INTF_TABLE = 'INTF_TABLE'
VRF_TABLE = 'VRF_TABLE'
LAG_MEMBER_TABLE = 'LAG_MEMBER_TABLE'
HASH_CALC_PATH = '/usr/bin/sx_hash_calculator' HASH_CALC_PATH = '/usr/bin/sx_hash_calculator'
HASH_CALC_INPUT_FILE = "/tmp/hash_calculator_input.json" HASH_CALC_INPUT_FILE = "/tmp/hash_calculator_input.json"
HASH_CALC_OUTPUT_FILE = "/tmp/hash_calculator_output.json" HASH_CALC_OUTPUT_FILE = "/tmp/hash_calculator_output.json"
@ -113,6 +117,8 @@ class EcmpCalc:
self.egress_ports = [] self.egress_ports = []
self.debug = False self.debug = False
self.config_db = ConfigDBConnector()
self.appl_db = DBConnector(APPL_DB_NAME, 0)
self.open_sdk_connection() self.open_sdk_connection()
self.init_ports_map() self.init_ports_map()
self.get_active_vrids() self.get_active_vrids()
@ -137,7 +143,7 @@ class EcmpCalc:
print(*args, **kwargs) print(*args, **kwargs)
def init_ports_map(self): 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): def validate_ingress_port(self, interface):
if interface not in self.ports_map.values(): if interface not in self.ports_map.values():
@ -157,15 +163,11 @@ class EcmpCalc:
raise ValueError("VRF validation failed: VRF {} does not exist".format(self.user_vrf)) raise ValueError("VRF validation failed: VRF {} does not exist".format(self.user_vrf))
def validate_vrf(self): def validate_vrf(self):
query_output = exec_cmd(['/usr/bin/redis-cli', '-n', '0', 'keys','*VRF*']).strip() vrf_table = Table(self.appl_db, VRF_TABLE)
if not query_output: vrf_table_keys = vrf_table.getKeys()
return False
vrf_entries= query_output.split('\n') if self.user_vrf in vrf_table_keys:
for entry in vrf_entries: return True
vrf = entry.split(':')[VRF_NAME_IDX]
if vrf == self.user_vrf:
return True
return False return False
@ -289,15 +291,17 @@ class EcmpCalc:
def is_port_bind_to_user_vrf(self, port_type, port, vlan_id = 0): def is_port_bind_to_user_vrf(self, port_type, port, vlan_id = 0):
if port_type == PORT: if port_type == PORT:
# INTF_TABLE:Ethernet0 # INTF_TABLE:Ethernet0
entry = '{}:{}'.format(INTF_TABLE, port) entry = '{}'.format(port)
elif port_type == VPORT: elif port_type == VPORT:
# INTF_TABLE:Ethernet0.300 # INTF_TABLE:Ethernet0.300
entry = '{}:{}.{}'.format(INTF_TABLE, port, vlan_id) entry = '{}.{}'.format(port, vlan_id)
elif port_type == VLAN: elif port_type == VLAN:
# INTF_TABLE:Vlan300 # 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(): if self.user_vrf == port_vrf.strip():
return True return True
@ -305,10 +309,12 @@ class EcmpCalc:
# Get port-channel name for given port-channel member port # Get port-channel name for given port-channel member port
def get_port_channel_name(self, port): def get_port_channel_name(self, port):
query_output = exec_cmd(['/usr/bin/redis-cli', '-n', '0', 'keys','*LAG_MEMBER_TABLE*']) lag_member_table = Table(self.appl_db, LAG_MEMBER_TABLE)
for line in query_output.split('\n'): lag_member_table_keys = lag_member_table.getKeys()
if str(port) in line:
port_channel = line.split(':')[PORT_CHANNEL_IDX] for key in lag_member_table_keys:
if port in key:
port_channel = key.split(':')[PORT_CHANNEL_IDX]
return port_channel return port_channel
raise KeyError("Failed to get port-channel name for interface {}".format(port)) raise KeyError("Failed to get port-channel name for interface {}".format(port))
@ -368,7 +374,7 @@ class EcmpCalc:
member_index = self.get_lag_member_index(len(lag_members), flood_case) member_index = self.get_lag_member_index(len(lag_members), flood_case)
lag_member = lag_members[member_index] 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 return lag_member
def call_hash_calculator(self, input_dict): def call_hash_calculator(self, input_dict):

View File

@ -2,27 +2,86 @@
from python_sdk_api.sx_api import * from python_sdk_api.sx_api import *
import inspect import inspect
import re
DEVICE_ID = 1 DEVICE_ID = 1
SWITCH_ID = 0 SWITCH_ID = 0
PORT_TABLE = 'PORT'
FIRST_LANE_INDEX = 0
ETHERNET_PREFIX = 'Ethernet' 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 ports map from SDK logical index to SONiC index """ 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: Args:
handle (sx_api_handle_t): SDK handle handle (sx_api_handle_t): SDK handle
Returns: Returns:
dict : Dictionary of ports indices. Key is SDK logical index, value is SONiC index (4 for Ethernet4) 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: key is SDK logical index, value is SONiC index (4 for Ethernet4)
""" """
try: try:
ports_map = {} ports_map = {}
port_attributes_list = None
port_cnt_p = None
# Get chip type # Get lanes map
chip_type = sx_get_chip_type(handle) 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 # Get ports count
port_cnt_p = new_uint32_t_p() port_cnt_p = new_uint32_t_p()
@ -45,10 +104,12 @@ def sx_get_ports_map(handle):
continue continue
# Calculate sonic index (sonic index=4 for Ethernet4) # 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" 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) sonic_interface = ETHERNET_PREFIX + str(sonic_index)
ports_map[logical_port] = sonic_interface ports_map[logical_port] = sonic_interface
@ -65,7 +126,7 @@ def sx_get_chip_type(handle):
handle (sx_api_handle_t): SDK handle handle (sx_api_handle_t): SDK handle
Returns: Returns:
sx_chip_types_t : Chip type sx_chip_types_t: Chip type
""" """
try: try:
device_info_cnt_p = new_uint32_t_p() 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 max_lanes (int): Max lanes in module
Returns: 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): for lane_idx in range(0, max_lanes):
if (lane_bmap & 0x1 == 1): if (lane_bmap & 0x1 == 1):