[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
|
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,14 +163,10 @@ 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:
|
|
||||||
vrf = entry.split(':')[VRF_NAME_IDX]
|
|
||||||
if vrf == self.user_vrf:
|
|
||||||
return True
|
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):
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user