[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:
parent
331b97e2aa
commit
fd122efb40
@ -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
|
||||
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 @@ class EcmpCalc:
|
||||
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 @@ class EcmpCalc:
|
||||
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 @@ class EcmpCalc:
|
||||
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 @@ class EcmpCalc:
|
||||
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 @@ class EcmpCalc:
|
||||
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):
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user