Skip to content

Commit

Permalink
Fix VDM freeze and unfreeze needed for PM stats collection (#402)
Browse files Browse the repository at this point in the history
* In order to collect PM stats without loss of data following vdm freeze and unfreeze request needs to be followed.

VDM freeze
PM stats collection
VDM unfreeze

* Signed-off-by: Jaganathan Anbalagan <[email protected]>

Addressing PR comments.
Removed VDM statistics freeze/unfreeze performed inside standard APIs.
It should be performed by xcvrd explicitly.

* test code correction

* code coverage
  • Loading branch information
jaganbal-a authored Nov 29, 2023
1 parent cb80f17 commit 2efe97e
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 17 deletions.
46 changes: 37 additions & 9 deletions sonic_platform_base/sonic_xcvr/api/public/c_cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,43 @@ def set_tx_power(self, tx_power):
time.sleep(1)
return status

def freeze_vdm_stats(self):
'''
This function freeze all the vdm statistics reporting registers.
When raised by the host, causes the module to freeze and hold all
reported statistics reporting registers (minimum, maximum and
average values)in Pages 24h-27h.
Returns True if the provision succeeds and False incase of failure.
'''
return self.xcvr_eeprom.write(consts.VDM_CONTROL, VDM_FREEZE)

def get_vdm_freeze_status(self):
'''
This function reads and returns the vdm Freeze done status.
Returns True if the vdm stats freeze is successful and False if not freeze.
'''
return self.xcvr_eeprom.read(consts.VDM_FREEZE_DONE)

def unfreeze_vdm_stats(self):
'''
This function unfreeze all the vdm statistics reporting registers.
When freeze is ceased by the host, releases the freeze request, allowing the
reported minimum, maximum and average values to update again.
Returns True if the provision succeeds and False incase of failure.
'''
return self.xcvr_eeprom.write(consts.VDM_CONTROL, VDM_UNFREEZE)

def get_vdm_unfreeze_status(self):
'''
This function reads and returns the vdm unfreeze status.
Returns True if the vdm stats unfreeze is successful and False if not unfreeze.
'''
return self.xcvr_eeprom.read(consts.VDM_UNFREEZE_DONE)

def get_pm_all(self):
'''
This function returns the PMs reported in Page 34h and 35h in OIF C-CMIS document
Expand All @@ -163,14 +200,6 @@ def get_pm_all(self):
SOPROC: unit in krad/s
MER: unit in dB
'''
# When raised by the host, causes the module to freeze and hold all
# reported statistics reporting registers (minimum, maximum and
# average values)in Pages 24h-27h.
# When ceased by the host, releases the freeze request, allowing the
# reported minimum, maximum and average values to update again.
self.xcvr_eeprom.write(consts.VDM_CONTROL, VDM_FREEZE)
time.sleep(1)
self.xcvr_eeprom.write(consts.VDM_CONTROL, VDM_UNFREEZE)
PM_dict = dict()

rx_bits_pm = self.xcvr_eeprom.read(consts.RX_BITS_PM)
Expand Down Expand Up @@ -256,7 +285,6 @@ def get_pm_all(self):
PM_dict['rx_mer_max'] = self.xcvr_eeprom.read(consts.RX_MAX_MER_PM)
return PM_dict


def get_transceiver_info(self):
"""
Retrieves transceiver info of this SFP
Expand Down
8 changes: 0 additions & 8 deletions sonic_platform_base/sonic_xcvr/api/public/cmisVDM.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,6 @@ def get_vdm_allpage(self):
return None
VDM_START_PAGE = 0x20
vdm = dict()
# When raised by the host, causes the module to freeze and hold all
# reported statistics reporting registers (minimum, maximum and
# average values)in Pages 24h-27h.
# When ceased by the host, releases the freeze request, allowing the
# reported minimum, maximum and average values to update again.
self.xcvr_eeprom.write(consts.VDM_CONTROL, VDM_FREEZE)
time.sleep(1)
self.xcvr_eeprom.write(consts.VDM_CONTROL, VDM_UNFREEZE)
vdm_flag_page = self.xcvr_eeprom.read_raw(VDM_FLAG_PAGE * PAGE_SIZE + PAGE_OFFSET, PAGE_SIZE)
for page in range(VDM_START_PAGE, VDM_START_PAGE + vdm_page_supported_raw + 1):
vdm_current_page = self.get_vdm_page(page, vdm_flag_page)
Expand Down
4 changes: 4 additions & 0 deletions sonic_platform_base/sonic_xcvr/fields/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,10 @@
VDM_SUPPORTED = "VdmSupported"
VDM_SUPPORTED_PAGE = "VdmSupportedPage"
VDM_CONTROL = "VdmControl"
VDM_STATUS = "VdmStatus"
VDM_FREEZE_DONE = "VdmFreezeDone"
VDM_UNFREEZE_DONE = "VdmUnfreezeDone"

MEDIA_LANE_FEC_PM = "Media Lane FEC Performance Monitoring"
MEDIA_LANE_LINK_PM = "Media Lane Link Performance Monitoring"
RX_BITS_PM = "rxBitsPm"
Expand Down
4 changes: 4 additions & 0 deletions sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,10 @@ def __init__(self, codes):
*(RegBitField("Bit%d" % (bit), bit) for bit in range (0, 2))
),
NumberRegField(consts.VDM_CONTROL, self.getaddr(0x2f, 144), size=1, ro=False),
NumberRegField(consts.VDM_STATUS, self.getaddr(0x2f, 145),
RegBitField(consts.VDM_UNFREEZE_DONE, 6),
RegBitField(consts.VDM_FREEZE_DONE, 7),
),
)

self.TRANS_CONFIG = RegGroupField(consts.TRANS_CONFIG_FIELD,
Expand Down
55 changes: 55 additions & 0 deletions sonic_platform_base/sonic_xcvr/sfp_optoe_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,61 @@ def get_transceiver_pm(self):
api = self.get_xcvr_api()
return api.get_transceiver_pm() if api is not None else None

def freeze_vdm_stats(self):
'''
This function freeze all the vdm statistics reporting registers.
When raised by the host, causes the module to freeze and hold all
reported statistics reporting registers (minimum, maximum and
average values)in Pages 24h-27h.
Returns True if the provision succeeds and False incase of failure.
'''
api = self.get_xcvr_api()
try:
return api.freeze_vdm_stats() if api is not None else False
except (NotImplementedError, AttributeError):
return False

def unfreeze_vdm_stats(self):
'''
This function unfreeze all the vdm statistics reporting registers.
When freeze is ceased by the host, releases the freeze request, allowing the
reported minimum, maximum and average values to update again.
Returns True if the provision succeeds and False incase of failure.
'''
api = self.get_xcvr_api()
try:
return api.unfreeze_vdm_stats() if api is not None else False
except (NotImplementedError, AttributeError):
return False


def get_vdm_freeze_status(self):
'''
This function reads and returns the vdm Freeze done status.
Returns True if the vdm stats freeze is successful and False if not freeze.
'''
api = self.get_xcvr_api()
try:
return api.get_vdm_freeze_status() if api is not None else False
except (NotImplementedError, AttributeError):
return False

def get_vdm_unfreeze_status(self):
'''
This function reads and returns the vdm unfreeze status.
Returns True if the vdm stats unfreeze is successful and False if not unfreeze.
'''
api = self.get_xcvr_api()
try:
return api.get_vdm_unfreeze_status() if api is not None else False
except (NotImplementedError, AttributeError):
return False


def get_rx_los(self):
api = self.get_xcvr_api()
if api is not None:
Expand Down
41 changes: 41 additions & 0 deletions tests/sonic_xcvr/test_ccmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,3 +624,44 @@ def test_get_transceiver_pm(self, mock_response, expected):
self.api.get_pm_all.return_value = mock_response
result = self.api.get_transceiver_pm()
assert result == expected

@pytest.mark.parametrize("mock_response, expected", [
(0, 0),
(1, 1),
])
def test_get_vdm_freeze_status(self, mock_response, expected):
self.api.xcvr_eeprom.read = MagicMock()
self.api.xcvr_eeprom.read.return_value = mock_response
result = self.api.get_vdm_freeze_status()
assert result == expected

@pytest.mark.parametrize("mock_response, expected", [
(0, 0),
(1, 1),
])
def test_get_vdm_unfreeze_status(self, mock_response, expected):
self.api.xcvr_eeprom.read = MagicMock()
self.api.xcvr_eeprom.read.return_value = mock_response
result = self.api.get_vdm_unfreeze_status()
assert result == expected

@pytest.mark.parametrize("mock_response, expected", [
(0, 0),
(1, 1),
])
def test_freeze_vdm_stats(self, mock_response, expected):
self.api.xcvr_eeprom.write = MagicMock()
self.api.xcvr_eeprom.write.return_value = mock_response
result = self.api.freeze_vdm_stats()
assert result == expected

@pytest.mark.parametrize("mock_response, expected", [
(0, 0),
(1, 1),
])
def test_unfreeze_vdm_stats(self, mock_response, expected):
self.api.xcvr_eeprom.write = MagicMock()
self.api.xcvr_eeprom.write.return_value = mock_response
result = self.api.unfreeze_vdm_stats()
assert result == expected

81 changes: 81 additions & 0 deletions tests/sonic_xcvr/test_sfp_optoe_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from mock import MagicMock
from mock import patch
import pytest
from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase
from sonic_platform_base.sonic_xcvr.api.public.c_cmis import CCmisApi
from sonic_platform_base.sonic_xcvr.api.public.cmis import CmisApi
from sonic_platform_base.sonic_xcvr.mem_maps.public.c_cmis import CCmisMemMap
from sonic_platform_base.sonic_xcvr.xcvr_eeprom import XcvrEeprom
from sonic_platform_base.sonic_xcvr.codes.public.cmis import CmisCodes

class TestSfpOptoeBase(object):

codes = CmisCodes
mem_map = CCmisMemMap(codes)
reader = MagicMock(return_value=None)
writer = MagicMock()
eeprom = XcvrEeprom(reader, writer, mem_map)
sfp_optoe_api = SfpOptoeBase()
ccmis_api = CCmisApi(eeprom)
cmis_api = CmisApi(eeprom)

@pytest.mark.parametrize("mock_response1, mock_response2, expected", [
(0, ccmis_api, 0),
(1, ccmis_api, 1),
(None, None, False),
(None, cmis_api, False),
])
def test_freeze_vdm_stats(self, mock_response1, mock_response2, expected):
self.sfp_optoe_api.get_xcvr_api = MagicMock()
self.sfp_optoe_api.get_xcvr_api.return_value = mock_response2
self.ccmis_api.freeze_vdm_stats = MagicMock()
self.ccmis_api.freeze_vdm_stats.return_value = mock_response1

result = self.sfp_optoe_api.freeze_vdm_stats()
assert result == expected

@pytest.mark.parametrize("mock_response1, mock_response2, expected", [
(0, ccmis_api, 0),
(1, ccmis_api, 1),
(None, None, False),
(None, cmis_api, False),
])
def test_unfreeze_vdm_stats(self, mock_response1, mock_response2, expected):
self.sfp_optoe_api.get_xcvr_api = MagicMock()
self.sfp_optoe_api.get_xcvr_api.return_value = mock_response2
self.ccmis_api.unfreeze_vdm_stats = MagicMock()
self.ccmis_api.unfreeze_vdm_stats.return_value = mock_response1

result = self.sfp_optoe_api.unfreeze_vdm_stats()
assert result == expected

@pytest.mark.parametrize("mock_response1, mock_response2, expected", [
(0, ccmis_api, 0),
(1, ccmis_api, 1),
(None, None, False),
(None, cmis_api, False),
])
def test_get_vdm_freeze_status(self, mock_response1, mock_response2, expected):
self.sfp_optoe_api.get_xcvr_api = MagicMock()
self.sfp_optoe_api.get_xcvr_api.return_value = mock_response2
self.ccmis_api.get_vdm_freeze_status = MagicMock()
self.ccmis_api.get_vdm_freeze_status.return_value = mock_response1

result = self.sfp_optoe_api.get_vdm_freeze_status()
assert result == expected

@pytest.mark.parametrize("mock_response1, mock_response2, expected", [
(0, ccmis_api, 0),
(1, ccmis_api, 1),
(None, None, False),
(None, cmis_api, False),
])
def test_get_vdm_unfreeze_status(self, mock_response1, mock_response2, expected):
self.sfp_optoe_api.get_xcvr_api = MagicMock()
self.sfp_optoe_api.get_xcvr_api.return_value = mock_response2
self.ccmis_api.get_vdm_unfreeze_status = MagicMock()
self.ccmis_api.get_vdm_unfreeze_status.return_value = mock_response1

result = self.sfp_optoe_api.get_vdm_unfreeze_status()
assert result == expected

0 comments on commit 2efe97e

Please sign in to comment.