Skip to content

Commit 320c11b

Browse files
Add is_lpo API (#599)
1 parent 2bcbe3a commit 320c11b

File tree

3 files changed

+147
-0
lines changed

3 files changed

+147
-0
lines changed

sonic_platform_base/sonic_xcvr/api/public/cmis.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,20 @@ class CmisApi(XcvrApi):
111111
LowPwrRequestSW = 4
112112
LowPwrAllowRequestHW = 6
113113

114+
LPO_HOST_ELECTRICAL_INTERFACE_IDS = [
115+
Sff8024.HOST_ELECTRICAL_INTERFACE[32],
116+
Sff8024.HOST_ELECTRICAL_INTERFACE[33],
117+
Sff8024.HOST_ELECTRICAL_INTERFACE[34],
118+
Sff8024.HOST_ELECTRICAL_INTERFACE[35]
119+
]
120+
121+
LPO_SM_MEDIA_INTERFACE_IDS = [
122+
Sff8024.SM_MEDIA_INTERFACE[151],
123+
Sff8024.SM_MEDIA_INTERFACE[152],
124+
Sff8024.SM_MEDIA_INTERFACE[153],
125+
Sff8024.SM_MEDIA_INTERFACE[154]
126+
]
127+
114128
# Default caching enabled; control via classmethod
115129
cache_enabled = True
116130

@@ -592,6 +606,25 @@ def is_copper(self):
592606
media_intf = self.get_module_media_type()
593607
return media_intf == "passive_copper_media_interface" if media_intf else None
594608

609+
def is_lpo(self):
610+
'''
611+
Returns True if the module is LPO, False otherwise
612+
'''
613+
appl_advt = self.get_application_advertisement()
614+
if not appl_advt:
615+
return False
616+
617+
for appl_dict in appl_advt.values():
618+
host_intf = appl_dict.get('host_electrical_interface_id')
619+
media_intf = appl_dict.get('module_media_interface_id')
620+
if (
621+
host_intf in self.LPO_HOST_ELECTRICAL_INTERFACE_IDS or
622+
media_intf in self.LPO_SM_MEDIA_INTERFACE_IDS
623+
):
624+
return True
625+
626+
return False
627+
595628
@read_only_cached_api_return
596629
def is_flat_memory(self):
597630
return self.xcvr_eeprom.read(consts.FLAT_MEM_FIELD) is not False

sonic_platform_base/sonic_xcvr/api/xcvr_api.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,3 +685,12 @@ def set_high_power_class(self, power_class, enable):
685685
bool: True if the provision succeeds, False if it fails
686686
"""
687687
raise NotImplementedError
688+
689+
def is_lpo(self):
690+
"""
691+
Check if the module is Linear Pluggable Optics (LPO)
692+
693+
Returns:
694+
A Boolean, True if the module is LPO, False otherwise
695+
"""
696+
raise NotImplementedError

tests/sonic_xcvr/test_cmis.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,111 @@ def test_is_copper(self):
201201
self.api.get_module_media_type.return_value = "sm_media_interface"
202202
assert not self.api.is_copper()
203203

204+
@pytest.mark.parametrize("mock_response, expected", [
205+
# Test case 1: No application advertisement
206+
(None, False),
207+
# Test case 2: Empty application advertisement
208+
({}, False),
209+
# Test case 3: Non-LPO host electrical interface
210+
({
211+
1: {
212+
'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', # ID 17, not LPO
213+
'module_media_interface_id': '400GBASE-DR4 (Cl 124)'
214+
}
215+
}, False),
216+
# Test case 4: LPO host electrical interface LEI-100G-PAM4-1 (ID 32)
217+
({
218+
1: {
219+
'host_electrical_interface_id': 'LEI-100G-PAM4-1', # LPO
220+
'module_media_interface_id': '100GBASE-DR (Cl 140)'
221+
}
222+
}, True),
223+
# Test case 5: LPO host electrical interface LEI-200G-PAM4-2 (ID 33)
224+
({
225+
1: {
226+
'host_electrical_interface_id': 'LEI-200G-PAM4-2', # LPO
227+
'module_media_interface_id': '200GBASE-DR2 (Clause 138)'
228+
}
229+
}, True),
230+
# Test case 6: LPO host electrical interface LEI-400G-PAM4-4 (ID 34)
231+
({
232+
1: {
233+
'host_electrical_interface_id': 'LEI-400G-PAM4-4', # LPO
234+
'module_media_interface_id': '400GBASE-DR4 (Cl 124)'
235+
}
236+
}, True),
237+
# Test case 7: LPO host electrical interface LEI-800G-PAM4-8 (ID 35)
238+
({
239+
1: {
240+
'host_electrical_interface_id': 'LEI-800G-PAM4-8', # LPO
241+
'module_media_interface_id': '800GBASE-DR8 (placeholder)'
242+
}
243+
}, True),
244+
# Test case 8: LPO SM media interface 100G-DR1-LPO (ID 151)
245+
({
246+
1: {
247+
'host_electrical_interface_id': '100GAUI-2 C2M (Annex 135G)',
248+
'module_media_interface_id': '100G-DR1-LPO' # LPO
249+
}
250+
}, True),
251+
# Test case 9: LPO SM media interface 200G-DR2-LPO (ID 152)
252+
({
253+
1: {
254+
'host_electrical_interface_id': '200GAUI-4 C2M (Annex 120E)',
255+
'module_media_interface_id': '200G-DR2-LPO' # LPO
256+
}
257+
}, True),
258+
# Test case 10: LPO SM media interface 400G-DR4-LPO (ID 153)
259+
({
260+
1: {
261+
'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)',
262+
'module_media_interface_id': '400G-DR4-LPO' # LPO
263+
}
264+
}, True),
265+
# Test case 11: LPO SM media interface 800G-DR8-LPO (ID 154)
266+
({
267+
1: {
268+
'host_electrical_interface_id': '800GAUI-16 C2M (Annex 120C)',
269+
'module_media_interface_id': '800G-DR8-LPO' # LPO
270+
}
271+
}, True),
272+
# Test case 12: Multiple applications, one with LPO
273+
({
274+
1: {
275+
'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', # Non-LPO
276+
'module_media_interface_id': '400GBASE-DR4 (Cl 124)'
277+
},
278+
2: {
279+
'host_electrical_interface_id': 'LEI-200G-PAM4-2', # LPO
280+
'module_media_interface_id': '200GBASE-DR2 (Clause 138)'
281+
}
282+
}, True),
283+
# Test case 13: Both host and media interfaces match LPO
284+
({
285+
1: {
286+
'host_electrical_interface_id': 'LEI-400G-PAM4-4', # LPO
287+
'module_media_interface_id': '400G-DR4-LPO' # LPO
288+
}
289+
}, True),
290+
# Test case 14: Application with missing keys
291+
({
292+
1: {
293+
'host_electrical_interface_id': '100GAUI-2 C2M (Annex 135G)',
294+
# Missing module_media_interface_id
295+
},
296+
2: {
297+
# Missing host_electrical_interface_id
298+
'module_media_interface_id': '100GBASE-DR (Cl 140)'
299+
}
300+
}, False),
301+
])
302+
def test_is_lpo(self, mock_response, expected):
303+
"""Test is_lpo() method with various application advertisement scenarios"""
304+
with patch.object(self.api, 'get_application_advertisement') as mock_get_app_adv:
305+
mock_get_app_adv.return_value = mock_response
306+
result = self.api.is_lpo()
307+
assert result == expected
308+
204309
@pytest.mark.parametrize("mock_response, expected", [
205310
(False, False)
206311
])

0 commit comments

Comments
 (0)