From 438b77565009e56c0947bcc8551ff3f644c2220a Mon Sep 17 00:00:00 2001
From: liora <liora@nvidia.com>
Date: Tue, 24 Jan 2023 16:05:47 +0000
Subject: [PATCH 1/5] Add support for 4600/4600C/2201 platforms with sonic
 interface names aligned to 4 instead of 8. Replace system calls to Python
 library functions when accessing Redis DB.

---
 .../ecmp_calculator/ecmp_calc.py              | 47 ++++++++++--------
 .../docker-syncd-mlnx/lib/port_utils.py       | 48 ++++++++++++++++---
 2 files changed, 69 insertions(+), 26 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 286a40fd2027..a1e12dddf2f7 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,15 +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:
+        for key in vrf_table_keys:
+            if key == self.user_vrf:
                 return True
 
         return False
@@ -289,26 +292,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 str(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 +375,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..376d3bf53bd7 100755
--- a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
+++ b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
@@ -2,25 +2,59 @@
 
 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 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 = {}
         
+        # Get lanes map
+        lanes_map = get_ports_lanes_map(config_db)
+
         # Get chip type
         chip_type = sx_get_chip_type(handle)
         
@@ -48,7 +82,9 @@ def sx_get_ports_map(handle):
             lane_index = get_lane_index(lane_bmap, ASIC_MAX_LANES[chip_type])
             assert lane_index != -1, "Failed to calculate port index"
             
-            sonic_index = label_port * ASIC_MAX_LANES[chip_type] + lane_index;
+            first_lane = label_port * ASIC_MAX_LANES[chip_type] + lane_index;
+            sonic_index = lanes_map[first_lane]
+
             sonic_interface = ETHERNET_PREFIX + str(sonic_index)    
             ports_map[logical_port] = sonic_interface
             
@@ -65,7 +101,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 +131,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):

From 03c51724bb3f2b1419e568e88650c8bfa697ebab Mon Sep 17 00:00:00 2001
From: liora <liora@nvidia.com>
Date: Mon, 30 Jan 2023 13:24:04 +0000
Subject: [PATCH 2/5] Fix review comments

---
 .../docker-syncd-mlnx/ecmp_calculator/ecmp_calc.py         | 7 +++----
 1 file changed, 3 insertions(+), 4 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 a1e12dddf2f7..2edc8ad4ffbd 100755
--- a/platform/mellanox/docker-syncd-mlnx/ecmp_calculator/ecmp_calc.py
+++ b/platform/mellanox/docker-syncd-mlnx/ecmp_calculator/ecmp_calc.py
@@ -166,9 +166,8 @@ def validate_vrf(self):
         vrf_table = Table(self.appl_db, VRF_TABLE)
         vrf_table_keys = vrf_table.getKeys()
 
-        for key in vrf_table_keys:
-            if key == self.user_vrf:
-                return True
+        if self.user_vrf in vrf_table_keys:
+            return True
 
         return False
 
@@ -314,7 +313,7 @@ def get_port_channel_name(self, port):
         lag_member_table_keys = lag_member_table.getKeys()
 
         for key in lag_member_table_keys:
-            if str(port) in key:
+            if port in key:
                 port_channel = key.split(':')[PORT_CHANNEL_IDX]
                 return port_channel
         

From cb07ba279b3eb26166594f13e18328c97c0b6b2d Mon Sep 17 00:00:00 2001
From: liora <liora@nvidia.com>
Date: Sun, 5 Feb 2023 14:09:50 +0000
Subject: [PATCH 3/5] Fix second review comments

---
 .../docker-syncd-mlnx/lib/port_utils.py       | 34 +++++++++++++++----
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
index 376d3bf53bd7..75ec5e4186a4 100755
--- a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
+++ b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
@@ -9,8 +9,6 @@
 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 get_ports_lanes_map(config_db):
     """ Get lane number of the first lane in use by port for all existing ports.
@@ -39,6 +37,30 @@ def get_ports_lanes_map(config_db):
 
     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()
+
+    rm_chip_limits_get(chip_type, limits)
+    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
     
@@ -55,8 +77,8 @@ def sx_get_ports_map(handle, config_db):
         # Get lanes map
         lanes_map = get_ports_lanes_map(config_db)
 
-        # Get chip type
-        chip_type = sx_get_chip_type(handle)
+        # 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()
@@ -79,10 +101,10 @@ def sx_get_ports_map(handle, config_db):
                 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"
             
-            first_lane = 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)    

From 69a82db3b6bde21fad53899abce530facbc433d0 Mon Sep 17 00:00:00 2001
From: liora <liora@nvidia.com>
Date: Sun, 5 Feb 2023 14:12:37 +0000
Subject: [PATCH 4/5] Remove newline

---
 platform/mellanox/docker-syncd-mlnx/lib/port_utils.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
index 75ec5e4186a4..15bd50337201 100755
--- a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
+++ b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
@@ -51,7 +51,6 @@ def get_port_max_width(handle):
 
     limits = rm_resources_t()
     modes = rm_modes_t()
-
     rm_chip_limits_get(chip_type, limits)
     max_width = limits.port_map_width_max
 

From f2bc9b013ad064fc891c7542f1ae61a655bdd1a6 Mon Sep 17 00:00:00 2001
From: liora <liora@nvidia.com>
Date: Mon, 6 Feb 2023 07:21:40 +0000
Subject: [PATCH 5/5] Fix more review comments

---
 platform/mellanox/docker-syncd-mlnx/lib/port_utils.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
index 15bd50337201..f00d22d5426c 100755
--- a/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
+++ b/platform/mellanox/docker-syncd-mlnx/lib/port_utils.py
@@ -51,7 +51,9 @@ def get_port_max_width(handle):
 
     limits = rm_resources_t()
     modes = rm_modes_t()
-    rm_chip_limits_get(chip_type, limits)
+
+    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
@@ -72,6 +74,8 @@ def sx_get_ports_map(handle, config_db):
     """         
     try:
         ports_map = {}
+        port_attributes_list = None
+        port_cnt_p = None
         
         # Get lanes map
         lanes_map = get_ports_lanes_map(config_db)