Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Independent Module - platform-daemon code to support port SI configuration per speed #3

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Changes from 13 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
ba78303
xcvrd changes to configure modules with logs
tshalvi Sep 11, 2023
f0f0489
sonic-platform-daemons changes before moving to combined codebase
tshalvi Sep 18, 2023
30bd3e4
pre-arrangement commit with temporary logs
tshalvi Sep 21, 2023
abddbca
platform-daemon code to support port SI configuration for the Indepen…
tshalvi Sep 22, 2023
f088df6
refactoring and removing redundant spaces
tshalvi Sep 22, 2023
d3f9653
removed redundant spaces
tshalvi Sep 22, 2023
62fea72
Removing redundant comments and spaces
tshalvi Sep 26, 2023
f43df10
removed redundant spaces and removed optics_si_settings.json from commit
tshalvi Sep 26, 2023
5d3b5c0
Replacing wrong print with the relevant log
tshalvi Sep 26, 2023
d186d88
removing redundant spaces and comments
tshalvi Sep 26, 2023
6f8b658
Removed redundant spaces and comments
tshalvi Sep 26, 2023
6525e33
Removed redundant spaces
tshalvi Sep 26, 2023
ad4a4e0
Removed TODOs and redudant spaces
tshalvi Sep 26, 2023
88b40b9
Fixes for existing unit tests
tshalvi Oct 1, 2023
2b4fe20
Fixes after CR with comments
tshalvi Oct 4, 2023
a247905
Removing redundant comments
tshalvi Oct 4, 2023
3ac3f1f
Removing redundant spaces
tshalvi Oct 4, 2023
ffc026f
Fixing existing tests after merging duplicated function get_cmis_appl…
tshalvi Oct 4, 2023
6b88870
Fixes after PR #2
tshalvi Oct 8, 2023
9d86e59
Fixed tuple-instead-of-list test issue
tshalvi Oct 11, 2023
c2ac376
Refactoring for the function get_cmis_application_desired()
tshalvi Oct 11, 2023
404e6ff
Removing log
tshalvi Oct 12, 2023
1a7bd24
Moving media_settings.json functionality to a separate file and updat…
tshalvi Oct 16, 2023
a78b4aa
Updating xcvrd test to work with media_settings_parser and moved the …
tshalvi Oct 17, 2023
de99228
Changed some logs in media_settings_parser from log_error to log_debug
tshalvi Oct 17, 2023
50ad9b2
Adding a docstring to describe media_settings_parser's functionality
tshalvi Oct 22, 2023
08a80ee
Added new unit test - test_get_speed_and_lane_count
tshalvi Oct 26, 2023
0690bf5
Added new unit test to meet coverage reqirement (test_is_si_per_speed…
tshalvi Oct 29, 2023
f6ec4f3
Updated CMIS module verification with the new method is_cmis_api()
tshalvi Nov 6, 2023
1ff6b55
Updated the test for get_media_settings_key() after the latest change…
tshalvi Nov 7, 2023
2b64b84
Refined Logs per CR Recommendations
tshalvi Nov 12, 2023
be29382
Tomer: modified log level of the log printed when no data is found in…
tshalvi Nov 19, 2023
dde725b
Added new unit tests for media_settings_parser.get_media_settings_val…
tshalvi Nov 20, 2023
721b29c
Added a new json for used in some unit test
tshalvi Nov 20, 2023
fac5bb2
Revert redundant changes in port_mapping (pushed by mistake)
tshalvi Nov 20, 2023
0c386aa
Fixed the indexes used for lookup in media_settings.json
tshalvi Nov 26, 2023
b5c8c0a
Added a unit test: test_get_media_settings_value()
tshalvi Nov 26, 2023
6d0142b
Added new unit test - test_load_media_settings
tshalvi Nov 26, 2023
cc91f93
Fix for load_media_settings tests
tshalvi Nov 26, 2023
03fbb09
Merge branch 'master' into code_for_CR3
tshalvi Nov 27, 2023
cef8ffc
Merge branch 'master' into code_for_CR3
tshalvi Nov 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 158 additions & 18 deletions sonic-xcvrd/xcvrd/xcvrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
from .xcvrd_utilities import sfp_status_helper
from .xcvrd_utilities import port_mapping
from .xcvrd_utilities import optics_si_parser

from sonic_platform_base.sonic_xcvr.api.public.c_cmis import CmisApi

except ImportError as e:
raise ImportError(str(e) + " - required module not found")

Expand Down Expand Up @@ -94,6 +97,8 @@
# Global chassis object based on new platform api
platform_chassis = None

LANE_SPEED_KEY_PREFIX = "speed:"

# Global logger instance for helper functions and classes
# TODO: Refactor so that we only need the logger inherited
# by DaemonXcvrd
Expand All @@ -103,6 +108,78 @@
# Helper functions =============================================================
#


def get_cmis_application_desired(api, host_lane_count, speed):
"""
Get the CMIS application code that matches the specified host side configurations

Args:
api:
XcvrApi object
host_lane_count:
Number of lanes on the host side
speed:
Integer, the port speed of the host interface

Returns:
Integer, the transceiver-specific application code
"""
app_found = False

if speed == 0 or host_lane_count == 0:
return 0

appl_code = 0
if type(api) == CmisApi:
appl_dict = api.get_application_advertisement()
for c in appl_dict.keys():
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
d = appl_dict[c]
if d.get('host_lane_count') != host_lane_count:
continue
if get_interface_speed(d.get('host_electrical_interface_id')) != speed:
continue
appl_code = c
app_found = True
result_index = appl_code & 0xf
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
break
tshalvi marked this conversation as resolved.
Show resolved Hide resolved

if app_found:
return (appl_code & 0xf)

return None


def get_interface_speed(ifname):
"""
Get the port speed from the host interface name

Args:
ifname: String, interface name

Returns:
Integer, the port speed if success otherwise 0
"""
# see HOST_ELECTRICAL_INTERFACE of sff8024.py
speed = 0
if '400G' in ifname:
speed = 400000
elif '200G' in ifname:
speed = 200000
elif '100G' in ifname or 'CAUI-4' in ifname:
speed = 100000
elif '50G' in ifname or 'LAUI-2' in ifname:
speed = 50000
elif '40G' in ifname or 'XLAUI' in ifname or 'XLPPI' in ifname:
speed = 40000
elif '25G' in ifname:
speed = 25000
elif '10G' in ifname or 'SFI' in ifname or 'XFI' in ifname:
speed = 10000
elif '1000BASE' in ifname:
speed = 1000
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
return speed


# Get physical port name


Expand Down Expand Up @@ -575,6 +652,8 @@ def check_port_in_range(range_str, physical_port):
return True
return False

def is_si_per_speed_supported(media_dict):
return LANE_SPEED_KEY_PREFIX in list(media_dict.keys())[0]

def get_media_settings_value(physical_port, key):
GLOBAL_MEDIA_SETTINGS_KEY = 'GLOBAL_MEDIA_SETTINGS'
Expand Down Expand Up @@ -611,11 +690,21 @@ def get_media_settings_value(physical_port, key):
# If there is a match in the global profile for a media type,
# fetch those values
if key[0] in media_dict:
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
return media_dict[key[0]]
elif key[0].split('-')[0] in media_dict:
return media_dict[key[0].split('-')[0]]
if is_si_per_speed_supported(media_dict[key[0]]):
if key[2] in media_dict[key[0]]:
return media_dict[key[0]][key[2]]
else:
return {}
else:
return media_dict[key[0]]
elif key[1] in media_dict:
return media_dict[key[1]]
if is_si_per_speed_supported(media_dict[key[1]]):
if key[2] in media_dict[key[1]]:
return media_dict[key[1]][key[2]]
else:
return {}
else:
return media_dict[key[1]]
elif DEFAULT_KEY in media_dict:
default_dict = media_dict[DEFAULT_KEY]

Expand All @@ -635,11 +724,21 @@ def get_media_settings_value(physical_port, key):
return {}

if key[0] in media_dict:
return media_dict[key[0]]
elif key[0].split('-')[0] in media_dict:
return media_dict[key[0].split('-')[0]]
if is_si_per_speed_supported(media_dict[key[0]]):
if key[2] in media_dict[key[0]]:
return media_dict[key[0]][key[2]]
else:
return {}
else:
return media_dict[key[0]]
elif key[1] in media_dict:
return media_dict[key[1]]
if is_si_per_speed_supported(media_dict[key[1]]):
if key[2] in media_dict[key[1]]:
return media_dict[key[1]][key[2]]
else:
return {}
else:
return media_dict[key[1]]
elif DEFAULT_KEY in media_dict:
return media_dict[DEFAULT_KEY]
elif len(default_dict) != 0:
Expand All @@ -651,7 +750,31 @@ def get_media_settings_value(physical_port, key):
return {}


def get_media_settings_key(physical_port, transceiver_dict):
def get_speed_and_lane_count(port, cfg_port_tbl):
port_speed, lane_count = -1, -1
found, port_info = cfg_port_tbl.get(port)
if found and 'speed' in dict(port_info) and 'lanes' in dict(port_info):
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
port_speed = dict(port_info).get('speed', '-1')
lanes = dict(port_info).get('lanes', '-1')
lane_count = len(lanes.split(','))
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
return port_speed, lane_count


def get_lane_speed_key(physical_port, port_speed, lane_count):
sfp = platform_chassis.get_sfp(physical_port)
api = sfp.get_xcvr_api()
speed_index = get_cmis_application_desired(api, int(lane_count), int(port_speed))
tshalvi marked this conversation as resolved.
Show resolved Hide resolved

appl_adv_dict, lane_speed_key = None, None
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
if type(api) == CmisApi:
appl_adv_dict = api.get_application_advertisement()
if speed_index is not None:
lane_speed_key = LANE_SPEED_KEY_PREFIX + (appl_adv_dict[speed_index].get('host_electrical_interface_id')).split()[0]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible that the get call would return None?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'host_electrical_interface_id' key is searched for in a dictionary returned from the EEPROM. Therefore, it is highly unlikely for this key not to be present in appl_adv_dict[speed_index] (returned from get_application_advertisement()).

Just to be on the safe side, I added some protection in case an invalid dictionary was returned from the EEPROM.


return lane_speed_key


def get_media_settings_key(physical_port, transceiver_dict, port_speed, lane_count):
sup_compliance_str = '10/40G Ethernet Compliance Code'
sup_len_str = 'Length Cable Assembly(m)'
vendor_name_str = transceiver_dict[physical_port]['manufacturer']
Expand Down Expand Up @@ -694,7 +817,9 @@ def get_media_settings_key(physical_port, transceiver_dict):
else:
media_key += '-' + '*'

return [vendor_key, media_key]
lane_speed_key = get_lane_speed_key(physical_port, port_speed, lane_count)
return [vendor_key, media_key, lane_speed_key]
tshalvi marked this conversation as resolved.
Show resolved Hide resolved


def get_media_val_str_from_dict(media_dict):
LANE_STR = 'lane'
Expand Down Expand Up @@ -741,10 +866,13 @@ def get_media_val_str(num_logical_ports, lane_dict, logical_idx):


def notify_media_setting(logical_port_name, transceiver_dict,
app_port_tbl, port_mapping):
app_port_tbl, cfg_port_tbl, port_mapping):

