Skip to content

Commit 655a5ff

Browse files
authored
Handle page select and remote access check after changing SFP target (#462)
* Handle page select and remote access check after changing SFP target Signed-off-by: Mihir Patel <[email protected]> * Addressed PR comments --------- Signed-off-by: Mihir Patel <[email protected]>
1 parent de16e50 commit 655a5ff

File tree

4 files changed

+107
-19
lines changed

4 files changed

+107
-19
lines changed

sonic_platform_base/sonic_xcvr/api/public/cmisTargetFWUpgrade.py

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
upgrade of remote target from the local target itself.
66
"""
77

8-
import struct
98
import sys
109
import traceback
1110
from ...fields import consts
@@ -44,7 +43,38 @@
4443

4544
class CmisTargetFWUpgradeAPI(CmisApi):
4645
def set_firmware_download_target_end(self, target):
47-
return self.xcvr_eeprom.write(consts.TARGET_MODE, target)
46+
"""
47+
Sets the target mode to the specified target.
48+
If the target mode is set to a remote target, then the page select byte is set to 0.
49+
Also, the remote target is then checked to ensure that its accessible.
50+
In case of any error, the target mode is restored to E0.
51+
Returns:
52+
True if the target mode is set successfully, False otherwise.
53+
"""
54+
try:
55+
if not self.xcvr_eeprom.write(consts.TARGET_MODE, target):
56+
logger.error("Failed to set target mode to {}".format(target))
57+
return self._restore_target_to_E0()
58+
if target != TARGET_E0_VALUE:
59+
if not self.xcvr_eeprom.write(consts.PAGE_SELECT_BYTE, 0):
60+
logger.error("Failed to set page select byte to {}".format(target))
61+
return self._restore_target_to_E0()
62+
if not self._is_remote_target_accessible():
63+
logger.error("Remote target {} not accessible.".format(target))
64+
return self._restore_target_to_E0()
65+
except Exception as e:
66+
logger.error("Exception occurred while setting target mode to {}: {}".format(target, repr(e)))
67+
return self._restore_target_to_E0()
68+
69+
return True
70+
71+
def get_current_target_end(self):
72+
"""
73+
Reads the target mode and returns the target mode.
74+
Returns:
75+
The target mode.
76+
"""
77+
return self.xcvr_eeprom.read(consts.TARGET_MODE)
4878

4979
"""
5080
Reads the active, inactive and server firmware version from all targets
@@ -67,14 +97,7 @@ def get_transceiver_info_firmware_versions(self):
6797
for target in TARGET_LIST:
6898
try:
6999
if not self.set_firmware_download_target_end(target):
70-
logging.error("Target mode change failed. Target: {}".format(target))
71-
continue
72-
73-
# Any register apart from the TARGET_MODE register will have the value 0xff
74-
# if the remote target is not accessible from the local target.
75-
module_type = self.get_module_type()
76-
if 'Unknown' in module_type:
77-
logging.info("Remote target {} not accessible. Skipping.".format(target))
100+
logger.error("Target mode change failed. Target: {}".format(target))
78101
continue
79102

80103
firmware_versions = super().get_transceiver_info_firmware_versions()
@@ -86,17 +109,41 @@ def get_transceiver_info_firmware_versions(self):
86109
else:
87110
return_dict.update(firmware_versions)
88111
except Exception as e:
89-
logging.error("Exception occurred while handling target {} firmware version: {}".format(target, repr(e)))
112+
logger.error("Exception occurred while handling target {} firmware version: {}".format(target, repr(e)))
90113
exc_type, exc_value, exc_traceback = sys.exc_info()
91114
msg = traceback.format_exception(exc_type, exc_value, exc_traceback)
92115
for tb_line in msg:
93116
for tb_line_split in tb_line.splitlines():
94-
logging.error(tb_line_split)
117+
logger.error(tb_line_split)
95118
continue
96119

97120
self.set_firmware_download_target_end(TARGET_E0_VALUE)
98121
return return_dict
99122

123+
def _is_remote_target_accessible(self):
124+
"""
125+
Once the target is changed to remote, any register apart from the TARGET_MODE register
126+
will have the value 0xff if the remote target is powered down.
127+
Assumption:
128+
The target mode has already been set to the desired remote target.
129+
Returns:
130+
True if the remote target is accessible from the local target, False otherwise.
131+
"""
132+
module_type = self.get_module_type()
133+
if 'Unknown' in module_type:
134+
return False
135+
136+
return True
137+
138+
def _restore_target_to_E0(self):
139+
"""
140+
Logs the error message and restores the target mode to E0.
141+
Returns:
142+
False always.
143+
"""
144+
self.xcvr_eeprom.write(consts.TARGET_MODE, TARGET_E0_VALUE)
145+
return False
146+
100147
def _convert_firmware_info_to_target_firmware_info(self, firmware_info, firmware_info_map):
101148
return_dict = {}
102149
for key, value in firmware_info_map.items():

sonic_platform_base/sonic_xcvr/fields/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@
482482
CDB_WRITE_MSG = "CdbWriteMessage"
483483

484484
#CMISTargetFWUpgrade
485+
PAGE_SELECT_BYTE = "PageSelectByte"
485486
CMIS_TARGET_SERVER_INFO = "CmisTargetServerInfo"
486487
SERVER_FW_MAGIC_BYTE = "ServerFirmwareMagicByte"
487488
SERVER_FW_CHECKSUM = "ServerFirmwareChecksum"

sonic_platform_base/sonic_xcvr/mem_maps/public/cmisTargetFWUpgrade.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def __init__(self, codes):
1919
super().__init__(codes)
2020

2121
self.CMIS_TARGET_SERVER_INFO = RegGroupField(consts.CMIS_TARGET_SERVER_INFO,
22+
NumberRegField(consts.PAGE_SELECT_BYTE, self.getaddr(0, 127), format="B", size=1, ro=False),
2223
NumberRegField(consts.SERVER_FW_MAGIC_BYTE, self.getaddr(0x3, 128), format="B", size=1),
2324
NumberRegField(consts.SERVER_FW_CHECKSUM, self.getaddr(0x3, 129), format="B", size=1),
2425
ServerFWVersionRegField(consts.SERVER_FW_VERSION, self.getaddr(0x3, 130), size=16))

tests/sonic_xcvr/test_cmisTargetFWUpgrade.py

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,42 @@ class TestCmis(object):
1414
eeprom = XcvrEeprom(reader, writer, mem_map)
1515
api = CmisTargetFWUpgradeAPI(eeprom)
1616

17-
@pytest.mark.parametrize("set_firmware_result, module_type, exception_raised", [
18-
(False, 'QSFP+ or later with CMIS', False),
19-
(True, 'Unknown', False),
20-
(True, 'QSFP+ or later with CMIS', True)
17+
@pytest.mark.parametrize("target,write_results,accessible,exception,expected_result", [
18+
(1, [False, True, True, True], True, None, False), # Failed to set target mode
19+
(1, [True, False, True, True], True, None, False), # Failed to set page select byte
20+
(1, [True, True, True, True], False, None, False), # Remote target not accessible
21+
(1, [True, True, True, True], True, Exception("Simulated exception"), False), # Exception occurred
22+
(1, [True, True, True, True], True, None, True), # All operations successful
23+
(0, [True, True, True, True], True, None, True), # Target is E0, all operations successful
24+
])
25+
def test_set_firmware_download_target_end(self, target, write_results, accessible, exception, expected_result):
26+
self.api.xcvr_eeprom.write = MagicMock()
27+
self.api.xcvr_eeprom.write.side_effect = write_results
28+
with patch('sonic_platform_base.sonic_xcvr.api.public.cmisTargetFWUpgrade.CmisTargetFWUpgradeAPI._is_remote_target_accessible', return_value=accessible):
29+
with patch('sonic_platform_base.sonic_xcvr.api.public.cmisTargetFWUpgrade.CmisTargetFWUpgradeAPI._restore_target_to_E0', return_value=False):
30+
if exception is not None:
31+
self.api.xcvr_eeprom.write.side_effect = exception
32+
33+
result = self.api.set_firmware_download_target_end(target)
34+
assert result == expected_result
35+
if result:
36+
expected_call_count = 0
37+
else:
38+
expected_call_count = 1
39+
assert self.api._restore_target_to_E0.call_count == expected_call_count
40+
41+
@pytest.mark.parametrize("set_firmware_result, exception_raised", [
42+
(False, False),
43+
(True, True)
2144
])
2245
@patch('sonic_platform_base.sonic_xcvr.api.public.cmis.CmisApi.get_transceiver_info_firmware_versions', MagicMock(side_effect=({}, Exception('error'), {})))
2346
@patch('sonic_platform_base.sonic_xcvr.api.public.cmisTargetFWUpgrade.CmisTargetFWUpgradeAPI._get_server_firmware_version', MagicMock())
2447
@patch('traceback.format_exception')
25-
def test_get_transceiver_info_firmware_versions_failure(self, mock_format_exception, set_firmware_result, module_type, exception_raised):
48+
def test_get_transceiver_info_firmware_versions_failure(self, mock_format_exception, set_firmware_result, exception_raised):
2649
expected_output = {'active_firmware': 'N/A', 'inactive_firmware': 'N/A', 'e1_active_firmware': 'N/A',\
2750
'e1_inactive_firmware': 'N/A', 'e2_active_firmware': 'N/A', 'e2_inactive_firmware': 'N/A',\
2851
'e1_server_firmware': 'N/A', 'e2_server_firmware': 'N/A'}
2952
self.api.set_firmware_download_target_end = MagicMock(return_value=set_firmware_result)
30-
self.api.get_module_type = MagicMock(return_value=module_type)
3153

3254
result = self.api.get_transceiver_info_firmware_versions()
3355
assert result == expected_output
@@ -58,12 +80,29 @@ def test_get_transceiver_info_firmware_versions_success(self, fw_info_dict, serv
5880
with patch('sonic_platform_base.sonic_xcvr.api.public.cmis.CmisApi.get_transceiver_info_firmware_versions', side_effect=fw_info_dict):
5981
with patch('sonic_platform_base.sonic_xcvr.api.public.cmisTargetFWUpgrade.CmisTargetFWUpgradeAPI._get_server_firmware_version', side_effect=server_fw_info_dict):
6082
self.api.set_firmware_download_target_end = MagicMock(return_value=True)
61-
self.api.get_module_type = MagicMock(return_value='QSFP+ or later with CMIS')
6283

6384
result = self.api.get_transceiver_info_firmware_versions()
6485
assert result == expected_output
6586
assert self.api.set_firmware_download_target_end.call_count == len(TARGET_LIST) + 1
6687

88+
@pytest.mark.parametrize("module_type, expected_result", [
89+
('Unknown', False),
90+
('QSFP+ or later with CMIS', True)
91+
])
92+
def test_is_remote_target_accessible(self, module_type, expected_result):
93+
# Mock the get_module_type method to return the parameterized module_type
94+
self.api.get_module_type = MagicMock(return_value=module_type)
95+
96+
# Call the method and check the result
97+
result = self.api._is_remote_target_accessible()
98+
assert result == expected_result
99+
100+
def test_restore_target_to_E0(self):
101+
self.api.xcvr_eeprom.write = MagicMock()
102+
assert self.api._restore_target_to_E0() == False
103+
self.api.xcvr_eeprom.write.assert_called_once()
104+
105+
67106
@pytest.mark.parametrize("magic_byte, checksum, server_fw_version_byte_array, expected", [
68107
(0, 0, (), {'server_firmware': 'N/A'}),
69108
(0, 0x98, [0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 5, 0x8d], {'server_firmware': 'N/A'}), # Magic byte is 0 but other values are valid

0 commit comments

Comments
 (0)