if not g_dict:
return

port_speed, lane_count = get_speed_and_lane_count(logical_port_name, cfg_port_tbl)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we continue if port_speed or lane_count is -1?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if port_speed or lane_count have invalid values, then lane_speed_key will be None, and therefore, if media_settings.json supports SI values per speed, then get_media_settings_value() will return an empty result, according to the updated flow of the parser. So in the case of invalid port_speed or lane_count, nothing will be posted to APP_DB during notify_media_settings().


ganged_port = False
ganged_member_num = 1

Expand All @@ -769,8 +897,10 @@ def notify_media_setting(logical_port_name, transceiver_dict,

port_name = get_physical_port_name(logical_port_name,
ganged_member_num, ganged_port)

ganged_member_num += 1
key = get_media_settings_key(physical_port, transceiver_dict)
key = get_media_settings_key(physical_port, transceiver_dict, port_speed, lane_count)
helper_logger.log_debug("Retrieving media settings for port {} using the following keys: {}".format(logical_port_name, key))
media_dict = get_media_settings_value(physical_port, key)

if len(media_dict) == 0:
Expand All @@ -780,13 +910,15 @@ def notify_media_setting(logical_port_name, transceiver_dict,
fvs = swsscommon.FieldValuePairs(len(media_dict))

index = 0
helper_logger.log_debug("Storing in Application DB the following media settings for port {}:".format(logical_port_name))
for media_key in media_dict:
if type(media_dict[media_key]) is dict:
media_val_str = get_media_val_str(num_logical_ports,
media_dict[media_key],
logical_idx)
else:
media_val_str = media_dict[media_key]
helper_logger.log_debug("{}:({},{}) ".format(index, str(media_key), str(media_val_str)))
fvs[index] = (str(media_key), str(media_val_str))
index += 1

Expand Down Expand Up @@ -1501,6 +1633,7 @@ def task_worker(self):
continue

try:
self.log_notice("Starting CMIS state machine...")
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
# CMIS state transitions
if state == self.CMIS_STATE_INSERTED:
self.port_dict[lport]['appl'] = self.get_cmis_application_desired(api,
Expand Down Expand Up @@ -1618,14 +1751,21 @@ def task_worker(self):
self.log_error("{} failed to configure laser frequency {} GHz".format(lport, freq))
else:
self.log_notice("{} configured laser frequency {} GHz".format(lport, freq))


helper_logger.log_debug("Starting to stage custom SI settings")
# Stage custom SI settings
if optics_si_parser.optics_si_present():
optics_si_dict = {}
# Apply module SI settings if applicable
lane_speed = int(speed/1000)//host_lane_count
optics_si_dict = optics_si_parser.fetch_optics_si_setting(pport, lane_speed, sfp)


helper_logger.log_debug("SI values for the connected module found in optics_si_settings.json:")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you going to keep this debug code?

Copy link
Owner Author

@tshalvi tshalvi Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this log message according to Moshe's suggestion

for key, sub_dict in optics_si_dict.items():
tshalvi marked this conversation as resolved.
Show resolved Hide resolved
helper_logger.log_debug("{}".format(key))
for sub_key, value in sub_dict.items():
helper_logger.log_debug("{}: {}".format(sub_key, str(value)))

if optics_si_dict:
self.log_notice("{}: Apply Optics SI found for Vendor: {} PN: {} lane speed: {}G".
format(lport, api.get_manufacturer(), api.get_model(), lane_speed))
Expand Down Expand Up @@ -1919,7 +2059,7 @@ def _post_port_sfp_info_and_dom_thr_to_db_once(self, port_mapping, xcvr_table_he

# Do not notify media settings during warm reboot to avoid dataplane traffic impact
if is_warm_start == False:
notify_media_setting(logical_port_name, transceiver_dict, xcvr_table_helper.get_app_port_tbl(asic_index), port_mapping)
notify_media_setting(logical_port_name, transceiver_dict, xcvr_table_helper.get_app_port_tbl(asic_index), xcvr_table_helper.get_cfg_port_tbl(asic_index), port_mapping)
transceiver_dict.clear()
else:
retry_eeprom_set.add(logical_port_name)
Expand Down Expand Up @@ -2141,7 +2281,7 @@ def task_worker(self, stopping_event, sfp_error_event):

if rc != SFP_EEPROM_NOT_READY:
post_port_dom_threshold_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_dom_threshold_tbl(asic_index))
notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(asic_index), self.port_mapping)
notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(asic_index), self.xcvr_table_helper.get_cfg_port_tbl(asic_index), self.port_mapping)
transceiver_dict.clear()
elif value == sfp_status_helper.SFP_STATUS_REMOVED:
helper_logger.log_notice("{}: Got SFP removed event".format(logical_port))
Expand Down Expand Up @@ -2340,7 +2480,7 @@ def on_add_logical_port(self, port_change_event):
self.retry_eeprom_set.add(port_change_event.port_name)
else:
post_port_dom_threshold_info_to_db(port_change_event.port_name, self.port_mapping, dom_threshold_tbl)
notify_media_setting(port_change_event.port_name, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(port_change_event.asic_id), self.port_mapping)
notify_media_setting(port_change_event.port_name, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(port_change_event.asic_id), self.xcvr_table_helper.get_cfg_port_tbl(asic_index), self.port_mapping)
else:
status = sfp_status_helper.SFP_STATUS_REMOVED if not status else status
update_port_transceiver_status_table_sw(port_change_event.port_name, status_tbl, status, error_description)
Expand All @@ -2366,7 +2506,7 @@ def retry_eeprom_reading(self):
rc = post_port_sfp_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_intf_tbl(asic_index), transceiver_dict)
if rc != SFP_EEPROM_NOT_READY:
post_port_dom_threshold_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_dom_threshold_tbl(asic_index))
notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(asic_index), self.port_mapping)
notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(asic_index), self.xcvr_table_helper.get_cfg_port_tbl(asic_index), self.port_mapping)
transceiver_dict.clear()
retry_success_set.add(logical_port)
# Update retry EEPROM set
Expand Down