From 203fdf147fa098f33b9755a29757e56b3eefd0db Mon Sep 17 00:00:00 2001 From: mihirpat1 <112018033+mihirpat1@users.noreply.github.com> Date: Tue, 25 Apr 2023 09:31:40 -0700 Subject: [PATCH 01/24] SFF-8472: Fix tx_disable_channel to avoid write to read-only bit (#364) * SFF-8472: Fix tx_disable_channel to avoid write to read-only bit Signed-off-by: Mihir Patel * Fixed test failure seen in test_api * SFF-8472: Fix tx_disable_channel to avoid write to read-only bit Signed-off-by: Mihir Patel --------- Signed-off-by: Mihir Patel --- .../sonic_xcvr/api/public/sff8472.py | 15 +-------------- tests/sonic_xcvr/test_sff8472.py | 2 +- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8472.py b/sonic_platform_base/sonic_xcvr/api/public/sff8472.py index 3ccfcfb0e..4c025a712 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/sff8472.py +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8472.py @@ -247,20 +247,7 @@ def tx_disable(self, tx_disable): return self.xcvr_eeprom.write(consts.TX_DISABLE_SELECT_FIELD, tx_disable) def tx_disable_channel(self, channel, disable): - channel_state = self.get_tx_disable_channel() - if channel_state is None or channel_state == "N/A": - return False - - for i in range(self.NUM_CHANNELS): - mask = (1 << i) - if not (channel & mask): - continue - if disable: - channel_state |= mask - else: - channel_state &= ~mask - - return self.xcvr_eeprom.write(consts.TX_DISABLE_FIELD, channel_state) + return self.tx_disable(disable) if channel != 0 else True def is_flat_memory(self): return not self.xcvr_eeprom.read(consts.PAGING_SUPPORT_FIELD) diff --git a/tests/sonic_xcvr/test_sff8472.py b/tests/sonic_xcvr/test_sff8472.py index 7831fcf06..35d2519ec 100644 --- a/tests/sonic_xcvr/test_sff8472.py +++ b/tests/sonic_xcvr/test_sff8472.py @@ -40,8 +40,8 @@ def test_api(self): self.api.get_rx_power() self.reader.return_value = bytearray([0xFF]) self.api.tx_disable(True) - self.reader.return_value = None self.api.tx_disable_channel(0x5, True) + self.reader.return_value = None self.api.is_flat_memory() self.api.get_temperature_support() self.api.get_voltage_support() From 89a7ad839ab1aff98611212d38c9f3ce1b7f7a0d Mon Sep 17 00:00:00 2001 From: mihirpat1 <112018033+mihirpat1@users.noreply.github.com> Date: Wed, 3 May 2023 06:51:34 -0700 Subject: [PATCH 02/24] Modify sfputil show fwversion to include build version for active/inactive FW version fields (#367) * Modify sfputil show fwversion to display FW version using CMD 100h Signed-off-by: Mihir Patel * Improved diff coverage --------- Signed-off-by: Mihir Patel --- sonic_platform_base/sonic_xcvr/api/public/cmis.py | 10 ++++++++-- tests/sonic_xcvr/test_cmis.py | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 7d6f2e883..23cfdb756 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -1286,10 +1286,16 @@ def get_module_fw_info(self): factory_image = '%d.%d.%d' % (rpl[74], rpl[75], ((rpl[76] << 8) | rpl[77])) txt += 'Factory Image Version: %s\n' %factory_image + ActiveFirmware = 'N/A' + InactiveFirmware = 'N/A' if ImageARunning == 1: RunningImage = 'A' + ActiveFirmware = ImageA + InactiveFirmware = ImageB elif ImageBRunning == 1: RunningImage = 'B' + ActiveFirmware = ImageB + InactiveFirmware = ImageA else: RunningImage = 'N/A' if ImageACommitted == 1: @@ -1300,8 +1306,8 @@ def get_module_fw_info(self): CommittedImage = 'N/A' txt += 'Running Image: %s\n' % (RunningImage) txt += 'Committed Image: %s\n' % (CommittedImage) - txt += 'Active Firmware: {}\n'.format(self.get_module_active_firmware()) - txt += 'Inactive Firmware: {}\n'.format(self.get_module_inactive_firmware()) + txt += 'Active Firmware: {}\n'.format(ActiveFirmware) + txt += 'Inactive Firmware: {}\n'.format(InactiveFirmware) else: txt += 'Reply payload check code error\n' return {'status': False, 'info': txt, 'result': None} diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index 1f106197c..9827537f7 100644 --- a/tests/sonic_xcvr/test_cmis.py +++ b/tests/sonic_xcvr/test_cmis.py @@ -1082,6 +1082,8 @@ def test_get_module_level_flag(self, mock_response, expected): ((None, 1, [0] * 128), {'status': False, 'info': "", 'result': 0}), ((128, None, [0] * 128), {'status': False, 'info': "", 'result': 0}), ((128, 0, [0] * 128), {'status': False, 'info': "", 'result': None}), + ((110, 1, [3, 3, 2, 2, 3, 183] + [0] * 104), {'status': True, 'info': "", 'result': None}), + ((110, 1, [48, 3, 2, 2, 3, 183] + [0] * 104), {'status': True, 'info': "", 'result': None}), ]) def test_get_module_fw_info(self, mock_response, expected): self.api.cdb = MagicMock() From fc5d5f005851983ab6fcaedea1e72012f3470645 Mon Sep 17 00:00:00 2001 From: snider-nokia <76123698+snider-nokia@users.noreply.github.com> Date: Thu, 4 May 2023 11:28:01 -0400 Subject: [PATCH 03/24] Prevent VDM dictionary related KeyError when a transceiver module is pulled while a bulk get method is interrogating said module (#360) --- .../sonic_xcvr/api/public/c_cmis.py | 121 +++++++++--------- 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py index 6929d2614..e0f6908a8 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py @@ -352,14 +352,14 @@ def get_transceiver_bulk_status(self): ======================================================================== """ trans_dom = super(CCmisApi,self).get_transceiver_bulk_status() - trans_dom['bias_xi'] = self.vdm_dict['Modulator Bias X/I [%]'][1][0] - trans_dom['bias_xq'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][0] - trans_dom['bias_xp'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][0] - trans_dom['bias_yi'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][0] - trans_dom['bias_yq'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][0] - trans_dom['bias_yp'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][0] - trans_dom['cd_shortlink'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][0] try: + trans_dom['bias_xi'] = self.vdm_dict['Modulator Bias X/I [%]'][1][0] + trans_dom['bias_xq'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][0] + trans_dom['bias_xp'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][0] + trans_dom['bias_yi'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][0] + trans_dom['bias_yq'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][0] + trans_dom['bias_yp'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][0] + trans_dom['cd_shortlink'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][0] trans_dom['cd_longlink'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][0] except KeyError: pass @@ -496,35 +496,35 @@ def get_transceiver_threshold_info(self): ======================================================================== """ trans_dom_th = super(CCmisApi,self).get_transceiver_threshold_info() - trans_dom_th['biasxihighalarm'] = self.vdm_dict['Modulator Bias X/I [%]'][1][1] - trans_dom_th['biasxilowalarm'] = self.vdm_dict['Modulator Bias X/I [%]'][1][2] - trans_dom_th['biasxihighwarning'] = self.vdm_dict['Modulator Bias X/I [%]'][1][3] - trans_dom_th['biasxilowwarning'] = self.vdm_dict['Modulator Bias X/I [%]'][1][4] - trans_dom_th['biasxqhighalarm'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][1] - trans_dom_th['biasxqlowalarm'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][2] - trans_dom_th['biasxqhighwarning'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][3] - trans_dom_th['biasxqlowwarning'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][4] - trans_dom_th['biasxphighalarm'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][1] - trans_dom_th['biasxplowalarm'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][2] - trans_dom_th['biasxphighwarning'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][3] - trans_dom_th['biasxplowwarning'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][4] - trans_dom_th['biasyihighalarm'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][1] - trans_dom_th['biasyilowalarm'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][2] - trans_dom_th['biasyihighwarning'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][3] - trans_dom_th['biasyilowwarning'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][4] - trans_dom_th['biasyqhighalarm'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][1] - trans_dom_th['biasyqlowalarm'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][2] - trans_dom_th['biasyqhighwarning'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][3] - trans_dom_th['biasyqlowwarning'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][4] - trans_dom_th['biasyphighalarm'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][1] - trans_dom_th['biasyplowalarm'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][2] - trans_dom_th['biasyphighwarning'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][3] - trans_dom_th['biasyplowwarning'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][4] - trans_dom_th['cdshorthighalarm'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][1] - trans_dom_th['cdshortlowalarm'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][2] - trans_dom_th['cdshorthighwarning'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][3] - trans_dom_th['cdshortlowwarning'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][4] try: + trans_dom_th['biasxihighalarm'] = self.vdm_dict['Modulator Bias X/I [%]'][1][1] + trans_dom_th['biasxilowalarm'] = self.vdm_dict['Modulator Bias X/I [%]'][1][2] + trans_dom_th['biasxihighwarning'] = self.vdm_dict['Modulator Bias X/I [%]'][1][3] + trans_dom_th['biasxilowwarning'] = self.vdm_dict['Modulator Bias X/I [%]'][1][4] + trans_dom_th['biasxqhighalarm'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][1] + trans_dom_th['biasxqlowalarm'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][2] + trans_dom_th['biasxqhighwarning'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][3] + trans_dom_th['biasxqlowwarning'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][4] + trans_dom_th['biasxphighalarm'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][1] + trans_dom_th['biasxplowalarm'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][2] + trans_dom_th['biasxphighwarning'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][3] + trans_dom_th['biasxplowwarning'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][4] + trans_dom_th['biasyihighalarm'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][1] + trans_dom_th['biasyilowalarm'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][2] + trans_dom_th['biasyihighwarning'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][3] + trans_dom_th['biasyilowwarning'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][4] + trans_dom_th['biasyqhighalarm'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][1] + trans_dom_th['biasyqlowalarm'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][2] + trans_dom_th['biasyqhighwarning'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][3] + trans_dom_th['biasyqlowwarning'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][4] + trans_dom_th['biasyphighalarm'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][1] + trans_dom_th['biasyplowalarm'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][2] + trans_dom_th['biasyphighwarning'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][3] + trans_dom_th['biasyplowwarning'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][4] + trans_dom_th['cdshorthighalarm'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][1] + trans_dom_th['cdshortlowalarm'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][2] + trans_dom_th['cdshorthighwarning'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][3] + trans_dom_th['cdshortlowwarning'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][4] trans_dom_th['cdlonghighalarm'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][1] trans_dom_th['cdlonglowalarm'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][2] trans_dom_th['cdlonghighwarning'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][3] @@ -759,30 +759,33 @@ def get_transceiver_status(self): trans_status['tuning_not_accepted'] = 'TuningNotAccepted' in laser_tuning_summary trans_status['invalid_channel_num'] = 'InvalidChannel' in laser_tuning_summary trans_status['tuning_complete'] = 'TuningComplete' in laser_tuning_summary - trans_status['biasxihighalarm_flag'] = self.vdm_dict['Modulator Bias X/I [%]'][1][5] - trans_status['biasxilowalarm_flag'] = self.vdm_dict['Modulator Bias X/I [%]'][1][6] - trans_status['biasxihighwarning_flag'] = self.vdm_dict['Modulator Bias X/I [%]'][1][7] - trans_status['biasxilowwarning_flag'] = self.vdm_dict['Modulator Bias X/I [%]'][1][8] - trans_status['biasxqhighalarm_flag'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][5] - trans_status['biasxqlowalarm_flag'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][6] - trans_status['biasxqhighwarning_flag'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][7] - trans_status['biasxqlowwarning_flag'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][8] - trans_status['biasxphighalarm_flag'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][5] - trans_status['biasxplowalarm_flag'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][6] - trans_status['biasxphighwarning_flag'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][7] - trans_status['biasxplowwarning_flag'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][8] - trans_status['biasyihighalarm_flag'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][5] - trans_status['biasyilowalarm_flag'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][6] - trans_status['biasyihighwarning_flag'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][7] - trans_status['biasyilowwarning_flag'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][8] - trans_status['biasyqhighalarm_flag'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][5] - trans_status['biasyqlowalarm_flag'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][6] - trans_status['biasyqhighwarning_flag'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][7] - trans_status['biasyqlowwarning_flag'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][8] - trans_status['biasyphighalarm_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][5] - trans_status['biasyplowalarm_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][6] - trans_status['biasyphighwarning_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][7] - trans_status['biasyplowwarning_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][8] + try: + trans_status['biasxihighalarm_flag'] = self.vdm_dict['Modulator Bias X/I [%]'][1][5] + trans_status['biasxilowalarm_flag'] = self.vdm_dict['Modulator Bias X/I [%]'][1][6] + trans_status['biasxihighwarning_flag'] = self.vdm_dict['Modulator Bias X/I [%]'][1][7] + trans_status['biasxilowwarning_flag'] = self.vdm_dict['Modulator Bias X/I [%]'][1][8] + trans_status['biasxqhighalarm_flag'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][5] + trans_status['biasxqlowalarm_flag'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][6] + trans_status['biasxqhighwarning_flag'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][7] + trans_status['biasxqlowwarning_flag'] = self.vdm_dict['Modulator Bias X/Q [%]'][1][8] + trans_status['biasxphighalarm_flag'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][5] + trans_status['biasxplowalarm_flag'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][6] + trans_status['biasxphighwarning_flag'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][7] + trans_status['biasxplowwarning_flag'] = self.vdm_dict['Modulator Bias X_Phase [%]'][1][8] + trans_status['biasyihighalarm_flag'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][5] + trans_status['biasyilowalarm_flag'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][6] + trans_status['biasyihighwarning_flag'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][7] + trans_status['biasyilowwarning_flag'] = self.vdm_dict['Modulator Bias Y/I [%]'][1][8] + trans_status['biasyqhighalarm_flag'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][5] + trans_status['biasyqlowalarm_flag'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][6] + trans_status['biasyqhighwarning_flag'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][7] + trans_status['biasyqlowwarning_flag'] = self.vdm_dict['Modulator Bias Y/Q [%]'][1][8] + trans_status['biasyphighalarm_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][5] + trans_status['biasyplowalarm_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][6] + trans_status['biasyphighwarning_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][7] + trans_status['biasyplowwarning_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][8] + except KeyError: + pass trans_status['cdshorthighalarm_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][5] trans_status['cdshortlowalarm_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][6] trans_status['cdshorthighwarning_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][7] From 10837a27eef3b8a4693776fd84b80107a8192731 Mon Sep 17 00:00:00 2001 From: daxia16 <110370108+daxia16@users.noreply.github.com> Date: Thu, 11 May 2023 00:45:13 +0800 Subject: [PATCH 04/24] add set get uid led api (#369) * add set get uid led api * add test case to improve coverage --- sonic_platform_base/chassis_base.py | 27 +++++++++++++++++++++++++++ tests/chassis_base_test.py | 18 ++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/sonic_platform_base/chassis_base.py b/sonic_platform_base/chassis_base.py index e292cfcd3..877d9c849 100644 --- a/sonic_platform_base/chassis_base.py +++ b/sonic_platform_base/chassis_base.py @@ -546,6 +546,33 @@ def get_status_led(self): """ raise NotImplementedError + ############################################## + # System LED methods + ############################################## + + def set_uid_led(self, color): + """ + Sets the state of the system UID LED + + Args: + color: A string representing the color with which to set the + system UID LED + + Returns: + bool: True if system LED state is set successfully, False if not + """ + raise NotImplementedError + + def get_uid_led(self): + """ + Gets the state of the system UID LED + + Returns: + A string, one of the valid LED color strings which could be vendor + specified. + """ + raise NotImplementedError + ############################################## # Other methods ############################################## diff --git a/tests/chassis_base_test.py b/tests/chassis_base_test.py index c2f2e5f97..d410ff4ae 100644 --- a/tests/chassis_base_test.py +++ b/tests/chassis_base_test.py @@ -16,3 +16,21 @@ def test_reboot_cause(self): assert(chassis.REBOOT_CAUSE_HARDWARE_BUTTON == "Push button") assert(chassis.REBOOT_CAUSE_HARDWARE_RESET_FROM_ASIC == "Reset from ASIC") assert(chassis.REBOOT_CAUSE_NON_HARDWARE == "Non-Hardware") + + def test_chassis_base(self): + chassis = ChassisBase() + not_implemented_methods = [ + [chassis.get_uid_led], + [chassis.set_uid_led, "COLOR"], + ] + + for method in not_implemented_methods: + exception_raised = False + try: + func = method[0] + args = method[1:] + func(*args) + except NotImplementedError: + exception_raised = True + + assert exception_raised \ No newline at end of file From d2b40214a9fafa20faf9ef5fdb846c269e9d6bd7 Mon Sep 17 00:00:00 2001 From: Mai Bui Date: Wed, 10 May 2023 17:47:23 -0400 Subject: [PATCH 05/24] replace exit() with sys.exit() (#370) Description Replace exit() by sys.exit() Motivation and Context sys.exit is better than exit, considered good to use in production code. Ref: https://stackoverflow.com/questions/6501121/difference-between-exit-and-sys-exit-in-python https://stackoverflow.com/questions/19747371/python-exit-commands-why-so-many-and-when-should-each-be-used --- .../sonic_eeprom/eeprom_base.py | 7 ++-- .../sonic_eeprom/eeprom_tlvinfo.py | 4 +- tests/eeprom_base_test.py | 40 ++++++++++++++++++- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/sonic_platform_base/sonic_eeprom/eeprom_base.py b/sonic_platform_base/sonic_eeprom/eeprom_base.py index 083d1cd1f..8f1c6303f 100644 --- a/sonic_platform_base/sonic_eeprom/eeprom_base.py +++ b/sonic_platform_base/sonic_eeprom/eeprom_base.py @@ -14,6 +14,7 @@ import binascii import os import io + import sys import struct import fcntl except ImportError as e: @@ -83,7 +84,7 @@ def encode_checksum(self, crc): elif self.checksum_field_size() == 1: return struct.pack('>B', crc) print('checksum type not yet supported') - exit(1) + sys.exit(1) def compute_2s_complement(self, e, size): crc = 0 @@ -122,7 +123,7 @@ def calculate_checksum(self, e): if self.checksum_type() == 'dell-crc': return self.compute_dell_crc(e) print('checksum type not yet supported') - exit(1) + sys.exit(1) def is_checksum_valid(self, e): offset = 0 - self.checksum_field_size() @@ -169,7 +170,7 @@ def set_eeprom(self, e, cmd_args): v = v.strip() if k not in fields: print("Error: invalid field '%s'" %(k)) - exit(1) + sys.exit(1) ndict[k] = v for I in self.f: diff --git a/sonic_platform_base/sonic_eeprom/eeprom_tlvinfo.py b/sonic_platform_base/sonic_eeprom/eeprom_tlvinfo.py index f8dd32807..a044b7328 100644 --- a/sonic_platform_base/sonic_eeprom/eeprom_tlvinfo.py +++ b/sonic_platform_base/sonic_eeprom/eeprom_tlvinfo.py @@ -193,7 +193,7 @@ def set_eeprom(self, e, cmd_args): self.decode_eeprom(new_e) if len(new_e) > min(self._TLV_INFO_MAX_LEN, self.eeprom_max_len): sys.stderr.write("\nERROR: There is not enough room in the EEPROM to save data.\n") - exit(1) + sys.exit(1) return new_e @@ -601,7 +601,7 @@ def encoder(self, I, v): sys.stderr.write("Error: '" + "0x%02X" % (I[0],) + "' -- Unable to set the read-only Quanta codes.\n") else: sys.stderr.write("Error: '" + "0x%02X" % (I[0],) + "' correct format is " + errstr + "\n") - exit(0) + sys.exit(0) return bytearray([I[0]]) + bytearray([len(value)]) + value diff --git a/tests/eeprom_base_test.py b/tests/eeprom_base_test.py index 0328ae2c3..da89e2b95 100644 --- a/tests/eeprom_base_test.py +++ b/tests/eeprom_base_test.py @@ -1,7 +1,9 @@ import os +import pytest import subprocess from unittest import mock -from sonic_platform_base.sonic_eeprom import eeprom_tlvinfo +from unittest.mock import patch, MagicMock +from sonic_platform_base.sonic_eeprom import eeprom_base, eeprom_tlvinfo EEPROM_SYMLINK = "vpd_info" EEPROM_HEX_FILE = "syseeprom.hex" TEST_PATH = os.path.dirname(os.path.abspath(__file__)) @@ -151,7 +153,7 @@ def test_eeprom_tlvinfo_set_eeprom(self): # Test adding invalid field (is_valid, t) = eeprom_class.get_tlv_field(eeprom_new, 0x20) assert(not is_valid) - with mock.patch('builtins.exit') as exit_mock: + with mock.patch('sys.exit') as exit_mock: eeprom_new = eeprom_class.set_eeprom(eeprom, ['0x20 = Invalid']) assert exit_mock.called @@ -167,3 +169,37 @@ def test_eeprom_tlvinfo_read_eeprom_db(self): eeprom_class = eeprom_tlvinfo.TlvInfoDecoder(EEPROM_SYMLINK_FULL_PATH, 0, '', True) eeprom_class.redis_client.hget = mock.MagicMock(return_value = b'1') assert(0 == eeprom_class.read_eeprom_db()) + +class TestEepromDecoder(object): + def setup(self): + print("SETUP") + + @patch('builtins.print') + @patch('sonic_platform_base.sonic_eeprom.eeprom_base.EepromDecoder.checksum_field_size', MagicMock(return_value=10)) + def test_encode_checksum_not_supported(self, mock_print): + with pytest.raises(SystemExit) as e: + eeprom = eeprom_base.EepromDecoder('path', 'format', 'start', 'status', 'readonly') + eeprom.encode_checksum('crc') + mock_print.assert_called_with('checksum type not yet supported') + assert e.value.code == 1 + + @patch('builtins.print') + @patch('sonic_platform_base.sonic_eeprom.eeprom_base.EepromDecoder.checksum_type', MagicMock(return_value='dell-crc32')) + def test_calculate_checksum_not_supported(self, mock_print): + with pytest.raises(SystemExit) as e: + eeprom = eeprom_base.EepromDecoder('path', 'format', 'start', 'status', 'readonly') + eeprom.calculate_checksum('crc') + mock_print.assert_called_with('checksum type not yet supported') + assert e.value.code == 1 + + @patch('builtins.print') + def test_set_eeprom_invalid_field(self, mock_print): + with pytest.raises(SystemExit) as e: + eeprom = eeprom_base.EepromDecoder('path', ['format'], 'start', 'status', 'readonly') + eeprom.set_eeprom('eeprom', ['0x20 = Invalid']) + mock_print.assert_called_with("Error: invalid field '0x20'") + assert e.value.code == 1 + + def teardown(self): + print("TEAR DOWN") + From b323337cb341df359bfbac1bef85a626e2370e1c Mon Sep 17 00:00:00 2001 From: Junchao-Mellanox <57339448+Junchao-Mellanox@users.noreply.github.com> Date: Sat, 13 May 2023 00:29:54 +0800 Subject: [PATCH 06/24] Fix issue '<' not supported between instances of 'NoneType' and 'int' (#371) --- sonic_platform_base/sonic_xcvr/api/public/cmis.py | 6 +++++- tests/sonic_xcvr/test_cmis.py | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 23cfdb756..0a30b36d2 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -501,8 +501,12 @@ def get_tx_bias(self): if not tx_bias_support: return ["N/A" for _ in range(self.NUM_CHANNELS)] scale_raw = self.xcvr_eeprom.read(consts.TX_BIAS_SCALE) + if scale_raw is None: + return ["N/A" for _ in range(self.NUM_CHANNELS)] scale = 2**scale_raw if scale_raw < 3 else 1 tx_bias = self.xcvr_eeprom.read(consts.TX_BIAS_FIELD) + if tx_bias is None: + return ["N/A" for _ in range(self.NUM_CHANNELS)] for key, value in tx_bias.items(): tx_bias[key] *= scale return [tx_bias['LaserBiasTx%dField' % i] for i in range(1, self.NUM_CHANNELS + 1)] @@ -874,7 +878,7 @@ def get_laser_temperature(self): 'high warn': laser_temp_high_warn, 'low warn': laser_temp_low_warn} return laser_temp_dict - + def _get_laser_temp_threshold(self, field): LASER_TEMP_SCALE = 256.0 value = self.xcvr_eeprom.read(field) diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index 9827537f7..48f1e3957 100644 --- a/tests/sonic_xcvr/test_cmis.py +++ b/tests/sonic_xcvr/test_cmis.py @@ -377,6 +377,16 @@ def test_get_tx_bias(self, mock_response, expected): result = self.api.get_tx_bias() assert result == expected + def test_get_tx_bias_neg(self): + self.api.get_tx_bias_support = MagicMock(return_value=True) + self.api.xcvr_eeprom.read = MagicMock() + # scale_raw is None, verify no crash + self.api.xcvr_eeprom.read.return_value = None + self.api.get_tx_bias() + # scale_raw is 1, tx_bias is None, verify no crash + self.api.xcvr_eeprom.read.side_effect = [1, None] + self.api.get_tx_bias() + @pytest.mark.parametrize("mock_response, expected", [ ([False, True], True) ]) From 39d4783ba162bf8cf3802b0cef245e778b198bb8 Mon Sep 17 00:00:00 2001 From: rajann Date: Fri, 12 May 2023 17:34:32 -0700 Subject: [PATCH 07/24] Render Media lane and Media assignment options info from Application Code (#368) * Render Media lane info from Application Code Render Media lane and Media Assignment Options from selected App code * Update media lane related test cases * Update media lane related test cases * Update media lane related test cases * Update media lane related test cases --- .../sonic_xcvr/api/public/cmis.py | 18 +++-- tests/sonic_xcvr/test_cmis.py | 66 +++++++++++++++---- 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 0a30b36d2..17a65efa0 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -734,13 +734,18 @@ def get_host_lane_count(self): ''' return self.xcvr_eeprom.read(consts.HOST_LANE_COUNT) - def get_media_lane_count(self): + def get_media_lane_count(self, appl=1): ''' This function returns number of media lanes for default application ''' if self.is_flat_memory(): return 0 - return self.xcvr_eeprom.read(consts.MEDIA_LANE_COUNT) + + if (appl <= 0): + return 0 + + appl_advt = self.get_application_advertisement() + return appl_advt[appl]['media_lane_count'] if len(appl_advt) >= appl else 0 def get_media_interface_technology(self): ''' @@ -761,13 +766,18 @@ def get_host_lane_assignment_option(self, appl=1): appl_advt = self.get_application_advertisement() return appl_advt[appl]['host_lane_assignment_options'] if len(appl_advt) >= appl else 0 - def get_media_lane_assignment_option(self): + def get_media_lane_assignment_option(self, appl=1): ''' This function returns the media lane that the application is allowed to begin on ''' if self.is_flat_memory(): return 'N/A' - return self.xcvr_eeprom.read(consts.MEDIA_LANE_ASSIGNMENT_OPTION) + + if (appl <= 0): + return 0 + + appl_advt = self.get_application_advertisement() + return appl_advt[appl]['media_lane_assignment_options'] if len(appl_advt) >= appl else 0 def get_active_apsel_hostlane(self): ''' diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index 48f1e3957..ae00ea314 100644 --- a/tests/sonic_xcvr/test_cmis.py +++ b/tests/sonic_xcvr/test_cmis.py @@ -699,13 +699,34 @@ def test_get_host_lane_count(self, mock_response, expected): result = self.api.get_host_lane_count() assert result == expected - @pytest.mark.parametrize("mock_response, expected", [ - (1, 1) + @pytest.mark.parametrize("appl, expected", [ + (0, 0), + (1, 4), + (2, 1), + (3, 0) ]) - def test_get_media_lane_count(self, mock_response, expected): - self.api.xcvr_eeprom.read = MagicMock() - self.api.xcvr_eeprom.read.return_value = mock_response - result = self.api.get_media_lane_count() + @patch('sonic_platform_base.sonic_xcvr.api.public.cmis.CmisApi.get_application_advertisement', MagicMock(return_value = + { + 1: { + 'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', + 'module_media_interface_id': '400GBASE-DR4 (Cl 124)', + 'media_lane_count': 4, + 'host_lane_count': 8, + 'host_lane_assignment_options': 1, + 'media_lane_assignment_options': 1 + }, + 2: { + 'host_electrical_interface_id': '100GAUI-2 C2M (Annex 135G)', + 'module_media_interface_id': '100G-LR/100GBASE-LR1 (Cl 140)', + 'media_lane_count': 1, + 'host_lane_count': 2, + 'host_lane_assignment_options': 85, + 'media_lane_assignment_options': 15 + } + } + )) + def test_get_media_lane_count(self, appl, expected): + result = self.api.get_media_lane_count(appl) assert result == expected @pytest.mark.parametrize("mock_response, expected", [ @@ -745,13 +766,34 @@ def test_get_host_lane_assignment_option(self, appl, expected): result = self.api.get_host_lane_assignment_option(appl) assert result == expected - @pytest.mark.parametrize("mock_response, expected", [ - (1, 1) + @pytest.mark.parametrize("appl, expected", [ + (0, 0), + (1, 1), + (2, 15), + (3, 0) ]) - def test_get_media_lane_assignment_option(self, mock_response, expected): - self.api.xcvr_eeprom.read = MagicMock() - self.api.xcvr_eeprom.read.return_value = mock_response - result = self.api.get_media_lane_assignment_option() + @patch('sonic_platform_base.sonic_xcvr.api.public.cmis.CmisApi.get_application_advertisement', MagicMock(return_value = + { + 1: { + 'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', + 'module_media_interface_id': '400GBASE-DR4 (Cl 124)', + 'media_lane_count': 4, + 'host_lane_count': 8, + 'host_lane_assignment_options': 1, + 'media_lane_assignment_options': 1 + }, + 2: { + 'host_electrical_interface_id': '100GAUI-2 C2M (Annex 135G)', + 'module_media_interface_id': '100G-LR/100GBASE-LR1 (Cl 140)', + 'media_lane_count': 1, + 'host_lane_count': 2, + 'host_lane_assignment_options': 85, + 'media_lane_assignment_options': 15 + } + } + )) + def test_get_media_lane_assignment_option(self, appl, expected): + result = self.api.get_media_lane_assignment_option(appl) assert result == expected @pytest.mark.parametrize("mock_response, expected", [ From 9a76bec1bcddc7aca7d348cdd45db3ec38f4a58c Mon Sep 17 00:00:00 2001 From: mihirpat1 <112018033+mihirpat1@users.noreply.github.com> Date: Tue, 16 May 2023 19:25:17 -0700 Subject: [PATCH 08/24] Retrieve FW version using CDB command for CMIS transceivers + handle single bank FW versioning (#372) * Display Inactive FW version from EEPROM for single bank CMIS transceivers Signed-off-by: Mihir Patel * Increased code coverage Signed-off-by: Mihir Patel --------- Signed-off-by: Mihir Patel --- .../sonic_xcvr/api/public/cmis.py | 35 ++++++++++++++----- .../sonic_xcvr/api/xcvr_api.py | 10 ++++++ .../sonic_xcvr/sfp_optoe_base.py | 4 +++ tests/sonic_xcvr/test_cmis.py | 34 ++++++++++++------ 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 17a65efa0..8bb26859f 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -161,10 +161,10 @@ def get_transceiver_info(self): xcvr_info['media_interface_technology'] = self.get_media_interface_technology() xcvr_info['vendor_rev'] = self.get_vendor_rev() xcvr_info['cmis_rev'] = self.get_cmis_rev() - xcvr_info['active_firmware'] = self.get_module_active_firmware() - xcvr_info['inactive_firmware'] = self.get_module_inactive_firmware() xcvr_info['specification_compliance'] = self.get_module_media_type() + xcvr_info['active_firmware'], xcvr_info['inactive_firmware'] = self.get_transceiver_info_firmware_versions() + # In normal case will get a valid value for each of the fields. If get a 'None' value # means there was a failure while reading the EEPROM, either because the EEPROM was # not ready yet or experincing some other issues. It shouldn't return a dict with a @@ -175,6 +175,17 @@ def get_transceiver_info(self): else: return xcvr_info + def get_transceiver_info_firmware_versions(self): + result = self.get_module_fw_info() + if result is None: + return ["N/A", "N/A"] + try: + ( _, _, _, _, _, _, _, _, ActiveFirmware, InactiveFirmware) = result['result'] + except (ValueError, TypeError): + return ["N/A", "N/A"] + + return [ActiveFirmware, InactiveFirmware] + def get_transceiver_bulk_status(self): rx_los = self.get_rx_los() tx_fault = self.get_tx_fault() @@ -1305,11 +1316,19 @@ def get_module_fw_info(self): if ImageARunning == 1: RunningImage = 'A' ActiveFirmware = ImageA - InactiveFirmware = ImageB + if ImageBValid == 0: + InactiveFirmware = ImageB + else: + #In case of single bank module, inactive firmware version can be read from EEPROM + InactiveFirmware = self.get_module_inactive_firmware() + ".0" elif ImageBRunning == 1: RunningImage = 'B' ActiveFirmware = ImageB - InactiveFirmware = ImageA + if ImageAValid == 0: + InactiveFirmware = ImageA + else: + #In case of single bank module, inactive firmware version can be read from EEPROM + InactiveFirmware = self.get_module_inactive_firmware() + ".0" else: RunningImage = 'N/A' if ImageACommitted == 1: @@ -1325,7 +1344,7 @@ def get_module_fw_info(self): else: txt += 'Reply payload check code error\n' return {'status': False, 'info': txt, 'result': None} - return {'status': True, 'info': txt, 'result': (ImageA, ImageARunning, ImageACommitted, ImageAValid, ImageB, ImageBRunning, ImageBCommitted, ImageBValid)} + return {'status': True, 'info': txt, 'result': (ImageA, ImageARunning, ImageACommitted, ImageAValid, ImageB, ImageBRunning, ImageBCommitted, ImageBValid, ActiveFirmware, InactiveFirmware)} def cdb_run_firmware(self, mode = 0x01): # run module FW (CMD 0109h) @@ -1552,7 +1571,7 @@ def module_fw_upgrade(self, imagepath): """ result = self.get_module_fw_info() try: - _, _, _, _, _, _, _, _ = result['result'] + _, _, _, _, _, _, _, _, _, _ = result['result'] except (ValueError, TypeError): return result['status'], result['info'] result = self.get_module_fw_mgmt_feature() @@ -1579,7 +1598,7 @@ def module_fw_switch(self): result = self.get_module_fw_info() try: (ImageA_init, ImageARunning_init, ImageACommitted_init, ImageAValid_init, - ImageB_init, ImageBRunning_init, ImageBCommitted_init, ImageBValid_init) = result['result'] + ImageB_init, ImageBRunning_init, ImageBCommitted_init, ImageBValid_init, _, _) = result['result'] except (ValueError, TypeError): return result['status'], result['info'] if ImageAValid_init == 0 and ImageBValid_init == 0: @@ -1587,7 +1606,7 @@ def module_fw_switch(self): time.sleep(60) self.module_fw_commit() (ImageA, ImageARunning, ImageACommitted, ImageAValid, - ImageB, ImageBRunning, ImageBCommitted, ImageBValid) = self.get_module_fw_info()['result'] + ImageB, ImageBRunning, ImageBCommitted, ImageBValid, _, _) = self.get_module_fw_info()['result'] # detect if image switch happened txt += 'Before switch Image A: %s; Run: %d Commit: %d, Valid: %d\n' %( ImageA_init, ImageARunning_init, ImageACommitted_init, ImageAValid_init diff --git a/sonic_platform_base/sonic_xcvr/api/xcvr_api.py b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py index e759f794e..57b77f691 100644 --- a/sonic_platform_base/sonic_xcvr/api/xcvr_api.py +++ b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py @@ -70,6 +70,16 @@ def get_transceiver_info(self): """ raise NotImplementedError + def get_transceiver_info_firmware_versions(self): + """ + Retrieves active and inactive firmware versions of the xcvr + + Returns: + A list with active and inactive firmware versions of the xcvr + [active_firmware, inactive_firmware] + """ + raise NotImplementedError + def get_transceiver_bulk_status(self): """ Retrieves bulk status info for this xcvr diff --git a/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py b/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py index 4bc62967b..668389e14 100644 --- a/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py +++ b/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py @@ -23,6 +23,10 @@ def get_transceiver_info(self): api = self.get_xcvr_api() return api.get_transceiver_info() if api is not None else None + def get_transceiver_info_firmware_versions(self): + api = self.get_xcvr_api() + return api.get_transceiver_info_firmware_versions() if api is not None else None + def get_transceiver_bulk_status(self): api = self.get_xcvr_api() return api.get_transceiver_bulk_status() if api is not None else None diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index ae00ea314..beb712c56 100644 --- a/tests/sonic_xcvr/test_cmis.py +++ b/tests/sonic_xcvr/test_cmis.py @@ -1134,6 +1134,8 @@ def test_get_module_level_flag(self, mock_response, expected): ((None, 1, [0] * 128), {'status': False, 'info': "", 'result': 0}), ((128, None, [0] * 128), {'status': False, 'info': "", 'result': 0}), ((128, 0, [0] * 128), {'status': False, 'info': "", 'result': None}), + ((128, 1, [67, 3, 2, 2, 3, 183] + [0] * 104), {'status': True, 'info': "", 'result': None}), + ((128, 1, [52, 3, 2, 2, 3, 183] + [0] * 104), {'status': True, 'info': "", 'result': None}), ((110, 1, [3, 3, 2, 2, 3, 183] + [0] * 104), {'status': True, 'info': "", 'result': None}), ((110, 1, [48, 3, 2, 2, 3, 183] + [0] * 104), {'status': True, 'info': "", 'result': None}), ]) @@ -1177,7 +1179,7 @@ def test_module_fw_commit(self, mock_response, expected): @pytest.mark.parametrize("input_param, mock_response, expected", [ ( 'abc', - [{'status': True, 'info': '', 'result': ('a', 1, 1, 0, 'b', 0, 0, 0)}, {'status': True, 'info': '', 'result': (112, 2048, True, True, 2048)}, (True, ''), (True, '')], + [{'status': True, 'info': '', 'result': ('a', 1, 1, 0, 'b', 0, 0, 0, 'a', 'b')}, {'status': True, 'info': '', 'result': (112, 2048, True, True, 2048)}, (True, ''), (True, '')], (True, '') ), ( @@ -1187,12 +1189,12 @@ def test_module_fw_commit(self, mock_response, expected): ), ( 'abc', - [{'status': True, 'info': '', 'result': ('a', 1, 1, 0, 'b', 0, 0, 0)}, {'status': False, 'info': '', 'result': None}, (True, ''), (True, '')], + [{'status': True, 'info': '', 'result': ('a', 1, 1, 0, 'b', 0, 0, 0, 'a', 'b')}, {'status': False, 'info': '', 'result': None}, (True, ''), (True, '')], (False, '') ), ( 'abc', - [{'status': True, 'info': '', 'result': ('a', 1, 1, 0, 'b', 0, 0, 0)}, {'status': True, 'info': '', 'result': (112, 2048, True, True, 2048)}, (False, ''), (True, '')], + [{'status': True, 'info': '', 'result': ('a', 1, 1, 0, 'b', 0, 0, 0, 'a', 'b')}, {'status': True, 'info': '', 'result': (112, 2048, True, True, 2048)}, (False, ''), (True, '')], (False, '') ), ]) @@ -1209,7 +1211,7 @@ def test_module_fw_upgrade(self, input_param, mock_response, expected): assert result == expected @pytest.mark.parametrize("mock_response, expected",[ - ([None, None, None, None, None, None, None, None, None, None, None, None, None, None], None), + ([None, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None), ( [ { @@ -1237,7 +1239,8 @@ def test_module_fw_upgrade(self, input_param, mock_response, expected): '5.0', '0.1', '0.0', - 'sm_media_interface' + 'sm_media_interface', + {'status': True, 'result': ("0.3.0", 1, 1, 0, "0.2.0", 0, 0, 0, "0.3.0", "0.2.0")} ], { 'type': 'QSFP-DD Double Density 8X Pluggable Transceiver', 'type_abbrv_name': 'QSFP-DD', @@ -1250,9 +1253,9 @@ def test_module_fw_upgrade(self, input_param, mock_response, expected): 'nominal_bit_rate': 0, 'specification_compliance': 'sm_media_interface', 'application_advertisement': 'N/A', - 'active_firmware': '0.1', + 'active_firmware': '0.3.0', 'media_lane_count': 1, - 'inactive_firmware': '0.0', + 'inactive_firmware': '0.2.0', 'vendor_rev': '0.0', 'host_electrical_interface': '400GAUI-8 C2M (Annex 120E)', 'vendor_oui': 'xx-xx-xx', @@ -1301,14 +1304,12 @@ def test_get_transceiver_info(self, mock_response, expected): self.api.get_vendor_rev.return_value = mock_response[9] self.api.get_cmis_rev = MagicMock() self.api.get_cmis_rev.return_value = mock_response[10] - self.api.get_module_active_firmware = MagicMock() - self.api.get_module_active_firmware.return_value = mock_response[11] - self.api.get_module_inactive_firmware = MagicMock() - self.api.get_module_inactive_firmware.return_value = mock_response[12] + self.api.get_module_fw_info = MagicMock() self.api.get_module_media_type = MagicMock() self.api.get_module_media_type.return_value = mock_response[13] self.api.get_module_hardware_revision = MagicMock() self.api.get_module_hardware_revision.return_value = '0.0' + self.api.get_module_fw_info.return_value = mock_response[14] self.api.is_flat_memory = MagicMock() self.api.is_flat_memory.return_value = False result = self.api.get_transceiver_info() @@ -2250,3 +2251,14 @@ def mock_read_raw(offset, size): except: assert 0, traceback.format_exc() run_num -= 1 + + def test_get_transceiver_info_firmware_versions_negative_tests(self): + self.api.get_module_fw_info = MagicMock() + self.api.get_module_fw_info.return_value = None + result = self.api.get_transceiver_info_firmware_versions() + assert result == ["N/A", "N/A"] + + self.api.get_module_fw_info = MagicMock() + self.api.get_module_fw_info.side_effect = {'result': TypeError} + result = self.api.get_transceiver_info_firmware_versions() + assert result == ["N/A", "N/A"] From e81f3d35c12ad4d375503cd131d96fa4fd89b607 Mon Sep 17 00:00:00 2001 From: ShiyanWangMS Date: Thu, 18 May 2023 14:44:33 +0800 Subject: [PATCH 09/24] Change Y cable simulator log level from error to warning due to false alarm Description In Y cable simulator, when the HTTP GET or POST method time out due to server's busy, method will retry with ERR level log message. This ERR level log will be treated a test case failure in Log Analyzer. Motivation and Context In this change, the ERR level log is changed to WARNING level log. Log Analyzer will not treat it as a test case failure. How Has This Been Tested? Build a private image and manually run the previously failed test cases with Broadcom platform: dualtor_io/test_link_failure.py Got 100% pass --- sonic_y_cable/microsoft/y_cable_simulated.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sonic_y_cable/microsoft/y_cable_simulated.py b/sonic_y_cable/microsoft/y_cable_simulated.py index 79aa920e6..8f011b5b2 100644 --- a/sonic_y_cable/microsoft/y_cable_simulated.py +++ b/sonic_y_cable/microsoft/y_cable_simulated.py @@ -99,14 +99,14 @@ def _get(self, url=None): with urllib.request.urlopen(req, timeout=self.URLOPEN_TIMEOUT) as resp: return json.loads(resp.read().decode('utf-8')) except urllib.error.HTTPError as e: - self.log_error('attempt={}, GET {} for physical_port {} failed with {}, detail: {}'.format( + self.log_warning('attempt={}, GET {} for physical_port {} failed with {}, detail: {}'.format( attempt, get_url, self.port, repr(e), e.read())) except (urllib.error.URLError, json.decoder.JSONDecodeError, Exception) as e: - self.log_error('attempt={}, GET {} for physical_port {} failed with {}'.format( + self.log_warning('attempt={}, GET {} for physical_port {} failed with {}'.format( attempt, get_url, self.port, @@ -114,7 +114,7 @@ def _get(self, url=None): # Retry in case of exception, to workaround 'no route to host' issue after pmon restart if (time.time() - start_time) > self.POLL_TIMEOUT: - self.log_error('Retry GET {} for physical port {} timeout after {} seconds, attempted={}'.format( + self.log_warning('Retry GET {} for physical port {} timeout after {} seconds, attempted={}'.format( get_url, self.port, self.POLL_TIMEOUT, @@ -156,7 +156,7 @@ def _post(self, url=None, data=None): with urllib.request.urlopen(req, timeout=self.URLOPEN_TIMEOUT) as resp: return json.loads(resp.read().decode('utf-8')) except urllib.error.HTTPError as e: - self.log_error('attempt={}, POST {} with data {} for physical_port {} failed with {}, detail: {}'.format( + self.log_warning('attempt={}, POST {} with data {} for physical_port {} failed with {}, detail: {}'.format( attempt, post_url, post_data, @@ -165,7 +165,7 @@ def _post(self, url=None, data=None): e.read() )) except (urllib.error.URLError, json.decoder.JSONDecodeError, Exception) as e: - self.log_error('attempt={}, POST {} with data {} for physical_port {} failed with {}'.format( + self.log_warning('attempt={}, POST {} with data {} for physical_port {} failed with {}'.format( attempt, post_url, post_data, @@ -175,7 +175,7 @@ def _post(self, url=None, data=None): # Retry in case of exception, to workaround 'no route to host' issue after pmon restart if time.time() - start_time > self.POLL_TIMEOUT: - self.log_error('Retry POST {} with data{} for physical port {} timeout after {} seconds, attempted={}'.format( + self.log_warning('Retry POST {} with data{} for physical port {} timeout after {} seconds, attempted={}'.format( post_url, post_data, self.port, From 8c0398eb911ee96967c3a0e1b7fb3ba9167451e1 Mon Sep 17 00:00:00 2001 From: rajann Date: Thu, 25 May 2023 16:23:31 -0700 Subject: [PATCH 10/24] Update CMIS api's rendering max-duration (#375) * Update api's rendering maxduration Update ModulePwrUp, ModulePwrDown, DataPathInit and DataPathDeInit api's to handle unsupported values * Update CMIS module timing enums with reserved values * Update the api's rendering duration values of CMIS modules * Update the api's rendering duration values of CMIS modules * Update the api's rendering duration values of CMIS modules --- sonic_platform_base/sonic_xcvr/codes/public/cmis.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sonic_platform_base/sonic_xcvr/codes/public/cmis.py b/sonic_platform_base/sonic_xcvr/codes/public/cmis.py index 7ad35610a..b75763575 100644 --- a/sonic_platform_base/sonic_xcvr/codes/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/codes/public/cmis.py @@ -144,6 +144,8 @@ class CmisCodes(Sff8024): 11: '600000', 12: '3000000', 13: '6000000', + 14: '0', + 15: '0' } # TODO: Add other codes From 285a4dd60d108e1996369d7ca4401671b571aa7e Mon Sep 17 00:00:00 2001 From: longhuan-cisco <84595962+longhuan-cisco@users.noreply.github.com> Date: Wed, 31 May 2023 13:44:57 -0700 Subject: [PATCH 11/24] Move tx_disable/tx_disabled_channel/rx_los/tx_fault to get_transceiver_status API (#359) * Add tx_disable in get_transceiver_status API * Remove tx_disable from bulk_status API * Add status API for non-cmis * Unify display format * Add testcases non-CMIS --- sonic_platform_base/sfp_base.py | 3 - .../sonic_xcvr/api/public/c_cmis.py | 2 +- .../sonic_xcvr/api/public/cmis.py | 23 +++--- .../sonic_xcvr/api/public/sff8436.py | 31 +++++--- .../sonic_xcvr/api/public/sff8472.py | 31 +++++--- .../sonic_xcvr/api/public/sff8636.py | 31 +++++--- .../sonic_xcvr/api/xcvr_api.py | 7 +- tests/sonic_xcvr/test_cmis.py | 75 ++++++++----------- tests/sonic_xcvr/test_sff8436.py | 46 ++++++++++++ tests/sonic_xcvr/test_sff8472.py | 38 ++++++++++ tests/sonic_xcvr/test_sff8636.py | 47 ++++++++++++ 11 files changed, 236 insertions(+), 98 deletions(-) diff --git a/sonic_platform_base/sfp_base.py b/sonic_platform_base/sfp_base.py index 0d98caa29..804b51ab8 100644 --- a/sonic_platform_base/sfp_base.py +++ b/sonic_platform_base/sfp_base.py @@ -161,9 +161,6 @@ def get_transceiver_bulk_status(self): tx_fault |BOOLEAN |TX fault status, True if has TX fault, False if not. reset_status |BOOLEAN |reset status, True if SFP in reset, False if not. lp_mode |BOOLEAN |low power mode status, True in lp mode, False if not. - tx_disable |BOOLEAN |TX disable status, True TX disabled, False if not. - tx_disabled_channel |HEX |disabled TX channels in hex, bits 0 to 3 represent channel 0 - | |to channel 3. temperature |INT |module temperature in Celsius voltage |INT |supply voltage in mV txbias |INT |TX Bias Current in mA, n is the channel number, diff --git a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py index e0f6908a8..82447929c 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py @@ -644,7 +644,7 @@ def get_transceiver_status(self): tuning_in_progress = BOOLEAN ; tuning in progress status wavelength_unlock_status = BOOLEAN ; laser unlocked status target_output_power_oor = BOOLEAN ; target output power out of range flag - fine_tuning_oor = BOOLEAN ; fine tuning out of range flag + fine_tuning_oor = BOOLEAN ; fine tuning out of range flag tuning_not_accepted = BOOLEAN ; tuning not accepted flag invalid_channel_num = BOOLEAN ; invalid channel number flag tuning_complete = BOOLEAN ; tuning complete flag diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 8bb26859f..2d04ff568 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -187,20 +187,12 @@ def get_transceiver_info_firmware_versions(self): return [ActiveFirmware, InactiveFirmware] def get_transceiver_bulk_status(self): - rx_los = self.get_rx_los() - tx_fault = self.get_tx_fault() - tx_disable = self.get_tx_disable() - tx_disabled_channel = self.get_tx_disable_channel() temp = self.get_module_temperature() voltage = self.get_voltage() tx_bias = self.get_tx_bias() rx_power = self.get_rx_power() tx_power = self.get_tx_power() - read_failed = rx_los is None or \ - tx_fault is None or \ - tx_disable is None or \ - tx_disabled_channel is None or \ - temp is None or \ + read_failed = temp is None or \ voltage is None or \ tx_bias is None or \ rx_power is None or \ @@ -209,15 +201,11 @@ def get_transceiver_bulk_status(self): return None bulk_status = { - "rx_los": all(rx_los) if self.get_rx_los_support() else 'N/A', - "tx_fault": all(tx_fault) if self.get_tx_fault_support() else 'N/A', - "tx_disabled_channel": tx_disabled_channel, "temperature": temp, "voltage": voltage } for i in range(1, self.NUM_CHANNELS + 1): - bulk_status["tx%ddisable" % i] = tx_disable[i-1] if self.get_tx_disable_support() else 'N/A' bulk_status["tx%dbias" % i] = tx_bias[i - 1] bulk_status["rx%dpower" % i] = float("{:.3f}".format(self.mw_to_dbm(rx_power[i - 1]))) if rx_power[i - 1] != 'N/A' else 'N/A' bulk_status["tx%dpower" % i] = float("{:.3f}".format(self.mw_to_dbm(tx_power[i - 1]))) if tx_power[i - 1] != 'N/A' else 'N/A' @@ -1659,6 +1647,8 @@ def get_transceiver_status(self): rxoutput_status_hostlane6 = BOOLEAN ; rx output status on host lane 6 rxoutput_status_hostlane7 = BOOLEAN ; rx output status on host lane 7 rxoutput_status_hostlane8 = BOOLEAN ; rx output status on host lane 8 + tx_disable = BOOLEAN ; tx disable status + tx_disabled_channel = INTEGER ; disabled TX channels txfault = BOOLEAN ; tx fault flag on media lane txlos_hostlane1 = BOOLEAN ; tx loss of signal flag on host lane 1 txlos_hostlane2 = BOOLEAN ; tx loss of signal flag on host lane 2 @@ -1777,6 +1767,13 @@ def get_transceiver_status(self): if rx_output_status_dict: for lane in range(1, self.NUM_CHANNELS+1): trans_status['rxoutput_status_hostlane%d' % lane] = rx_output_status_dict.get('RxOutputStatus%d' % lane) + tx_disabled_channel = self.get_tx_disable_channel() + if tx_disabled_channel is not None: + trans_status['tx_disabled_channel'] = tx_disabled_channel + tx_disable = self.get_tx_disable() + if tx_disable is not None: + for lane in range(1, self.NUM_CHANNELS+1): + trans_status['tx%ddisable' % lane] = tx_disable[lane - 1] tx_fault = self.get_tx_fault() if tx_fault: for lane in range(1, self.NUM_CHANNELS+1): diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8436.py b/sonic_platform_base/sonic_xcvr/api/public/sff8436.py index 4cf2aacce..152b1281e 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/sff8436.py +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8436.py @@ -68,21 +68,36 @@ def get_transceiver_info(self): return xcvr_info - def get_transceiver_bulk_status(self): + def get_transceiver_status(self): rx_los = self.get_rx_los() tx_fault = self.get_tx_fault() tx_disable = self.get_tx_disable() tx_disabled_channel = self.get_tx_disable_channel() + read_failed = rx_los is None or \ + tx_fault is None or \ + tx_disable is None or \ + tx_disabled_channel is None + if read_failed: + return None + + trans_status = dict() + for lane in range(1, len(rx_los) + 1): + trans_status['rxlos%d' % lane] = rx_los[lane - 1] + for lane in range(1, len(tx_fault) + 1): + trans_status['txfault%d' % lane] = tx_fault[lane - 1] + for lane in range(1, len(tx_disable) + 1): + trans_status['tx%ddisable' % lane] = tx_disable[lane - 1] + trans_status['tx_disabled_channel'] = tx_disabled_channel + + return trans_status + + def get_transceiver_bulk_status(self): temp = self.get_module_temperature() voltage = self.get_voltage() tx_bias = self.get_tx_bias() rx_power = self.get_rx_power() tx_power = self.get_tx_power() - read_failed = rx_los is None or \ - tx_fault is None or \ - tx_disable is None or \ - tx_disabled_channel is None or \ - temp is None or \ + read_failed = temp is None or \ voltage is None or \ tx_bias is None or \ rx_power is None or \ @@ -91,10 +106,6 @@ def get_transceiver_bulk_status(self): return None bulk_status = { - "rx_los": all(rx_los) if self.get_rx_los_support() else 'N/A', - "tx_fault": all(tx_fault) if self.get_tx_fault_support() else 'N/A', - "tx_disable": all(tx_disable), - "tx_disabled_channel": tx_disabled_channel, "temperature": temp, "voltage": voltage } diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8472.py b/sonic_platform_base/sonic_xcvr/api/public/sff8472.py index 4c025a712..f1a7daf2b 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/sff8472.py +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8472.py @@ -60,21 +60,36 @@ def get_transceiver_info(self): return xcvr_info - def get_transceiver_bulk_status(self): + def get_transceiver_status(self): rx_los = self.get_rx_los() tx_fault = self.get_tx_fault() tx_disable = self.get_tx_disable() tx_disabled_channel = self.get_tx_disable_channel() + read_failed = rx_los is None or \ + tx_fault is None or \ + tx_disable is None or \ + tx_disabled_channel is None + if read_failed: + return None + + trans_status = dict() + for lane in range(1, len(rx_los) + 1): + trans_status['rxlos%d' % lane] = rx_los[lane - 1] + for lane in range(1, len(tx_fault) + 1): + trans_status['txfault%d' % lane] = tx_fault[lane - 1] + for lane in range(1, len(tx_disable) + 1): + trans_status['tx%ddisable' % lane] = tx_disable[lane - 1] + trans_status['tx_disabled_channel'] = tx_disabled_channel + + return trans_status + + def get_transceiver_bulk_status(self): temp = self.get_module_temperature() voltage = self.get_voltage() tx_bias = self.get_tx_bias() rx_power = self.get_rx_power() tx_power = self.get_tx_power() - read_failed = rx_los is None or \ - tx_fault is None or \ - tx_disable is None or \ - tx_disabled_channel is None or \ - temp is None or \ + read_failed = temp is None or \ voltage is None or \ tx_bias is None or \ rx_power is None or \ @@ -83,10 +98,6 @@ def get_transceiver_bulk_status(self): return None bulk_status = { - "rx_los": all(rx_los) if self.get_rx_los_support() else 'N/A', - "tx_fault": all(tx_fault) if self.get_tx_fault_support() else 'N/A', - "tx_disable": all(tx_disable), - "tx_disabled_channel": tx_disabled_channel, "temperature": temp, "voltage": voltage } diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8636.py b/sonic_platform_base/sonic_xcvr/api/public/sff8636.py index e14c07da5..9c4d34214 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/sff8636.py +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8636.py @@ -75,21 +75,36 @@ def get_transceiver_info(self): return xcvr_info - def get_transceiver_bulk_status(self): + def get_transceiver_status(self): rx_los = self.get_rx_los() tx_fault = self.get_tx_fault() tx_disable = self.get_tx_disable() tx_disabled_channel = self.get_tx_disable_channel() + read_failed = rx_los is None or \ + tx_fault is None or \ + tx_disable is None or \ + tx_disabled_channel is None + if read_failed: + return None + + trans_status = dict() + for lane in range(1, len(rx_los) + 1): + trans_status['rxlos%d' % lane] = rx_los[lane - 1] + for lane in range(1, len(tx_fault) + 1): + trans_status['txfault%d' % lane] = tx_fault[lane - 1] + for lane in range(1, len(tx_disable) + 1): + trans_status['tx%ddisable' % lane] = tx_disable[lane - 1] + trans_status['tx_disabled_channel'] = tx_disabled_channel + + return trans_status + + def get_transceiver_bulk_status(self): temp = self.get_module_temperature() voltage = self.get_voltage() tx_bias = self.get_tx_bias() rx_power = self.get_rx_power() tx_power = self.get_tx_power() - read_failed = rx_los is None or \ - tx_fault is None or \ - tx_disable is None or \ - tx_disabled_channel is None or \ - temp is None or \ + read_failed = temp is None or \ voltage is None or \ tx_bias is None or \ rx_power is None or \ @@ -98,10 +113,6 @@ def get_transceiver_bulk_status(self): return None bulk_status = { - "rx_los": all(rx_los) if self.get_rx_los_support() else 'N/A', - "tx_fault": all(tx_fault) if self.get_tx_fault_support() else 'N/A', - "tx_disable": all(tx_disable), - "tx_disabled_channel": tx_disabled_channel, "temperature": temp, "voltage": voltage } diff --git a/sonic_platform_base/sonic_xcvr/api/xcvr_api.py b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py index 57b77f691..eaaed35da 100644 --- a/sonic_platform_base/sonic_xcvr/api/xcvr_api.py +++ b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py @@ -89,11 +89,6 @@ def get_transceiver_bulk_status(self): ======================================================================== keys |Value Format |Information ---------------------------|---------------|---------------------------- - rx_los |bool |RX loss-of-signal status, True if has RX los, False if not. - tx_fault |bool |TX fault status, True if has TX fault, False if not. - tx_disable |bool |TX disable status, True TX disabled, False if not. - tx_disabled_channel |int |disabled TX channels in hex, bits 0 to 3 represent channel 0 - | |to channel 3 (for example). temperature |float |module temperature in Celsius voltage |float |supply voltage in mV txbias |float |TX Bias Current in mA, n is the channel number, @@ -145,7 +140,7 @@ def get_transceiver_threshold_info(self): def get_transceiver_status(self): """ - Retrieves transceiver status of this SFP (applicable for CMIS/C-CMIS) + Retrieves transceiver status of this SFP Returns: A dict which may contain following keys/values (there could be more for C-CMIS) : diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index beb712c56..9c181b087 100644 --- a/tests/sonic_xcvr/test_cmis.py +++ b/tests/sonic_xcvr/test_cmis.py @@ -1323,10 +1323,6 @@ def test_get_transceiver_info(self, mock_response, expected): @pytest.mark.parametrize("mock_response, expected",[ ( [ - [False, False, False, False, False, False, False, False], - [False, False, False, False, False, False, False, False], - [False, False, False, False, False, False, False, False], - 0, 50, 3.3, [70, 70, 70, 70, 70, 70, 70, 70], @@ -1351,11 +1347,6 @@ def test_get_transceiver_info(self, mock_response, expected): 'rx5power': -10.0, 'rx6power': -10.0, 'rx7power': -10.0, 'rx8power': -10.0, 'tx1bias': 70, 'tx2bias': 70, 'tx3bias': 70, 'tx4bias': 70, 'tx5bias': 70, 'tx6bias': 70, 'tx7bias': 70, 'tx8bias': 70, - 'rx_los': False, - 'tx_fault': False, - 'tx1disable': False, 'tx2disable': False, 'tx3disable': False, 'tx4disable': False, - 'tx5disable': False, 'tx6disable': False, 'tx7disable': False, 'tx8disable': False, - 'tx_disabled_channel': 0, 'laser_temperature': 40, 'prefec_ber': 0.001, 'postfec_ber_min': 0, @@ -1366,10 +1357,6 @@ def test_get_transceiver_info(self, mock_response, expected): ), ( [ - ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], - ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], - ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], - 'N/A', 50, 3.3, ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], None, @@ -1382,10 +1369,6 @@ def test_get_transceiver_info(self, mock_response, expected): ), ( [ - ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], - ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], - ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], - 'N/A', 50, 3.3, ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A'], @@ -1403,50 +1386,37 @@ def test_get_transceiver_info(self, mock_response, expected): 'rx5power': 'N/A', 'rx6power': 'N/A', 'rx7power': 'N/A', 'rx8power': 'N/A', 'tx1bias': 'N/A', 'tx2bias': 'N/A', 'tx3bias': 'N/A', 'tx4bias': 'N/A', 'tx5bias': 'N/A', 'tx6bias': 'N/A', 'tx7bias': 'N/A', 'tx8bias': 'N/A', - 'rx_los': 'N/A', - 'tx_fault': 'N/A', - 'tx1disable': 'N/A', 'tx2disable': 'N/A', 'tx3disable': 'N/A', 'tx4disable': 'N/A', - 'tx5disable': 'N/A', 'tx6disable': 'N/A', 'tx7disable': 'N/A', 'tx8disable': 'N/A', - 'tx_disabled_channel': 'N/A', 'laser_temperature': 40 } ) ]) def test_get_transceiver_bulk_status(self, mock_response, expected): - self.api.get_rx_los = MagicMock() - self.api.get_rx_los.return_value = mock_response[0] - self.api.get_tx_fault = MagicMock() - self.api.get_tx_fault.return_value = mock_response[1] - self.api.get_tx_disable = MagicMock() - self.api.get_tx_disable.return_value = mock_response[2] - self.api.get_tx_disable_channel = MagicMock() - self.api.get_tx_disable_channel.return_value = mock_response[3] self.api.get_module_temperature = MagicMock() - self.api.get_module_temperature.return_value = mock_response[4] + self.api.get_module_temperature.return_value = mock_response[0] self.api.get_voltage = MagicMock() - self.api.get_voltage.return_value = mock_response[5] + self.api.get_voltage.return_value = mock_response[1] self.api.get_tx_bias = MagicMock() - self.api.get_tx_bias.return_value = mock_response[6] + self.api.get_tx_bias.return_value = mock_response[2] self.api.get_rx_power = MagicMock() - self.api.get_rx_power.return_value = mock_response[7] + self.api.get_rx_power.return_value = mock_response[3] self.api.get_tx_power = MagicMock() - self.api.get_tx_power.return_value = mock_response[8] + self.api.get_tx_power.return_value = mock_response[4] self.api.get_rx_los_support = MagicMock() - self.api.get_rx_los_support.return_value = mock_response[9] + self.api.get_rx_los_support.return_value = mock_response[5] self.api.get_tx_fault_support = MagicMock() - self.api.get_tx_fault_support.return_value = mock_response[10] + self.api.get_tx_fault_support.return_value = mock_response[6] self.api.get_tx_disable_support = MagicMock() - self.api.get_tx_disable_support.return_value = mock_response[11] + self.api.get_tx_disable_support.return_value = mock_response[7] self.api.get_tx_bias_support = MagicMock() - self.api.get_tx_bias_support.return_value = mock_response[12] + self.api.get_tx_bias_support.return_value = mock_response[8] self.api.get_tx_power_support = MagicMock() - self.api.get_tx_power_support.return_value = mock_response[13] + self.api.get_tx_power_support.return_value = mock_response[9] self.api.get_rx_power_support = MagicMock() - self.api.get_rx_power_support.return_value = mock_response[14] + self.api.get_rx_power_support.return_value = mock_response[10] self.api.get_laser_temperature = MagicMock() - self.api.get_laser_temperature.return_value = mock_response[15] + self.api.get_laser_temperature.return_value = mock_response[11] self.api.get_vdm = MagicMock() - self.api.get_vdm.return_value = mock_response[16] + self.api.get_vdm.return_value = mock_response[12] result = self.api.get_transceiver_bulk_status() assert result == expected @@ -1656,7 +1626,8 @@ def test_get_transceiver_threshold_info(self, mock_response, expected): { 'Pre-FEC BER Average Media Input':{1:[0.001, 0.0125, 0, 0.01, 0, False, False, False, False]}, 'Errored Frames Average Media Input':{1:[0, 1, 0, 1, 0, False, False, False, False]}, - } + }, + 0, [False, False, False, False, False, False, False, False] ], { 'module_state': 'ModuleReady', @@ -1688,6 +1659,15 @@ def test_get_transceiver_threshold_info(self, mock_response, expected): 'rxoutput_status_hostlane6': True, 'rxoutput_status_hostlane7': True, 'rxoutput_status_hostlane8': True, + "tx_disabled_channel": 0, + "tx1disable": False, + "tx2disable": False, + "tx3disable": False, + "tx4disable": False, + "tx5disable": False, + "tx6disable": False, + "tx7disable": False, + "tx8disable": False, 'txfault1': False, 'txfault2': False, 'txfault3': False, @@ -1890,7 +1870,8 @@ def test_get_transceiver_threshold_info(self, mock_response, expected): { 'Pre-FEC BER Average Media Input':{1:[0.001, 0.0125, 0, 0.01, 0, False, False, False, False]}, 'Errored Frames Average Media Input':{1:[0, 1, 0, 1, 0, False, False, False, False]}, - } + }, + None, None ], { 'module_state': 'ModuleReady', @@ -1949,6 +1930,10 @@ def test_get_transceiver_status(self, mock_response, expected): self.api.get_tx_bias_flag.return_value = mock_response[18] self.api.get_vdm = MagicMock() self.api.get_vdm.return_value = mock_response[19] + self.api.get_tx_disable_channel = MagicMock() + self.api.get_tx_disable_channel.return_value = mock_response[20] + self.api.get_tx_disable = MagicMock() + self.api.get_tx_disable.return_value = mock_response[21] result = self.api.get_transceiver_status() assert result == expected diff --git a/tests/sonic_xcvr/test_sff8436.py b/tests/sonic_xcvr/test_sff8436.py index 053175efd..1c192e7d0 100644 --- a/tests/sonic_xcvr/test_sff8436.py +++ b/tests/sonic_xcvr/test_sff8436.py @@ -27,6 +27,7 @@ def test_api(self): self.api.get_transceiver_info() self.api.get_transceiver_bulk_status() self.api.get_transceiver_threshold_info() + self.api.get_transceiver_status() self.api.get_rx_los() self.api.get_tx_fault() self.api.get_tx_disable() @@ -155,3 +156,48 @@ def test_set_lpmode(self): self.api.get_power_override_support.return_value = False assert not self.api.set_lpmode(True) + @pytest.mark.parametrize("mock_response, expected",[ + ( + [ + [False, False, False, False], + [False, False, False, False], + 0, + [False, False, False, False] + ], + { + "tx_disabled_channel": 0, + "tx1disable": False, + "tx2disable": False, + "tx3disable": False, + "tx4disable": False, + 'txfault1': False, + 'txfault2': False, + 'txfault3': False, + 'txfault4': False, + 'rxlos1': False, + 'rxlos2': False, + 'rxlos3': False, + 'rxlos4': False, + } + ), + ( + [ + None, + None, + None, + None + ], + None + ) + ]) + def test_get_transceiver_status(self, mock_response, expected): + self.api.get_rx_los = MagicMock() + self.api.get_rx_los.return_value = mock_response[0] + self.api.get_tx_fault = MagicMock() + self.api.get_tx_fault.return_value = mock_response[1] + self.api.get_tx_disable_channel = MagicMock() + self.api.get_tx_disable_channel.return_value = mock_response[2] + self.api.get_tx_disable = MagicMock() + self.api.get_tx_disable.return_value = mock_response[3] + result = self.api.get_transceiver_status() + assert result == expected diff --git a/tests/sonic_xcvr/test_sff8472.py b/tests/sonic_xcvr/test_sff8472.py index 35d2519ec..7a10c6cf6 100644 --- a/tests/sonic_xcvr/test_sff8472.py +++ b/tests/sonic_xcvr/test_sff8472.py @@ -29,6 +29,7 @@ def test_api(self): self.api.get_transceiver_info() self.api.get_transceiver_bulk_status() self.api.get_transceiver_threshold_info() + self.api.get_transceiver_status() self.api.get_rx_los() self.api.get_tx_fault() self.api.get_tx_disable() @@ -205,3 +206,40 @@ def mock_read_raw(offset, size): except: assert 0, traceback.format_exc() run_num -= 1 + + @pytest.mark.parametrize("mock_response, expected",[ + ( + [ + [False], + [False], + 0, + [False] + ], + { + "tx_disabled_channel": 0, + "tx1disable": False, + 'txfault1': False, + 'rxlos1': False, + } + ), + ( + [ + None, + None, + None, + None + ], + None + ) + ]) + def test_get_transceiver_status(self, mock_response, expected): + self.api.get_rx_los = MagicMock() + self.api.get_rx_los.return_value = mock_response[0] + self.api.get_tx_fault = MagicMock() + self.api.get_tx_fault.return_value = mock_response[1] + self.api.get_tx_disable_channel = MagicMock() + self.api.get_tx_disable_channel.return_value = mock_response[2] + self.api.get_tx_disable = MagicMock() + self.api.get_tx_disable.return_value = mock_response[3] + result = self.api.get_transceiver_status() + assert result == expected diff --git a/tests/sonic_xcvr/test_sff8636.py b/tests/sonic_xcvr/test_sff8636.py index a347e8fa2..7b337b9ae 100644 --- a/tests/sonic_xcvr/test_sff8636.py +++ b/tests/sonic_xcvr/test_sff8636.py @@ -26,6 +26,7 @@ def test_api(self): self.api.get_transceiver_info() self.api.get_transceiver_bulk_status() self.api.get_transceiver_threshold_info() + self.api.get_transceiver_status() self.api.get_rx_los() self.api.get_tx_fault() self.api.get_tx_disable() @@ -154,3 +155,49 @@ def test_set_lpmode(self): self.api.get_lpmode_support.return_value = False self.api.get_power_override_support.return_value = False assert not self.api.set_lpmode(True) + + @pytest.mark.parametrize("mock_response, expected",[ + ( + [ + [False, False, False, False], + [False, False, False, False], + 0, + [False, False, False, False] + ], + { + "tx_disabled_channel": 0, + "tx1disable": False, + "tx2disable": False, + "tx3disable": False, + "tx4disable": False, + 'txfault1': False, + 'txfault2': False, + 'txfault3': False, + 'txfault4': False, + 'rxlos1': False, + 'rxlos2': False, + 'rxlos3': False, + 'rxlos4': False, + } + ), + ( + [ + None, + None, + None, + None + ], + None + ) + ]) + def test_get_transceiver_status(self, mock_response, expected): + self.api.get_rx_los = MagicMock() + self.api.get_rx_los.return_value = mock_response[0] + self.api.get_tx_fault = MagicMock() + self.api.get_tx_fault.return_value = mock_response[1] + self.api.get_tx_disable_channel = MagicMock() + self.api.get_tx_disable_channel.return_value = mock_response[2] + self.api.get_tx_disable = MagicMock() + self.api.get_tx_disable.return_value = mock_response[3] + result = self.api.get_transceiver_status() + assert result == expected From d05ebd3081b43821221b0f408f68dbb4396be370 Mon Sep 17 00:00:00 2001 From: rajann Date: Fri, 9 Jun 2023 09:49:15 -0700 Subject: [PATCH 12/24] Add DPTxTurnOnDuration and DPTxTurnOffDuration CMIS api (#378) * Update consts.py Include DPTxTurnOnDuration and DPTxTurnOffDuration * Update txturnon and txturnoff duration * update txturnon and txturnoff duration apis --- .../sonic_xcvr/api/public/cmis.py | 18 ++++++++++++ .../sonic_xcvr/fields/consts.py | 2 ++ .../sonic_xcvr/mem_maps/public/cmis.py | 6 ++++ tests/sonic_xcvr/test_cmis.py | 28 +++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 2d04ff568..36657ddd7 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -709,6 +709,24 @@ def get_datapath_deinit_duration(self): duration = self.xcvr_eeprom.read(consts.DP_PATH_DEINIT_DURATION) return float(duration) if duration is not None else 0 + def get_datapath_tx_turnon_duration(self): + ''' + This function returns the duration of datapath tx turnon + ''' + if self.is_flat_memory(): + return 0 + duration = self.xcvr_eeprom.read(consts.DP_TX_TURNON_DURATION) + return float(duration) if duration is not None else 0 + + def get_datapath_tx_turnoff_duration(self): + ''' + This function returns the duration of datapath tx turnoff + ''' + if self.is_flat_memory(): + return 0 + duration = self.xcvr_eeprom.read(consts.DP_TX_TURNOFF_DURATION) + return float(duration) if duration is not None else 0 + def get_module_pwr_up_duration(self): ''' This function returns the duration of module power up diff --git a/sonic_platform_base/sonic_xcvr/fields/consts.py b/sonic_platform_base/sonic_xcvr/fields/consts.py index d5cf4f608..c638f19b6 100644 --- a/sonic_platform_base/sonic_xcvr/fields/consts.py +++ b/sonic_platform_base/sonic_xcvr/fields/consts.py @@ -174,6 +174,8 @@ DP_PATH_DEINIT_DURATION = "DPDeinitDuration" MODULE_PWRUP_DURATION = "ModulePowerUpDuration" MODULE_PWRDN_DURATION = "ModulePowerDownDuration" +DP_TX_TURNON_DURATION = "DPTxTurnOnDuration" +DP_TX_TURNOFF_DURATION = "DPTxTurnOffDuration" # DOM TRANS_DOM_FIELD = "TransceiverDom" diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py index 2f220aa1c..512ed0776 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py @@ -126,6 +126,12 @@ def __init__(self, codes): CodeRegField(consts.MODULE_PWRDN_DURATION, self.getaddr(0x1, 167), self.codes.DP_PATH_TIMINGS, *(RegBitField("Bit%d" % (bit), bit) for bit in range (4, 8)) ), + CodeRegField(consts.DP_TX_TURNON_DURATION, self.getaddr(0x1, 168), self.codes.DP_PATH_TIMINGS, + *(RegBitField("Bit%d" % (bit), bit) for bit in range (0, 4)) + ), + CodeRegField(consts.DP_TX_TURNOFF_DURATION, self.getaddr(0x1, 168), self.codes.DP_PATH_TIMINGS, + *(RegBitField("Bit%d" % (bit), bit) for bit in range (4, 8)) + ), NumberRegField(consts.MEDIA_LANE_ASSIGNMENT_OPTION, self.getaddr(0x1, 176), format="B", size=1), RegGroupField(consts.ACTIVE_APSEL_CODE, diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index 9c181b087..a3cb0b345 100644 --- a/tests/sonic_xcvr/test_cmis.py +++ b/tests/sonic_xcvr/test_cmis.py @@ -662,6 +662,34 @@ def test_get_datapath_deinit_duration(self, mock_response1, mock_response2, expe result = self.api.get_datapath_deinit_duration() assert result == expected + @pytest.mark.parametrize("mock_response1, mock_response2, expected", [ + (True, '10', 0 ), + (False, None, 0), + (False, '8', 8.0), + (False, '5000000', 5000000.0), + ]) + def test_get_datapath_tx_turnon_duration(self, mock_response1, mock_response2, expected): + self.api.is_flat_memory = MagicMock() + self.api.is_flat_memory.return_value = mock_response1 + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.read.return_value = mock_response2 + result = self.api.get_datapath_tx_turnon_duration() + assert result == expected + + @pytest.mark.parametrize("mock_response1, mock_response2, expected", [ + (True, '1', 0 ), + (False, None, 0), + (False, '6', 6.0), + (False, '80000', 80000.0), + ]) + def test_get_datapath_tx_turnoff_duration(self, mock_response1, mock_response2, expected): + self.api.is_flat_memory = MagicMock() + self.api.is_flat_memory.return_value = mock_response1 + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.read.return_value = mock_response2 + result = self.api.get_datapath_tx_turnoff_duration() + assert result == expected + @pytest.mark.parametrize("mock_response1, mock_response2, expected", [ (True, '10', 0 ), (False, None, 0), From 74ddd4e8a17c0ecd94adc3202bd0a6f32dfb1432 Mon Sep 17 00:00:00 2001 From: Junchao-Mellanox <57339448+Junchao-Mellanox@users.noreply.github.com> Date: Tue, 20 Jun 2023 03:05:02 +0800 Subject: [PATCH 13/24] Fix issue: should use 'Value' column to calculate the health percentage (#381) --- sonic_platform_base/sonic_ssd/ssd_generic.py | 6 +-- tests/ssd_generic_test.py | 56 ++++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/sonic_platform_base/sonic_ssd/ssd_generic.py b/sonic_platform_base/sonic_ssd/ssd_generic.py index 95ed5c332..1541d31f3 100644 --- a/sonic_platform_base/sonic_ssd/ssd_generic.py +++ b/sonic_platform_base/sonic_ssd/ssd_generic.py @@ -124,7 +124,7 @@ def parse_innodisk_info(self): if self.vendor_ssd_info: self.health = self._parse_re('Health:\s*(.+?)%', self.vendor_ssd_info) self.temperature = self._parse_re('Temperature\s*\[\s*(.+?)\]', self.vendor_ssd_info) - + if self.health == NOT_AVAILABLE: health_raw = self.parse_id_number(INNODISK_HEALTH_ID) if health_raw == NOT_AVAILABLE: @@ -150,10 +150,10 @@ def parse_virtium_info(self): pass else: try: - self.health = float(self._parse_re('Remaining_Life_Left\s*\d*\s*(\d+?)\s+', self.vendor_ssd_info)) + self.health = float(self._parse_re('Remaining_Life_Left\s*\d*\s*\d*\s*(\d+?)\s+', self.vendor_ssd_info)) except ValueError: pass - + def fetch_vendor_ssd_info(self, diskdev, model): self.vendor_ssd_info = self._execute_shell(self.vendor_ssd_utility[model]["utility"].format(diskdev)) diff --git a/tests/ssd_generic_test.py b/tests/ssd_generic_test.py index 630d59c6f..889205e11 100644 --- a/tests/ssd_generic_test.py +++ b/tests/ssd_generic_test.py @@ -98,7 +98,7 @@ Offline data collection status: (0x00) Offline data collection activity was never started. Auto Offline Data Collection: Disabled. -Total time to complete Offline +Total time to complete Offline data collection: ( 32) seconds. Offline data collection capabilities: (0x00) Offline data collection not supported. @@ -178,7 +178,7 @@ Offline data collection status: (0x00) Offline data collection activity was never started. Auto Offline Data Collection: Disabled. -Total time to complete Offline +Total time to complete Offline data collection: ( 32) seconds. Offline data collection capabilities: (0x00) Offline data collection not supported. @@ -232,29 +232,29 @@ output_Innodisk_vendor_info = """******************************************************************************************** * Innodisk iSMART V3.9.41 2018/05/25 * ******************************************************************************************** -Model Name: InnoDisk Corp. - mSATA 3ME -FW Version: S140714 +Model Name: InnoDisk Corp. - mSATA 3ME +FW Version: S140714 Serial Number: 20171126AAAA11730156 -Health: 82.34% +Health: 82.34% Capacity: 29.818199 GB -P/E Cycle: 3000 -Lifespan : 0 (Years : 0 Months : 0 Days : 0) -Write Protect: Disable -InnoRobust: Enable +P/E Cycle: 3000 +Lifespan : 0 (Years : 0 Months : 0 Days : 0) +Write Protect: Disable +InnoRobust: Enable -------------------------------------------------------------------------------------------- -ID SMART Attributes Value Raw Value +ID SMART Attributes Value Raw Value -------------------------------------------------------------------------------------------- -[09] Power On Hours [32474] [0902006464DA7E0000000000] -[0C] Power Cycle Count [ 297] [0C0200646429010000000000] -[AA] Total Bad Block Count [ 47] [AA0300646400002F00000000] -[AD] Erase Count Max. [ 7280] [AD02006464181C701C000000] -[AD] Erase Count Avg. [ 7192] [AD02006464181C701C000000] -[C2] Temperature [ 0] [000000000000000000000000] -[EB] Later Bad Block [ 0] [EB0200640000000000000000] -[EB] Read Block [ 0] [EB0200640000000000000000] -[EB] Write Block [ 0] [EB0200640000000000000000] -[EB] Erase Block [ 0] [EB0200640000000000000000] -[EC] Unstable Power Count [ 0] [EC0200646400000000000000] +[09] Power On Hours [32474] [0902006464DA7E0000000000] +[0C] Power Cycle Count [ 297] [0C0200646429010000000000] +[AA] Total Bad Block Count [ 47] [AA0300646400002F00000000] +[AD] Erase Count Max. [ 7280] [AD02006464181C701C000000] +[AD] Erase Count Avg. [ 7192] [AD02006464181C701C000000] +[C2] Temperature [ 0] [000000000000000000000000] +[EB] Later Bad Block [ 0] [EB0200640000000000000000] +[EB] Read Block [ 0] [EB0200640000000000000000] +[EB] Write Block [ 0] [EB0200640000000000000000] +[EB] Erase Block [ 0] [EB0200640000000000000000] +[EC] Unstable Power Count [ 0] [EC0200646400000000000000] """ output_lack_info_ssd = """smartctl 7.2 2020-12-30 r5155 [x86_64-linux-5.10.0-8-2-amd64] (local build) @@ -292,7 +292,7 @@ Offline data collection status: (0x02) Offline data collection activity was completed without error. Auto Offline Data Collection: Disabled. -Total time to complete Offline +Total time to complete Offline data collection: ( 32) seconds. Offline data collection capabilities: (0x00) Offline data collection not supported. @@ -372,9 +372,9 @@ was never started. Auto Offline Data Collection: Disabled. Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever + without error or no self-test has ever been run. -Total time to complete Offline +Total time to complete Offline data collection: ( 0) seconds. Offline data collection capabilities: (0x73) SMART execute Offline immediate. @@ -390,7 +390,7 @@ Supports SMART auto save timer. Error logging capability: (0x01) Error logging supported. General Purpose Logging supported. -Short self-test routine +Short self-test routine recommended polling time: ( 2) minutes. Extended self-test routine recommended polling time: ( 15) minutes. @@ -484,7 +484,7 @@ 241 Total_LBAs_Written 0 302116658 100 100 0 242 Total_LBAs_Read 0 45608497 100 100 0 247 Reserved_Attribute 0 347463680 100 100 0 -248 Remaining_Life_Left 0 1 100 100 0 +248 Remaining_Life_Left 0 0 1 100 0 """ output_virtium_no_remain_life = """ @@ -599,7 +599,7 @@ def test_Innodisk_missing_names_ssd(self): Innodisk_ssd.parse_vendor_ssd_info('InnoDisk') assert(Innodisk_ssd.get_health() == '94') assert(Innodisk_ssd.get_temperature() == '39') - + @mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell', mock.MagicMock(return_value=output_Innodisk_missing_names_ssd)) def test_Innodisk_missing_names_ssd_2(self): # Test parsing Innodisk ssd info @@ -608,7 +608,7 @@ def test_Innodisk_missing_names_ssd_2(self): Innodisk_ssd.parse_vendor_ssd_info('InnoDisk') assert(Innodisk_ssd.get_health() == '94') assert(Innodisk_ssd.get_temperature() == '39') - + @mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell') def test_virtium_ssd(self, mock_exec): From 4d270f07e040e35b7fc90be6e38bd5feb6e163ed Mon Sep 17 00:00:00 2001 From: Mai Bui Date: Mon, 19 Jun 2023 17:41:42 -0400 Subject: [PATCH 14/24] [actions] Support Semgrep by Github Actions (#383) **Why I did it** [Semgrep](https://github.com/returntocorp/semgrep) is a static analysis tool to find security vulnerabilities. When opening a PR or commtting to PR, Semgrep performs a diff-aware scanning, which scans changed files in PRs. When merging PR, Semgrep performs a full scan on master branch and report all findings. Ref: - [Supported Language](https://semgrep.dev/docs/supported-languages/#language-maturity) - [Semgrep Rules](https://registry.semgrep.dev/rule) **How I did it** Integrate Semgrep into this repository by committing a job configuration file --- .github/workflows/semgrep.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/semgrep.yml diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 000000000..975769a50 --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,22 @@ +name: Semgrep + +on: + pull_request: {} + push: + branches: + - master + - '201[7-9][0-1][0-9]' + - '202[0-9][0-1][0-9]' + +jobs: + semgrep: + if: github.repository_owner == 'sonic-net' + name: Semgrep + runs-on: ubuntu-latest + container: + image: returntocorp/semgrep + steps: + - uses: actions/checkout@v3 + - run: semgrep ci + env: + SEMGREP_RULES: p/default From 10af810fa1d40347ab8556e79374e6c94a99a22d Mon Sep 17 00:00:00 2001 From: snider-nokia <76123698+snider-nokia@users.noreply.github.com> Date: Fri, 30 Jun 2023 17:49:39 -0400 Subject: [PATCH 15/24] More prevention of fatal exception caused by VDM dictionary missing fields when a transceiver has just been pulled (#376) --- .../sonic_xcvr/api/public/c_cmis.py | 159 ++++++++---------- 1 file changed, 69 insertions(+), 90 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py index 82447929c..6a94df544 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py @@ -361,21 +361,15 @@ def get_transceiver_bulk_status(self): trans_dom['bias_yp'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][0] trans_dom['cd_shortlink'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][0] trans_dom['cd_longlink'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][0] - except KeyError: - pass - trans_dom['dgd'] = self.vdm_dict['DGD [ps]'][1][0] - try: + trans_dom['dgd'] = self.vdm_dict['DGD [ps]'][1][0] trans_dom['sopmd'] = self.vdm_dict['SOPMD [ps^2]'][1][0] - except KeyError: - pass - trans_dom['soproc'] = self.vdm_dict['SOP ROC [krad/s]'][1][0] - trans_dom['pdl'] = self.vdm_dict['PDL [dB]'][1][0] - trans_dom['osnr'] = self.vdm_dict['OSNR [dB]'][1][0] - trans_dom['esnr'] = self.vdm_dict['eSNR [dB]'][1][0] - trans_dom['cfo'] = self.vdm_dict['CFO [MHz]'][1][0] - trans_dom['tx_curr_power'] = self.vdm_dict['Tx Power [dBm]'][1][0] - trans_dom['rx_tot_power'] = self.vdm_dict['Rx Total Power [dBm]'][1][0] - try: + trans_dom['soproc'] = self.vdm_dict['SOP ROC [krad/s]'][1][0] + trans_dom['pdl'] = self.vdm_dict['PDL [dB]'][1][0] + trans_dom['osnr'] = self.vdm_dict['OSNR [dB]'][1][0] + trans_dom['esnr'] = self.vdm_dict['eSNR [dB]'][1][0] + trans_dom['cfo'] = self.vdm_dict['CFO [MHz]'][1][0] + trans_dom['tx_curr_power'] = self.vdm_dict['Tx Power [dBm]'][1][0] + trans_dom['rx_tot_power'] = self.vdm_dict['Rx Total Power [dBm]'][1][0] trans_dom['rx_sig_power'] = self.vdm_dict['Rx Signal Power [dBm]'][1][0] except KeyError: pass @@ -529,44 +523,38 @@ def get_transceiver_threshold_info(self): trans_dom_th['cdlonglowalarm'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][2] trans_dom_th['cdlonghighwarning'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][3] trans_dom_th['cdlonglowwarning'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][4] - except KeyError: - pass - trans_dom_th['dgdhighalarm'] = self.vdm_dict['DGD [ps]'][1][1] - trans_dom_th['dgdlowalarm'] = self.vdm_dict['DGD [ps]'][1][2] - trans_dom_th['dgdhighwarning'] = self.vdm_dict['DGD [ps]'][1][3] - trans_dom_th['dgdlowwarning'] = self.vdm_dict['DGD [ps]'][1][4] - try: + trans_dom_th['dgdhighalarm'] = self.vdm_dict['DGD [ps]'][1][1] + trans_dom_th['dgdlowalarm'] = self.vdm_dict['DGD [ps]'][1][2] + trans_dom_th['dgdhighwarning'] = self.vdm_dict['DGD [ps]'][1][3] + trans_dom_th['dgdlowwarning'] = self.vdm_dict['DGD [ps]'][1][4] trans_dom_th['sopmdhighalarm'] = self.vdm_dict['SOPMD [ps^2]'][1][1] trans_dom_th['sopmdlowalarm'] = self.vdm_dict['SOPMD [ps^2]'][1][2] trans_dom_th['sopmdhighwarning'] = self.vdm_dict['SOPMD [ps^2]'][1][3] trans_dom_th['sopmdlowwarning'] = self.vdm_dict['SOPMD [ps^2]'][1][4] - except KeyError: - pass - trans_dom_th['pdlhighalarm'] = self.vdm_dict['PDL [dB]'][1][1] - trans_dom_th['pdllowalarm'] = self.vdm_dict['PDL [dB]'][1][2] - trans_dom_th['pdlhighwarning'] = self.vdm_dict['PDL [dB]'][1][3] - trans_dom_th['pdllowwarning'] = self.vdm_dict['PDL [dB]'][1][4] - trans_dom_th['osnrhighalarm'] = self.vdm_dict['OSNR [dB]'][1][1] - trans_dom_th['osnrlowalarm'] = self.vdm_dict['OSNR [dB]'][1][2] - trans_dom_th['osnrhighwarning'] = self.vdm_dict['OSNR [dB]'][1][3] - trans_dom_th['osnrlowwarning'] = self.vdm_dict['OSNR [dB]'][1][4] - trans_dom_th['esnrhighalarm'] = self.vdm_dict['eSNR [dB]'][1][1] - trans_dom_th['esnrlowalarm'] = self.vdm_dict['eSNR [dB]'][1][2] - trans_dom_th['esnrhighwarning'] = self.vdm_dict['eSNR [dB]'][1][3] - trans_dom_th['esnrlowwarning'] = self.vdm_dict['eSNR [dB]'][1][4] - trans_dom_th['cfohighalarm'] = self.vdm_dict['CFO [MHz]'][1][1] - trans_dom_th['cfolowalarm'] = self.vdm_dict['CFO [MHz]'][1][2] - trans_dom_th['cfohighwarning'] = self.vdm_dict['CFO [MHz]'][1][3] - trans_dom_th['cfolowwarning'] = self.vdm_dict['CFO [MHz]'][1][4] - trans_dom_th['txcurrpowerhighalarm'] = self.vdm_dict['Tx Power [dBm]'][1][1] - trans_dom_th['txcurrpowerlowalarm'] = self.vdm_dict['Tx Power [dBm]'][1][2] - trans_dom_th['txcurrpowerhighwarning'] = self.vdm_dict['Tx Power [dBm]'][1][3] - trans_dom_th['txcurrpowerlowwarning'] = self.vdm_dict['Tx Power [dBm]'][1][4] - trans_dom_th['rxtotpowerhighalarm'] = self.vdm_dict['Rx Total Power [dBm]'][1][1] - trans_dom_th['rxtotpowerlowalarm'] = self.vdm_dict['Rx Total Power [dBm]'][1][2] - trans_dom_th['rxtotpowerhighwarning'] = self.vdm_dict['Rx Total Power [dBm]'][1][3] - trans_dom_th['rxtotpowerlowwarning'] = self.vdm_dict['Rx Total Power [dBm]'][1][4] - try: + trans_dom_th['pdlhighalarm'] = self.vdm_dict['PDL [dB]'][1][1] + trans_dom_th['pdllowalarm'] = self.vdm_dict['PDL [dB]'][1][2] + trans_dom_th['pdlhighwarning'] = self.vdm_dict['PDL [dB]'][1][3] + trans_dom_th['pdllowwarning'] = self.vdm_dict['PDL [dB]'][1][4] + trans_dom_th['osnrhighalarm'] = self.vdm_dict['OSNR [dB]'][1][1] + trans_dom_th['osnrlowalarm'] = self.vdm_dict['OSNR [dB]'][1][2] + trans_dom_th['osnrhighwarning'] = self.vdm_dict['OSNR [dB]'][1][3] + trans_dom_th['osnrlowwarning'] = self.vdm_dict['OSNR [dB]'][1][4] + trans_dom_th['esnrhighalarm'] = self.vdm_dict['eSNR [dB]'][1][1] + trans_dom_th['esnrlowalarm'] = self.vdm_dict['eSNR [dB]'][1][2] + trans_dom_th['esnrhighwarning'] = self.vdm_dict['eSNR [dB]'][1][3] + trans_dom_th['esnrlowwarning'] = self.vdm_dict['eSNR [dB]'][1][4] + trans_dom_th['cfohighalarm'] = self.vdm_dict['CFO [MHz]'][1][1] + trans_dom_th['cfolowalarm'] = self.vdm_dict['CFO [MHz]'][1][2] + trans_dom_th['cfohighwarning'] = self.vdm_dict['CFO [MHz]'][1][3] + trans_dom_th['cfolowwarning'] = self.vdm_dict['CFO [MHz]'][1][4] + trans_dom_th['txcurrpowerhighalarm'] = self.vdm_dict['Tx Power [dBm]'][1][1] + trans_dom_th['txcurrpowerlowalarm'] = self.vdm_dict['Tx Power [dBm]'][1][2] + trans_dom_th['txcurrpowerhighwarning'] = self.vdm_dict['Tx Power [dBm]'][1][3] + trans_dom_th['txcurrpowerlowwarning'] = self.vdm_dict['Tx Power [dBm]'][1][4] + trans_dom_th['rxtotpowerhighalarm'] = self.vdm_dict['Rx Total Power [dBm]'][1][1] + trans_dom_th['rxtotpowerlowalarm'] = self.vdm_dict['Rx Total Power [dBm]'][1][2] + trans_dom_th['rxtotpowerhighwarning'] = self.vdm_dict['Rx Total Power [dBm]'][1][3] + trans_dom_th['rxtotpowerlowwarning'] = self.vdm_dict['Rx Total Power [dBm]'][1][4] trans_dom_th['rxsigpowerhighalarm'] = self.vdm_dict['Rx Signal Power [dBm]'][1][1] trans_dom_th['rxsigpowerlowalarm'] = self.vdm_dict['Rx Signal Power [dBm]'][1][2] trans_dom_th['rxsigpowerhighwarning'] = self.vdm_dict['Rx Signal Power [dBm]'][1][3] @@ -784,61 +772,52 @@ def get_transceiver_status(self): trans_status['biasyplowalarm_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][6] trans_status['biasyphighwarning_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][7] trans_status['biasyplowwarning_flag'] = self.vdm_dict['Modulator Bias Y_Phase [%]'][1][8] - except KeyError: - pass - trans_status['cdshorthighalarm_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][5] - trans_status['cdshortlowalarm_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][6] - trans_status['cdshorthighwarning_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][7] - trans_status['cdshortlowwarning_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][8] - try: + trans_status['cdshorthighalarm_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][5] + trans_status['cdshortlowalarm_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][6] + trans_status['cdshorthighwarning_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][7] + trans_status['cdshortlowwarning_flag'] = self.vdm_dict['CD high granularity, short link [ps/nm]'][1][8] trans_status['cdlonghighalarm_flag'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][5] trans_status['cdlonglowalarm_flag'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][6] trans_status['cdlonghighwarning_flag'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][7] trans_status['cdlonglowwarning_flag'] = self.vdm_dict['CD low granularity, long link [ps/nm]'][1][8] - except KeyError: - pass - trans_status['dgdhighalarm_flag'] = self.vdm_dict['DGD [ps]'][1][5] - trans_status['dgdlowalarm_flag'] = self.vdm_dict['DGD [ps]'][1][6] - trans_status['dgdhighwarning_flag'] = self.vdm_dict['DGD [ps]'][1][7] - trans_status['dgdlowwarning_flag'] = self.vdm_dict['DGD [ps]'][1][8] - try: + trans_status['dgdhighalarm_flag'] = self.vdm_dict['DGD [ps]'][1][5] + trans_status['dgdlowalarm_flag'] = self.vdm_dict['DGD [ps]'][1][6] + trans_status['dgdhighwarning_flag'] = self.vdm_dict['DGD [ps]'][1][7] + trans_status['dgdlowwarning_flag'] = self.vdm_dict['DGD [ps]'][1][8] trans_status['sopmdhighalarm_flag'] = self.vdm_dict['SOPMD [ps^2]'][1][5] trans_status['sopmdlowalarm_flag'] = self.vdm_dict['SOPMD [ps^2]'][1][6] trans_status['sopmdhighwarning_flag'] = self.vdm_dict['SOPMD [ps^2]'][1][7] trans_status['sopmdlowwarning_flag'] = self.vdm_dict['SOPMD [ps^2]'][1][8] - except KeyError: - pass - trans_status['pdlhighalarm_flag'] = self.vdm_dict['PDL [dB]'][1][5] - trans_status['pdllowalarm_flag'] = self.vdm_dict['PDL [dB]'][1][6] - trans_status['pdlhighwarning_flag'] = self.vdm_dict['PDL [dB]'][1][7] - trans_status['pdllowwarning_flag'] = self.vdm_dict['PDL [dB]'][1][8] - trans_status['osnrhighalarm_flag'] = self.vdm_dict['OSNR [dB]'][1][5] - trans_status['osnrlowalarm_flag'] = self.vdm_dict['OSNR [dB]'][1][6] - trans_status['osnrhighwarning_flag'] = self.vdm_dict['OSNR [dB]'][1][7] - trans_status['osnrlowwarning_flag'] = self.vdm_dict['OSNR [dB]'][1][8] - trans_status['esnrhighalarm_flag'] = self.vdm_dict['eSNR [dB]'][1][5] - trans_status['esnrlowalarm_flag'] = self.vdm_dict['eSNR [dB]'][1][6] - trans_status['esnrhighwarning_flag'] = self.vdm_dict['eSNR [dB]'][1][7] - trans_status['esnrlowwarning_flag'] = self.vdm_dict['eSNR [dB]'][1][8] - trans_status['cfohighalarm_flag'] = self.vdm_dict['CFO [MHz]'][1][5] - trans_status['cfolowalarm_flag'] = self.vdm_dict['CFO [MHz]'][1][6] - trans_status['cfohighwarning_flag'] = self.vdm_dict['CFO [MHz]'][1][7] - trans_status['cfolowwarning_flag'] = self.vdm_dict['CFO [MHz]'][1][8] - trans_status['txcurrpowerhighalarm_flag'] = self.vdm_dict['Tx Power [dBm]'][1][5] - trans_status['txcurrpowerlowalarm_flag'] = self.vdm_dict['Tx Power [dBm]'][1][6] - trans_status['txcurrpowerhighwarning_flag'] = self.vdm_dict['Tx Power [dBm]'][1][7] - trans_status['txcurrpowerlowwarning_flag'] = self.vdm_dict['Tx Power [dBm]'][1][8] - trans_status['rxtotpowerhighalarm_flag'] = self.vdm_dict['Rx Total Power [dBm]'][1][5] - trans_status['rxtotpowerlowalarm_flag'] = self.vdm_dict['Rx Total Power [dBm]'][1][6] - trans_status['rxtotpowerhighwarning_flag'] = self.vdm_dict['Rx Total Power [dBm]'][1][7] - trans_status['rxtotpowerlowwarning_flag'] = self.vdm_dict['Rx Total Power [dBm]'][1][8] - try: + trans_status['pdlhighalarm_flag'] = self.vdm_dict['PDL [dB]'][1][5] + trans_status['pdllowalarm_flag'] = self.vdm_dict['PDL [dB]'][1][6] + trans_status['pdlhighwarning_flag'] = self.vdm_dict['PDL [dB]'][1][7] + trans_status['pdllowwarning_flag'] = self.vdm_dict['PDL [dB]'][1][8] + trans_status['osnrhighalarm_flag'] = self.vdm_dict['OSNR [dB]'][1][5] + trans_status['osnrlowalarm_flag'] = self.vdm_dict['OSNR [dB]'][1][6] + trans_status['osnrhighwarning_flag'] = self.vdm_dict['OSNR [dB]'][1][7] + trans_status['osnrlowwarning_flag'] = self.vdm_dict['OSNR [dB]'][1][8] + trans_status['esnrhighalarm_flag'] = self.vdm_dict['eSNR [dB]'][1][5] + trans_status['esnrlowalarm_flag'] = self.vdm_dict['eSNR [dB]'][1][6] + trans_status['esnrhighwarning_flag'] = self.vdm_dict['eSNR [dB]'][1][7] + trans_status['esnrlowwarning_flag'] = self.vdm_dict['eSNR [dB]'][1][8] + trans_status['cfohighalarm_flag'] = self.vdm_dict['CFO [MHz]'][1][5] + trans_status['cfolowalarm_flag'] = self.vdm_dict['CFO [MHz]'][1][6] + trans_status['cfohighwarning_flag'] = self.vdm_dict['CFO [MHz]'][1][7] + trans_status['cfolowwarning_flag'] = self.vdm_dict['CFO [MHz]'][1][8] + trans_status['txcurrpowerhighalarm_flag'] = self.vdm_dict['Tx Power [dBm]'][1][5] + trans_status['txcurrpowerlowalarm_flag'] = self.vdm_dict['Tx Power [dBm]'][1][6] + trans_status['txcurrpowerhighwarning_flag'] = self.vdm_dict['Tx Power [dBm]'][1][7] + trans_status['txcurrpowerlowwarning_flag'] = self.vdm_dict['Tx Power [dBm]'][1][8] + trans_status['rxtotpowerhighalarm_flag'] = self.vdm_dict['Rx Total Power [dBm]'][1][5] + trans_status['rxtotpowerlowalarm_flag'] = self.vdm_dict['Rx Total Power [dBm]'][1][6] + trans_status['rxtotpowerhighwarning_flag'] = self.vdm_dict['Rx Total Power [dBm]'][1][7] + trans_status['rxtotpowerlowwarning_flag'] = self.vdm_dict['Rx Total Power [dBm]'][1][8] trans_status['rxsigpowerhighalarm_flag'] = self.vdm_dict['Rx Signal Power [dBm]'][1][5] trans_status['rxsigpowerlowalarm_flag'] = self.vdm_dict['Rx Signal Power [dBm]'][1][6] trans_status['rxsigpowerhighwarning_flag'] = self.vdm_dict['Rx Signal Power [dBm]'][1][7] trans_status['rxsigpowerlowwarning_flag'] = self.vdm_dict['Rx Signal Power [dBm]'][1][8] except KeyError: - helper_logger.log_debug('Rx Signal Power [dBm] not present in VDM') + helper_logger.log_debug('fields not present in VDM') return trans_status def get_transceiver_pm(self): From 465f95eed18cd9a9a598beabb8c02cb5387e199a Mon Sep 17 00:00:00 2001 From: spilkey-cisco <110940806+spilkey-cisco@users.noreply.github.com> Date: Tue, 11 Jul 2023 10:46:22 -0700 Subject: [PATCH 16/24] Default implementation of under/over speed checks (#382) * Default implementation of under/over speed checks * Update comment, remove requirement for float conversion * Fan test coverage --- sonic_platform_base/fan_base.py | 46 ++++++++++ tests/fan_base_test.py | 158 ++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 tests/fan_base_test.py diff --git a/sonic_platform_base/fan_base.py b/sonic_platform_base/fan_base.py index 2b1259144..1eaa28859 100644 --- a/sonic_platform_base/fan_base.py +++ b/sonic_platform_base/fan_base.py @@ -66,6 +66,52 @@ def get_speed_tolerance(self): """ raise NotImplementedError + def is_under_speed(self): + """ + Calculates if the fan speed is under the tolerated low speed threshold + + Default calculation requires get_speed_tolerance to be implemented, and checks + if the current fan speed (expressed as a percentage) is lower than + percent below the target fan speed (expressed as a percentage) + + Returns: + A boolean, True if fan speed is under the low threshold, False if not + """ + speed = self.get_speed() + target_speed = self.get_target_speed() + tolerance = self.get_speed_tolerance() + + for param, value in (('speed', speed), ('target speed', target_speed), ('speed tolerance', tolerance)): + if not isinstance(value, int): + raise TypeError(f'Fan {param} is not an integer value: {param}={value}') + if value < 0 or value > 100: + raise ValueError(f'Fan {param} is not a valid percentage value: {param}={value}') + + return speed * 100 < target_speed * (100 - tolerance) + + def is_over_speed(self): + """ + Calculates if the fan speed is over the tolerated high speed threshold + + Default calculation requires get_speed_tolerance to be implemented, and checks + if the current fan speed (expressed as a percentage) is higher than + percent above the target fan speed (expressed as a percentage) + + Returns: + A boolean, True if fan speed is over the high threshold, False if not + """ + speed = self.get_speed() + target_speed = self.get_target_speed() + tolerance = self.get_speed_tolerance() + + for param, value in (('speed', speed), ('target speed', target_speed), ('speed tolerance', tolerance)): + if not isinstance(value, int): + raise TypeError(f'Fan {param} is not an integer value: {param}={value}') + if value < 0 or value > 100: + raise ValueError(f'Fan {param} is not a valid percentage value: {param}={value}') + + return speed * 100 > target_speed * (100 + tolerance) + def set_speed(self, speed): """ Sets the fan speed diff --git a/tests/fan_base_test.py b/tests/fan_base_test.py new file mode 100644 index 000000000..af3055e28 --- /dev/null +++ b/tests/fan_base_test.py @@ -0,0 +1,158 @@ +''' +Test FanBase module +''' + +from unittest import mock +from sonic_platform_base.fan_base import FanBase + +class TestFanBase: + ''' + Collection of FanBase test methods + ''' + + @staticmethod + def test_is_under_speed(): + ''' + Test fan.is_under_speed default implementation + ''' + fan = FanBase() + fan.get_speed = mock.MagicMock(return_value=100) + fan.get_target_speed = mock.MagicMock(return_value=50) + fan.get_speed_tolerance = mock.MagicMock(return_value=10) + + for func in (fan.get_speed, fan.get_target_speed, fan.get_speed_tolerance): + return_val = func() + + # Check type and bounds errors + for value, exc_type in ((None, TypeError), (-1, ValueError), (101, ValueError)): + func.return_value = value + expected_exception = False + try: + fan.is_under_speed() + except Exception as exc: + expected_exception = isinstance(exc, exc_type) + assert expected_exception + + # Reset function return value + func.return_value = return_val + + # speed=100, minimum tolerated speed=45, not under speed + assert not fan.is_under_speed() + + # speed=46, minimum tolerated speed=45, not under speed + fan.get_speed.return_value = 46 + assert not fan.is_under_speed() + + # speed=45, minimum tolerated speed=45, not under speed + fan.get_speed.return_value = 45 + assert not fan.is_under_speed() + + # speed=44, minimum tolerated speed=45, under speed + fan.get_speed.return_value = 44 + assert fan.is_under_speed() + + # speed=44, minimum tolerated speed=40, not under speed + fan.get_speed_tolerance.return_value = 20 + assert not fan.is_under_speed() + + # speed=41, minimum tolerated speed=40, not under speed + fan.get_speed.return_value = 41 + assert not fan.is_under_speed() + + # speed=40, minimum tolerated speed=40, not under speed + fan.get_speed.return_value = 40 + assert not fan.is_under_speed() + + # speed=39, minimum tolerated speed=40, under speed + fan.get_speed.return_value = 39 + assert fan.is_under_speed() + + # speed=1, minimum tolerated speed=40, under speed + fan.get_speed.return_value = 1 + assert fan.is_under_speed() + + @staticmethod + def test_is_over_speed(): + ''' + test fan.is_over_speed default implementation + ''' + fan = FanBase() + fan.get_speed = mock.MagicMock(return_value=1) + fan.get_target_speed = mock.MagicMock(return_value=50) + fan.get_speed_tolerance = mock.MagicMock(return_value=10) + + for func in (fan.get_speed, fan.get_target_speed, fan.get_speed_tolerance): + return_val = func() + + # Check type and bounds errors + for value, exc_type in ((None, TypeError), (-1, ValueError), (101, ValueError)): + func.return_value = value + expected_exception = False + try: + fan.is_under_speed() + except Exception as exc: + expected_exception = isinstance(exc, exc_type) + assert expected_exception + + # Reset function return value + func.return_value = return_val + + # speed=1, maximum tolerated speed=55, not over speed + assert not fan.is_over_speed() + + # speed=54, maximum tolerated speed=55, not over speed + fan.get_speed.return_value = 54 + assert not fan.is_over_speed() + + # speed=55, maximum tolerated speed=55, not over speed + fan.get_speed.return_value = 55 + assert not fan.is_over_speed() + + # speed=56, maximum tolerated speed=55, over speed + fan.get_speed.return_value = 56 + assert fan.is_over_speed() + + # speed=56, maximum tolerated speed=60, not over speed + fan.get_speed_tolerance.return_value = 20 + assert not fan.is_over_speed() + + # speed=59, maximum tolerated speed=60, not over speed + fan.get_speed.return_value = 59 + assert not fan.is_over_speed() + + # speed=60, maximum tolerated speed=60, not over speed + fan.get_speed.return_value = 60 + assert not fan.is_over_speed() + + # speed=61, maximum tolerated speed=60, over speed + fan.get_speed.return_value = 61 + assert fan.is_over_speed() + + # speed=100, maximum tolerated speed=60, over speed + fan.get_speed.return_value = 100 + assert fan.is_over_speed() + + @staticmethod + def test_fan_base(): + ''' + Verify unimplemented methods + ''' + fan = FanBase() + not_implemented_methods = [ + (fan.get_direction,), + (fan.get_speed,), + (fan.get_target_speed,), + (fan.get_speed_tolerance,), + (fan.set_speed, 50), + (fan.set_status_led, 'green'), + (fan.get_status_led,)] + + for method in not_implemented_methods: + expected_exception = False + try: + func = method[0] + args = method[1:] + func(*args) + except Exception as exc: + expected_exception = isinstance(exc, NotImplementedError) + assert expected_exception From c99d31164175bec5f99784bf222d91bf8f2c20c6 Mon Sep 17 00:00:00 2001 From: abdosi <58047199+abdosi@users.noreply.github.com> Date: Thu, 27 Jul 2023 19:34:15 -0700 Subject: [PATCH 17/24] Comment out tx power validation check and program the passed value (#389) * Comment out power validation check and program the passed value to the eeprom write function. Signed-off-by: Abhishek Dosi * Update c_cmis.py --------- Signed-off-by: Abhishek Dosi --- sonic_platform_base/sonic_xcvr/api/public/c_cmis.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py index 6a94df544..bb7e3a703 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py @@ -143,9 +143,6 @@ def set_tx_power(self, tx_power): Return True if the provision succeeds, False if it fails ''' min_prog_tx_output_power, max_prog_tx_output_power = self.get_supported_power_config() - if tx_power > max_prog_tx_output_power or tx_power < min_prog_tx_output_power: - raise ValueError('Provisioned TX power out of range. Max: %.1f; Min: %.1f dBm.' - %(max_prog_tx_output_power, min_prog_tx_output_power)) status = self.xcvr_eeprom.write(consts.TX_CONFIG_POWER, tx_power) time.sleep(1) return status From 537095cca2e8074600805915217e091d474b8211 Mon Sep 17 00:00:00 2001 From: Prince George <45705344+prgeor@users.noreply.github.com> Date: Tue, 8 Aug 2023 12:25:46 -0700 Subject: [PATCH 18/24] Added new RegBitsFields (#391) --- .../sonic_xcvr/fields/xcvr_field.py | 31 +++++++++++ tests/sonic_xcvr/test_xcvr_field.py | 54 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py b/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py index 68febd72c..93f51613f 100644 --- a/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py +++ b/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py @@ -105,7 +105,38 @@ def encode(self, val, raw_state=None): curr_state &= ~(1 << self.bitpos % 8) return bytearray([curr_state]) +class RegBitsField(XcvrField): + """ + Multi-bit register field. Must be defined under a parent RegField + + Args: + bitpos: the bit position of this field relative to its parent's offset + """ + def __init__(self, name, bitpos, offset=None, **kwargs): + super(RegBitsField, self).__init__(name, offset, **kwargs) + self.size = self.size = kwargs.get("size", 1) #No of bits + assert (bitpos >= 0 and (bitpos+self.size-1 < 8), "bitpos must be within one byte") + self.bitpos = bitpos + self.bitmask = (((1 << self.size) - 1) << self.bitpos) & 0xff + def get_size(self): + return 1 # 1-Byte + + def read_before_write(self): + return True + + def decode(self, raw_data, **decoded_deps): + val = (raw_data[0] & self.bitmask) >> self.bitpos + return val + + def encode(self, val, raw_value=None): + assert not self.ro and raw_value is not None + val = val & ((1 << self.size) - 1) + byte = raw_value[0] + byte &= ~self.bitmask + byte |= (val << self.bitpos) + return bytearray([byte]) + class RegField(XcvrField): """ Field denoting one or more bytes, but logically interpreted as one unit (e.g. a 4-byte integer) diff --git a/tests/sonic_xcvr/test_xcvr_field.py b/tests/sonic_xcvr/test_xcvr_field.py index d137413b3..a955cc8e8 100644 --- a/tests/sonic_xcvr/test_xcvr_field.py +++ b/tests/sonic_xcvr/test_xcvr_field.py @@ -5,6 +5,7 @@ HexRegField, NumberRegField, RegBitField, + RegBitsField, RegGroupField, StringRegField, ) @@ -51,6 +52,16 @@ def __init__(self, codes): RegBitField("NumRegBit", bitpos=20, ro=False), format=" Date: Thu, 10 Aug 2023 13:47:50 +0800 Subject: [PATCH 19/24] Add new SSD type support (#390) * Add new SSD type support * Fix review comment --------- Co-authored-by: Prince George <45705344+prgeor@users.noreply.github.com> --- sonic_platform_base/sonic_ssd/ssd_generic.py | 20 +++- tests/ssd_generic_test.py | 116 +++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/sonic_platform_base/sonic_ssd/ssd_generic.py b/sonic_platform_base/sonic_ssd/ssd_generic.py index 1541d31f3..2fc5903b0 100644 --- a/sonic_platform_base/sonic_ssd/ssd_generic.py +++ b/sonic_platform_base/sonic_ssd/ssd_generic.py @@ -23,6 +23,8 @@ # Set Vendor Specific IDs INNODISK_HEALTH_ID = 169 INNODISK_TEMPERATURE_ID = 194 +SWISSBIT_HEALTH_ID = 248 +SWISSBIT_TEMPERATURE_ID = 194 class SsdUtil(SsdBase): """ @@ -42,7 +44,8 @@ def __init__(self, diskdev): "InnoDisk" : { "utility" : INNODISK, "parser" : self.parse_innodisk_info }, "M.2" : { "utility" : INNODISK, "parser" : self.parse_innodisk_info }, "StorFly" : { "utility" : VIRTIUM, "parser" : self.parse_virtium_info }, - "Virtium" : { "utility" : VIRTIUM, "parser" : self.parse_virtium_info } + "Virtium" : { "utility" : VIRTIUM, "parser" : self.parse_virtium_info }, + "Swissbit" : { "utility" : SMARTCTL, "parser" : self.parse_swissbit_info }, } self.dev = diskdev @@ -78,6 +81,8 @@ def _parse_vendor(self): return model_short elif self.model.startswith('VSF'): return 'Virtium' + elif self.model.startswith('SFS'): + return 'Swissbit' else: return None @@ -154,6 +159,19 @@ def parse_virtium_info(self): except ValueError: pass + def parse_swissbit_info(self): + if self.ssd_info: + health_raw = self.parse_id_number(SWISSBIT_HEALTH_ID) + if health_raw == NOT_AVAILABLE: + self.health = NOT_AVAILABLE + else: + self.health = health_raw.split()[-1] + temp_raw = self.parse_id_number(SWISSBIT_TEMPERATURE_ID) + if temp_raw == NOT_AVAILABLE: + self.temperature = NOT_AVAILABLE + else: + self.temperature = temp_raw.split()[-3] + def fetch_vendor_ssd_info(self, diskdev, model): self.vendor_ssd_info = self._execute_shell(self.vendor_ssd_utility[model]["utility"].format(diskdev)) diff --git a/tests/ssd_generic_test.py b/tests/ssd_generic_test.py index 889205e11..26fc0cd25 100644 --- a/tests/ssd_generic_test.py +++ b/tests/ssd_generic_test.py @@ -532,6 +532,112 @@ ID Attribute High Raw Low Raw Value Worst Threshold """ +output_swissbit_vendor = """ +smartctl 7.2 2020-12-30 r5155 [x86_64-linux-5.10.0-23-2-amd64] (local build) +Copyright (C) 2002-20, Bruce Allen, Christian Franke, www.smartmontools.org + +=== START OF INFORMATION SECTION === +Device Model: SFSA160GM2AK2TO-I-8C-22K-STD +Serial Number: 00006022750795000010 +Firmware Version: SBR15004 +User Capacity: 160,041,885,696 bytes [160 GB] +Sector Size: 512 bytes logical/physical +Rotation Rate: Solid State Device +Form Factor: 2.5 inches +TRIM Command: Available, deterministic, zeroed +Device is: Not in smartctl database [for details use: -P showall] +ATA Version is: ACS-3 (minor revision not indicated) +SATA Version is: SATA 3.2, 6.0 Gb/s (current: 6.0 Gb/s) +Local Time is: Wed Aug 2 08:24:31 2023 UTC +SMART support is: Available - device has SMART capability. +SMART support is: Enabled + +=== START OF READ SMART DATA SECTION === +SMART overall-health self-assessment test result: PASSED + +General SMART Values: +Offline data collection status: (0x00) Offline data collection activity + was never started. + Auto Offline Data Collection: Disabled. +Self-test execution status: ( 0) The previous self-test routine completed + without error or no self-test has ever + been run. +Total time to complete Offline +data collection: ( 0) seconds. +Offline data collection +capabilities: (0x53) SMART execute Offline immediate. + Auto Offline data collection on/off support. + Suspend Offline collection upon new + command. + No Offline surface scan supported. + Self-test supported. + No Conveyance Self-test supported. + Selective Self-test supported. +SMART capabilities: (0x0003) Saves SMART data before entering + power-saving mode. + Supports SMART auto save timer. +Error logging capability: (0x01) Error logging supported. + General Purpose Logging supported. +Short self-test routine +recommended polling time: ( 2) minutes. +Extended self-test routine +recommended polling time: ( 15) minutes. +SCT capabilities: (0x0031) SCT Status supported. + SCT Feature Control supported. + SCT Data Table supported. + +SMART Attributes Data Structure revision number: 1 +Vendor Specific SMART Attributes with Thresholds: +ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE + 1 Raw_Read_Error_Rate 0x000b 100 100 000 Pre-fail Always - 0 + 5 Reallocated_Sector_Ct 0x0013 100 100 000 Pre-fail Always - 0 + 9 Power_On_Hours 0x0012 100 100 000 Old_age Always - 825 + 12 Power_Cycle_Count 0x0012 100 100 000 Old_age Always - 447 + 16 Unknown_Attribute 0x0112 100 100 001 Old_age Always - 4 + 17 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 30000 +160 Unknown_Attribute 0x0002 100 100 000 Old_age Always - 0 +161 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 15401195 +163 Unknown_Attribute 0x0003 100 100 000 Pre-fail Always - 33 +164 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 6506 +165 Unknown_Attribute 0x0002 100 100 000 Old_age Always - 38 +166 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 1 +167 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 4 +168 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 30000 +169 Unknown_Attribute 0x0003 100 100 000 Pre-fail Always - 421 +193 Unknown_SSD_Attribute 0x0012 100 100 000 Old_age Always - 0 +194 Temperature_Celsius 0x0023 100 100 000 Pre-fail Always - 25 (Min/Max 22/45) +195 Hardware_ECC_Recovered 0x0012 100 100 000 Old_age Always - 0 +196 Reallocated_Event_Count 0x0012 000 000 000 Old_age Always - 0 +198 Offline_Uncorrectable 0x0012 100 100 000 Old_age Always - 0 +199 UDMA_CRC_Error_Count 0x000b 100 100 000 Pre-fail Always - 0 +215 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 4275 +231 Unknown_SSD_Attribute 0x1913 100 100 025 Pre-fail Always - 100 +235 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 1302467136 +237 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 0 +241 Total_LBAs_Written 0x0012 100 100 000 Old_age Always - 1186450104 +242 Total_LBAs_Read 0x0012 100 100 000 Old_age Always - 2257141451 +243 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 0 +244 Unknown_Attribute 0x0012 100 100 000 Old_age Always - 0 +248 Unknown_Attribute 0x0112 100 100 001 Old_age Always - 100 + +SMART Error Log Version: 1 +No Errors Logged + +SMART Self-test log structure revision number 1 +No self-tests have been logged. [To run self-tests, use: smartctl -t] + +SMART Selective self-test log data structure revision number 1 + SPAN MIN_LBA MAX_LBA CURRENT_TEST_STATUS + 1 0 0 Not_testing + 2 0 0 Not_testing + 3 0 0 Not_testing + 4 0 0 Not_testing + 5 0 0 Not_testing +Selective self-test flags (0x0): + After scanning selected spans, do NOT read-scan remainder of disk. +If Selective self-test is pending on power-up, resume after 0 minute delay. +""" + class TestSsdGeneric: @mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell', mock.MagicMock(return_value=output_nvme_ssd)) def test_nvme_ssd(self): @@ -631,3 +737,13 @@ def test_virtium_ssd(self, mock_exec): mock_exec.side_effect = [output_virtium_generic, output_virtium_invalid_remain_life] virtium_ssd = SsdUtil('/dev/sda') assert virtium_ssd.get_health() == "N/A" + + @mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell') + def test_swissbit_ssd(self, mock_exec): + mock_exec.return_value = output_swissbit_vendor + swissbit_ssd = SsdUtil('/dev/sda') + assert swissbit_ssd.get_health() == '100' + assert swissbit_ssd.get_model() == 'SFSA160GM2AK2TO-I-8C-22K-STD' + assert swissbit_ssd.get_firmware() == "SBR15004" + assert swissbit_ssd.get_temperature() == '25' + assert swissbit_ssd.get_serial() == "00006022750795000010" From ae7049cd0ac917c7f19f82fa3a5fb1fc231eefbb Mon Sep 17 00:00:00 2001 From: Anoop Kamath <115578705+AnoopKamath@users.noreply.github.com> Date: Fri, 11 Aug 2023 12:52:54 -0700 Subject: [PATCH 20/24] Apply custom Si settings via CMIS: SONiC xcvrd platform common changes (#384) Co-authored-by: Prince George <45705344+prgeor@users.noreply.github.com> --- .../sonic_xcvr/api/public/cmis.py | 322 +++++++++++++++++- .../sonic_xcvr/fields/consts.py | 97 ++++++ .../sonic_xcvr/mem_maps/public/cmis.py | 134 ++++++++ tests/sonic_xcvr/test_cmis.py | 116 ++++++- 4 files changed, 657 insertions(+), 12 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 36657ddd7..a677f32ff 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -31,6 +31,12 @@ def __init__(self, xcvr_eeprom): self.vdm = CmisVdmApi(xcvr_eeprom) if not self.is_flat_memory() else None self.cdb = CmisCdbApi(xcvr_eeprom) if not self.is_flat_memory() else None + def get_manufacturer(self): + ''' + This function returns the manufacturer of the module + ''' + return self.xcvr_eeprom.read(consts.VENDOR_NAME_FIELD) + def get_model(self): ''' This function returns the part number of the module @@ -2069,7 +2075,7 @@ def get_application(self, lane): return (appl & 0xf) - def set_application(self, channel, appl_code): + def set_application(self, channel, appl_code, ec=0): """ Update the selected application code to the specified lanes on the host side @@ -2092,11 +2098,323 @@ def set_application(self, channel, appl_code): lane_first = lane addr = "{}_{}_{}".format(consts.STAGED_CTRL_APSEL_FIELD, 0, lane + 1) data = (appl_code << 4) | (lane_first << 1) + #set EC bit + data|= ec self.xcvr_eeprom.write(addr, data) - # Apply DataPathInit + def scs_apply_datapath_init(self, channel): + ''' + This function applies DataPathInit + ''' return self.xcvr_eeprom.write("%s_%d" % (consts.STAGED_CTRL_APPLY_DPINIT_FIELD, 0), channel) + def get_rx_output_amp_max_val(self): + ''' + This function returns the supported RX output amp val + ''' + rx_amp_max_val = self.xcvr_eeprom.read(consts.RX_OUTPUT_LEVEL_SUPPORT) + if rx_amp_max_val is None: + return None + return rx_amp_max_val + + def get_rx_output_eq_pre_max_val(self): + ''' + This function returns the supported RX output eq pre cursor val + ''' + rx_pre_max_val = self.xcvr_eeprom.read(consts.RX_OUTPUT_EQ_PRE_CURSOR_MAX) + if rx_pre_max_val is None: + return None + return rx_pre_max_val + + def get_rx_output_eq_post_max_val(self): + ''' + This function returns the supported RX output eq post cursor val + ''' + rx_post_max_val = self.xcvr_eeprom.read(consts.RX_OUTPUT_EQ_POST_CURSOR_MAX) + if rx_post_max_val is None: + return None + return rx_post_max_val + + def get_tx_input_eq_max_val(self): + ''' + This function returns the supported TX input eq val + ''' + tx_input_max_val = self.xcvr_eeprom.read(consts.TX_INPUT_EQ_MAX) + if tx_input_max_val is None: + return None + return tx_input_max_val + + def get_tx_cdr_supported(self): + ''' + This function returns the supported TX CDR field + ''' + tx_cdr_support = self.xcvr_eeprom.read(consts.TX_CDR_SUPPORT_FIELD) + if not tx_cdr_support or tx_cdr_support is None: + return False + return tx_cdr_support + + def get_rx_cdr_supported(self): + ''' + This function returns the supported RX CDR field + ''' + rx_cdr_support = self.xcvr_eeprom.read(consts.RX_CDR_SUPPORT_FIELD) + if not rx_cdr_support or rx_cdr_support is None: + return False + return rx_cdr_support + + def get_tx_input_eq_fixed_supported(self): + ''' + This function returns the supported TX input eq field + ''' + tx_fixed_support = self.xcvr_eeprom.read(consts.TX_INPUT_EQ_FIXED_MANUAL_CTRL_SUPPORT_FIELD) + if not tx_fixed_support or tx_fixed_support is None: + return False + return tx_fixed_support + + def get_tx_input_adaptive_eq_supported(self): + ''' + This function returns the supported TX input adaptive eq field + ''' + tx_adaptive_support = self.xcvr_eeprom.read(consts.TX_INPUT_ADAPTIVE_EQ_SUPPORT_FIELD) + if not tx_adaptive_support or tx_adaptive_support is None: + return False + return tx_adaptive_support + + def get_tx_input_recall_buf1_supported(self): + ''' + This function returns the supported TX input recall buf1 field + ''' + tx_recall_buf1_support = self.xcvr_eeprom.read(consts.TX_INPUT_EQ_RECALL_BUF1_SUPPORT_FIELD) + if not tx_recall_buf1_support or tx_recall_buf1_support is None: + return False + return tx_recall_buf1_support + + def get_tx_input_recall_buf2_supported(self): + ''' + This function returns the supported TX input recall buf2 field + ''' + tx_recall_buf2_support = self.xcvr_eeprom.read(consts.TX_INPUT_EQ_RECALL_BUF2_SUPPORT_FIELD) + if not tx_recall_buf2_support or tx_recall_buf2_support is None: + return False + return tx_recall_buf2_support + + def get_rx_ouput_amp_ctrl_supported(self): + ''' + This function returns the supported RX output amp control field + ''' + rx_amp_support = self.xcvr_eeprom.read(consts.RX_OUTPUT_AMP_CTRL_SUPPORT_FIELD) + if not rx_amp_support or rx_amp_support is None: + return False + return rx_amp_support + + def get_rx_output_eq_pre_ctrl_supported(self): + ''' + This function returns the supported RX output eq pre control field + ''' + rx_pre_support = self.xcvr_eeprom.read(consts.RX_OUTPUT_EQ_PRE_CTRL_SUPPORT_FIELD) + if not rx_pre_support or rx_pre_support is None: + return False + return rx_pre_support + + def get_rx_output_eq_post_ctrl_supported(self): + ''' + This function returns the supported RX output eq post control field + ''' + rx_post_support = self.xcvr_eeprom.read(consts.RX_OUTPUT_EQ_POST_CTRL_SUPPORT_FIELD) + if not rx_post_support or rx_post_support is None: + return False + return rx_post_support + + def scs_lane_write(self, si_param, host_lanes_mask, si_settings_dict): + ''' + This function sets each lane val based on SI param + ''' + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & host_lanes_mask) == 0: + continue + lane = lane+1 + si_param_lane = "{}{}".format(si_param, lane) + si_param_lane_val = si_settings_dict[si_param_lane] + if si_param_lane_val is None: + return False + if not self.xcvr_eeprom.write(si_param_lane, si_param_lane_val): + return False + return True + + def stage_output_eq_pre_cursor_target_rx(self, host_lanes_mask, si_settings_dict): + ''' + This function applies RX output eq pre cursor settings + ''' + rx_pre_max_val = self.get_rx_output_eq_pre_max_val() + if rx_pre_max_val is None: + return False + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & host_lanes_mask) == 0: + continue + lane = lane+1 + si_param_lane = "{}{}".format(consts.OUTPUT_EQ_PRE_CURSOR_TARGET_RX, lane) + si_param_lane_val = si_settings_dict[si_param_lane] + if si_param_lane_val is None or si_param_lane_val > rx_pre_max_val: + return False + if not self.xcvr_eeprom.write(si_param_lane, si_param_lane_val): + return False + return True + + def stage_output_eq_post_cursor_target_rx(self, host_lanes_mask, si_settings_dict): + ''' + This function applies RX output eq post cursor settings + ''' + rx_post_max_val = self.get_rx_output_eq_post_max_val() + if rx_post_max_val is None: + return False + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & host_lanes_mask) == 0: + continue + lane = lane+1 + si_param_lane = "{}{}".format(consts.OUTPUT_EQ_POST_CURSOR_TARGET_RX, lane) + si_param_lane_val = si_settings_dict[si_param_lane] + if si_param_lane_val is None or si_param_lane_val > rx_post_max_val: + return False + if not self.xcvr_eeprom.write(si_param_lane, si_param_lane_val): + return False + return True + + def stage_output_amp_target_rx(self, host_lanes_mask, si_settings_dict): + ''' + This function applies RX output amp settings + ''' + rx_amp_max_val = self.get_rx_output_amp_max_val() + if rx_amp_max_val is None: + return False + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & host_lanes_mask) == 0: + continue + lane = lane+1 + si_param_lane = "{}{}".format(consts.OUTPUT_AMPLITUDE_TARGET_RX, lane) + si_param_lane_val = si_settings_dict[si_param_lane] + if si_param_lane_val is None or si_param_lane_val > rx_amp_max_val: + return False + if not self.xcvr_eeprom.write(si_param_lane, si_param_lane_val): + return False + return True + + def stage_fixed_input_target_tx(self, host_lanes_mask, si_settings_dict): + ''' + This function applies fixed TX input si settings + ''' + tx_fixed_input = self.get_tx_input_eq_max_val() + if tx_fixed_input is None: + return False + for lane in range(self.NUM_CHANNELS): + if ((1 << lane) & host_lanes_mask) == 0: + continue + lane = lane+1 + si_param_lane = "{}{}".format(consts.FIXED_INPUT_EQ_TARGET_TX, lane) + si_param_lane_val = si_settings_dict[si_param_lane] + if si_param_lane_val is None or si_param_lane_val > tx_fixed_input: + return False + if not self.xcvr_eeprom.write(si_param_lane, si_param_lane_val): + return False + return True + + def stage_adaptive_input_eq_recall_tx(self, host_lanes_mask, si_settings_dict): + ''' + This function applies adaptive TX input recall si settings. + ''' + if si_settings_dict is None: + return False + return self.scs_lane_write(consts.ADAPTIVE_INPUT_EQ_RECALLED_TX, host_lanes_mask, si_settings_dict) + + def stage_adaptive_input_eq_enable_tx(self, host_lanes_mask, si_settings_dict): + ''' + This function applies adaptive TX input enable si settings + ''' + if si_settings_dict is None: + return False + return self.scs_lane_write(consts.ADAPTIVE_INPUT_EQ_ENABLE_TX, host_lanes_mask, si_settings_dict) + + def stage_cdr_tx(self, host_lanes_mask, si_settings_dict): + ''' + This function applies TX CDR si settings + ''' + if si_settings_dict is None: + return False + return self.scs_lane_write(consts.CDR_ENABLE_TX, host_lanes_mask, si_settings_dict) + + def stage_cdr_rx(self, host_lanes_mask, si_settings_dict): + ''' + This function applies RX CDR si settings + ''' + if si_settings_dict is None: + return False + return self.scs_lane_write(consts.CDR_ENABLE_RX, host_lanes_mask, si_settings_dict) + + def stage_rx_si_settings(self, host_lanes_mask, si_settings_dict): + for si_param in si_settings_dict: + if si_param == consts.OUTPUT_EQ_PRE_CURSOR_TARGET_RX: + if self.get_rx_output_eq_pre_ctrl_supported(): + if not self.stage_output_eq_pre_cursor_target_rx(host_lanes_mask, si_settings_dict[si_param]): + return False + elif si_param == consts.OUTPUT_EQ_POST_CURSOR_TARGET_RX: + if self.get_rx_output_eq_post_ctrl_supported(): + if not self.stage_output_eq_post_cursor_target_rx(host_lanes_mask, si_settings_dict[si_param]): + return False + elif si_param == consts.OUTPUT_AMPLITUDE_TARGET_RX: + if self.get_rx_ouput_amp_ctrl_supported(): + if not self.stage_output_amp_target_rx(host_lanes_mask, si_settings_dict[si_param]): + return False + elif si_param == consts.CDR_ENABLE_RX: + if self.get_rx_cdr_supported(): + if not self.stage_cdr_rx(host_lanes_mask, si_settings_dict[si_param]): + return False + else: + return False + + return True + + def stage_tx_si_settings(self, host_lanes_mask, si_settings_dict): + for si_param in si_settings_dict: + if si_param == consts.FIXED_INPUT_EQ_TARGET_TX: + if self.get_tx_input_eq_fixed_supported(): + if not self.stage_fixed_input_target_tx(host_lanes_mask, si_settings_dict[si_param]): + return False + elif si_param == consts.ADAPTIVE_INPUT_EQ_RECALLED_TX: + if self.get_tx_input_recall_buf1_supported() or self.get_tx_input_recall_buf2_supported(): + if not self.stage_adaptive_input_eq_recall_tx(host_lanes_mask, si_settings_dict[si_param]): + return False + elif si_param == consts.ADAPTIVE_INPUT_EQ_ENABLE_TX: + if self.get_tx_input_adaptive_eq_supported(): + if not self.stage_adaptive_input_eq_enable_tx(host_lanes_mask, si_settings_dict[si_param]): + return False + elif si_param == consts.CDR_ENABLE_TX: + if self.get_tx_cdr_supported(): + if not self.stage_cdr_tx(host_lanes_mask, si_settings_dict[si_param]): + return False + else: + return False + + return True + + def stage_custom_si_settings(self, host_lanes_mask, optics_si_dict): + # Create TX/RX specific si_dict + rx_si_settings = {} + tx_si_settings = {} + for si_param in optics_si_dict: + if si_param.endswith("Tx"): + tx_si_settings[si_param] = optics_si_dict[si_param] + elif si_param.endswith("Rx"): + rx_si_settings[si_param] = optics_si_dict[si_param] + + # stage RX si settings + if not self.stage_rx_si_settings(host_lanes_mask, rx_si_settings): + return False + + # stage TX si settings + if not self.stage_tx_si_settings(host_lanes_mask, tx_si_settings): + return False + + return True + def get_error_description(self): dp_state = self.get_datapath_state() conf_state = self.get_config_datapath_hostlane_status() diff --git a/sonic_platform_base/sonic_xcvr/fields/consts.py b/sonic_platform_base/sonic_xcvr/fields/consts.py index c638f19b6..e9e250a32 100644 --- a/sonic_platform_base/sonic_xcvr/fields/consts.py +++ b/sonic_platform_base/sonic_xcvr/fields/consts.py @@ -299,6 +299,103 @@ STAGED_CTRL_APPLY_IMMEDIATE_FIELD = "Staged Control Set Apply Immediate" STAGED_CTRL_APSEL_FIELD = "Staged Control Set ApSel" +STAGED_CTRL0_TX_RX_CTRL_FIELD = "Staged Control TX RX Control" + +TX_INPUT_EQ_MAX = "TXInputEqMax" +RX_OUTPUT_LEVEL_SUPPORT = "RX Output Level Supported" +RX_OUTPUT_LEVEL_0_SUPPORTED = "RXOutputLevel0Supported" +RX_OUTPUT_LEVEL_1_SUPPORTED = "RXOutputLevel1Supported" +RX_OUTPUT_LEVEL_2_SUPPORTED = "RXOutputLevel2Supported" +RX_OUTPUT_LEVEL_3_SUPPORTED = "RXOutputLevel3Supported" +RX_OUTPUT_EQ_PRE_CURSOR_MAX = "RXOutputEqPreCursorMax" +RX_OUTPUT_EQ_POST_CURSOR_MAX = "RXOutputEqPostCursorMax" + +SIGNAL_INTEGRITY_CTRL_ADVT_FIELD = "Signal Integrity Control Advertisement" +TX_SI_CTRL_ADVT = "TX SI Control Advertisement" +TX_CDR_SUPPORT_FIELD = "TXCDRSupported" +TX_CDR_BYPASS_CTRL_FIELD = "TXCDRBypassSupported" +TX_INPUT_EQ_FIXED_MANUAL_CTRL_SUPPORT_FIELD = "TXInputEqFixedManualSupported" +TX_INPUT_ADAPTIVE_EQ_SUPPORT_FIELD = "TXInputAdaptiveEqSupported" +TX_INPUT_EQ_FREEZE_SUPPORT_FIELD = "TXInputEqFreezeSupported" +TX_INPUT_EQ_RECALL_BUF1_SUPPORT_FIELD = "TXInputEqRecallBuf1Supported" +TX_INPUT_EQ_RECALL_BUF2_SUPPORT_FIELD = "TXInputEqRecallBuf2Supported" +TX_INPUT_EQ_RECALL_BUF_SUPPORT_FIELD = "TXInputEqRecallBufSupported" +RX_SI_CTRL_ADVT = "RX SI Control Advertisement" +RX_CDR_SUPPORT_FIELD = "RxCDRSupported" +RX_CDR_BYPASS_CTRL_FIELD = "RXCDRBypassSupported" +RX_OUTPUT_AMP_CTRL_SUPPORT_FIELD = "RXOutputAmpSupported" +RX_OUTPUT_EQ_PRE_CTRL_SUPPORT_FIELD = "RXOutputEQPreSupported" +RX_OUTPUT_EQ_POST_CTRL_SUPPORT_FIELD = "RXOutputEQPostSupported" +RX_OUTPUT_EQ_CTRL_SUPPORT_FIELD = "RXOutputEQSupported" + +ACTIVE_CTRL_FIELD = "Active Control Set" +ADAPTIVE_INPUT_EQ_ENABLE_TX = "AdaptiveInputEqEnableTx" +ADAPTIVE_INPUT_EQ_RECALLED_TX = "AdaptiveInputEqRecalledTx" +ADAPTIVE_INPUT_EQ_RECALLED_TX1_4 = "AdaptiveInputEqRecalledTx1_4" +ADAPTIVE_INPUT_EQ_RECALLED_TX1 = "AdaptiveInputEqRecalledTx1" +ADAPTIVE_INPUT_EQ_RECALLED_TX2 = "AdaptiveInputEqRecalledTx2" +ADAPTIVE_INPUT_EQ_RECALLED_TX3 = "AdaptiveInputEqRecalledTx3" +ADAPTIVE_INPUT_EQ_RECALLED_TX4 = "AdaptiveInputEqRecalledTx4" +ADAPTIVE_INPUT_EQ_RECALLED_TX5_8 = "AdaptiveInputEqRecalledTx5_8" +ADAPTIVE_INPUT_EQ_RECALLED_TX5 = "AdaptiveInputEqRecalledTx5" +ADAPTIVE_INPUT_EQ_RECALLED_TX6 = "AdaptiveInputEqRecalledTx6" +ADAPTIVE_INPUT_EQ_RECALLED_TX7 = "AdaptiveInputEqRecalledTx7" +ADAPTIVE_INPUT_EQ_RECALLED_TX8 = "AdaptiveInputEqRecalledTx8" +FIXED_INPUT_EQ_TARGET_TX = "FixedInputEqTargetTx" +FIXED_INPUT_EQ_TARGET_TX1_2 = "FixedInputEqTargetTx1_2" +FIXED_INPUT_EQ_TARGET_TX1 = "FixedInputEqTargetTx1" +FIXED_INPUT_EQ_TARGET_TX2 = "FixedInputEqTargetTx2" +FIXED_INPUT_EQ_TARGET_TX3_4 = "FixedInputEqTargetTx3_4" +FIXED_INPUT_EQ_TARGET_TX3 = "FixedInputEqTargetTx3" +FIXED_INPUT_EQ_TARGET_TX4 = "FixedInputEqTargetTx4" +FIXED_INPUT_EQ_TARGET_TX5_6 = "FixedInputEqTargetTx5_6" +FIXED_INPUT_EQ_TARGET_TX5 = "FixedInputEqTargetTx5" +FIXED_INPUT_EQ_TARGET_TX6 = "FixedInputEqTargetTx6" +FIXED_INPUT_EQ_TARGET_TX7_8 = "FixedInputEqTargetTx7_8" +FIXED_INPUT_EQ_TARGET_TX7 = "FixedInputEqTargetTx7" +FIXED_INPUT_EQ_TARGET_TX8 = "FixedInputEqTargetTx8" +CDR_ENABLE_TX = "CDREnableTx" +CDR_ENABLE_RX = "CDREnableRx" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX = "OutputEqPreCursorTargetRx" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX1_2 = "OutputEqPreCursorTargetRx1_2" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX1 = "OutputEqPreCursorTargetRx1" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX2 = "OutputEqPreCursorTargetRx2" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX3_4 = "OutputEqPreCursorTargetRx3_4" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX3 = "OutputEqPreCursorTargetRx3" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX4 = "OutputEqPreCursorTargetRx4" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX5_6 = "OutputEqPreCursorTargetRx5_6" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX5 = "OutputEqPreCursorTargetRx5" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX6 = "OutputEqPreCursorTargetRx6" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX7_8 = "OutputEqPreCursorTargetRx7_8" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX7 = "OutputEqPreCursorTargetRx7" +OUTPUT_EQ_PRE_CURSOR_TARGET_RX8 = "OutputEqPreCursorTargetRx8" +OUTPUT_EQ_POST_CURSOR_TARGET_RX = "OutputEqPostCursorTargetRx" +OUTPUT_EQ_POST_CURSOR_TARGET_RX1_2 = "OutputEqPostCursorTargetRx1_2" +OUTPUT_EQ_POST_CURSOR_TARGET_RX1 = "OutputEqPostCursorTargetRx1" +OUTPUT_EQ_POST_CURSOR_TARGET_RX2 = "OutputEqPostCursorTargetRx2" +OUTPUT_EQ_POST_CURSOR_TARGET_RX3_4 = "OutputEqPostCursorTargetRx3_4" +OUTPUT_EQ_POST_CURSOR_TARGET_RX3 = "OutputEqPostCursorTargetRx3" +OUTPUT_EQ_POST_CURSOR_TARGET_RX4 = "OutputEqPostCursorTargetRx4" +OUTPUT_EQ_POST_CURSOR_TARGET_RX5_6 = "OutputEqPostCursorTargetRx5_6" +OUTPUT_EQ_POST_CURSOR_TARGET_RX5 = "OutputEqPostCursorTargetRx5" +OUTPUT_EQ_POST_CURSOR_TARGET_RX6 = "OutputEqPostCursorTargetRx6" +OUTPUT_EQ_POST_CURSOR_TARGET_RX7_8 = "OutputEqPostCursorTargetRx7_8" +OUTPUT_EQ_POST_CURSOR_TARGET_RX7 = "OutputEqPostCursorTargetRx7" +OUTPUT_EQ_POST_CURSOR_TARGET_RX8 = "OutputEqPostCursorTargetRx8" +OUTPUT_AMPLITUDE_TARGET_RX = "OutputAmplitudeTargetRx" +OUTPUT_AMPLITUDE_TARGET_RX1_2 = "OutputAmplitudeTargetRx1_2" +OUTPUT_AMPLITUDE_TARGET_RX1 = "OutputAmplitudeTargetRx1" +OUTPUT_AMPLITUDE_TARGET_RX2 = "OutputAmplitudeTargetRx2" +OUTPUT_AMPLITUDE_TARGET_RX3_4 = "OutputAmplitudeTargetRx3_4" +OUTPUT_AMPLITUDE_TARGET_RX3 = "OutputAmplitudeTargetRx3" +OUTPUT_AMPLITUDE_TARGET_RX4 = "OutputAmplitudeTargetRx4" +OUTPUT_AMPLITUDE_TARGET_RX5_6 = "OutputAmplitudeTargetRx5_6" +OUTPUT_AMPLITUDE_TARGET_RX5 = "OutputAmplitudeTargetRx5" +OUTPUT_AMPLITUDE_TARGET_RX6 = "OutputAmplitudeTargetRx6" +OUTPUT_AMPLITUDE_TARGET_RX7_8 = "OutputAmplitudeTargetRx7_8" +OUTPUT_AMPLITUDE_TARGET_RX7 = "OutputAmplitudeTargetRx7" +OUTPUT_AMPLITUDE_TARGET_RX8 = "OutputAmplitudeTargetRx8" + # C-CMIS # Module configuration support fields diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py index 512ed0776..140faeafd 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py @@ -11,6 +11,7 @@ HexRegField, NumberRegField, RegBitField, + RegBitsField, RegGroupField, StringRegField, ) @@ -195,6 +196,21 @@ def __init__(self, codes): NumberRegField(consts.PAGE_SUPPORT_ADVT_FIELD, self.getaddr(0x1, 142), RegBitField(consts.VDM_SUPPORTED, 6), ), + NumberRegField(consts.TX_INPUT_EQ_MAX, self.getaddr(0x1, 153), + *(RegBitField("Bit%d" % (bit), bit) for bit in range (0 , 4)) + ), + NumberRegField(consts.RX_OUTPUT_LEVEL_SUPPORT, self.getaddr(0x1, 153), + RegBitField(consts.RX_OUTPUT_LEVEL_0_SUPPORTED, 4), + RegBitField(consts.RX_OUTPUT_LEVEL_1_SUPPORTED, 5), + RegBitField(consts.RX_OUTPUT_LEVEL_2_SUPPORTED, 6), + RegBitField(consts.RX_OUTPUT_LEVEL_3_SUPPORTED, 7), + ), + NumberRegField(consts.RX_OUTPUT_EQ_PRE_CURSOR_MAX, self.getaddr(0x1, 154), + *(RegBitField("Bit%d" % (bit), bit) for bit in range (0 , 4)) + ), + NumberRegField(consts.RX_OUTPUT_EQ_POST_CURSOR_MAX, self.getaddr(0x1, 154), + *(RegBitField("Bit%d" % (bit), bit) for bit in range (4 , 8)) + ), NumberRegField(consts.CTRLS_ADVT_FIELD, self.getaddr(0x1, 155), RegBitField(consts.TX_DISABLE_SUPPORT_FIELD, 1), size=2, format=" Date: Mon, 14 Aug 2023 14:58:09 -0700 Subject: [PATCH 21/24] Fix NumRegField byte(s) read (#392) Description NumRegField read worked fine when the fields were single bit. Now that we have RegBitsFields with multiple bits we need to adjust the mask accordingly. Motivation and Context NumRegFields having RegBitsFields did not read the byte correctly. This is now fixed with this change. --- sonic_platform_base/sonic_xcvr/fields/xcvr_field.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py b/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py index 93f51613f..a9e0fd5ad 100644 --- a/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py +++ b/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py @@ -20,6 +20,7 @@ def __init__(self, name, offset, **kwargs): self.offset = offset self.ro = kwargs.get("ro", True) self.deps = kwargs.get("deps", []) + self.bitmask = None def get_fields(self): """ @@ -86,6 +87,7 @@ def __init__(self, name, bitpos, offset=None, **kwargs): super(RegBitField, self).__init__(name, offset, **kwargs) assert bitpos < 64 self.bitpos = bitpos + self.bitmask = 1 << self.bitpos def get_size(self): return 1 @@ -115,7 +117,7 @@ class RegBitsField(XcvrField): def __init__(self, name, bitpos, offset=None, **kwargs): super(RegBitsField, self).__init__(name, offset, **kwargs) self.size = self.size = kwargs.get("size", 1) #No of bits - assert (bitpos >= 0 and (bitpos+self.size-1 < 8), "bitpos must be within one byte") + assert bitpos >= 0 and bitpos+self.size <= 8, "bitpos must be within one byte" self.bitpos = bitpos self.bitmask = (((1 << self.size) - 1) << self.bitpos) & 0xff @@ -159,7 +161,7 @@ def get_bitmask(self): return None mask = 0 for field in self.fields: - mask |= 1 << field.bitpos + mask |= field.bitmask return mask def get_size(self): From 1988b37c7668394f38f155c86f5462a4461fe82e Mon Sep 17 00:00:00 2001 From: ChiouRung Haung Date: Fri, 18 Aug 2023 01:38:22 +0800 Subject: [PATCH 22/24] Convert the tx/rx power unit to the dBm unit (#377) * JIRA-SONIC-7072: Convert the tx/rx power unit to the dBm unit in get_transceiver_bulk_status(). The tx/rx power unit in the eeprom is mW, and the tx/rx power unit for the API is dBm. It should be converted to the dBm in get_transceiver_bulk_status(). Signed-off-by: chiourung_huang --------- Signed-off-by: chiourung_huang Co-authored-by: Prince George <45705344+prgeor@users.noreply.github.com> --- .../sonic_xcvr/api/public/sff8436.py | 4 +- .../sonic_xcvr/api/public/sff8472.py | 4 +- .../sonic_xcvr/api/public/sff8636.py | 4 +- tests/sonic_xcvr/test_sff8436.py | 47 +++++++++++++++++++ tests/sonic_xcvr/test_sff8472.py | 46 ++++++++++++++++++ tests/sonic_xcvr/test_sff8636.py | 46 ++++++++++++++++++ 6 files changed, 145 insertions(+), 6 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8436.py b/sonic_platform_base/sonic_xcvr/api/public/sff8436.py index 152b1281e..4336cf6f0 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/sff8436.py +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8436.py @@ -112,8 +112,8 @@ def get_transceiver_bulk_status(self): for i in range(1, self.NUM_CHANNELS + 1): bulk_status["tx%dbias" % i] = tx_bias[i - 1] - bulk_status["rx%dpower" % i] = rx_power[i - 1] - bulk_status["tx%dpower" % i] = tx_power[i - 1] + bulk_status["rx%dpower" % i] = self.mw_to_dbm(rx_power[i - 1]) if rx_power[i - 1] != 'N/A' else 'N/A' + bulk_status["tx%dpower" % i] = self.mw_to_dbm(tx_power[i - 1]) if tx_power[i - 1] != 'N/A' else 'N/A' return bulk_status diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8472.py b/sonic_platform_base/sonic_xcvr/api/public/sff8472.py index f1a7daf2b..36e458d3a 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/sff8472.py +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8472.py @@ -104,8 +104,8 @@ def get_transceiver_bulk_status(self): for i in range(1, self.NUM_CHANNELS + 1): bulk_status["tx%dbias" % i] = tx_bias[i - 1] - bulk_status["rx%dpower" % i] = rx_power[i - 1] - bulk_status["tx%dpower" % i] = tx_power[i - 1] + bulk_status["rx%dpower" % i] = self.mw_to_dbm(rx_power[i - 1]) if rx_power[i - 1] != 'N/A' else 'N/A' + bulk_status["tx%dpower" % i] = self.mw_to_dbm(tx_power[i - 1]) if tx_power[i - 1] != 'N/A' else 'N/A' # Added to avoid failing xcvrd. Ideally xcvrd should be fixed so that this is not necessary for i in range(2, 5): diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8636.py b/sonic_platform_base/sonic_xcvr/api/public/sff8636.py index 9c4d34214..f59163a6c 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/sff8636.py +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8636.py @@ -119,8 +119,8 @@ def get_transceiver_bulk_status(self): for i in range(1, self.NUM_CHANNELS + 1): bulk_status["tx%dbias" % i] = tx_bias[i - 1] - bulk_status["rx%dpower" % i] = rx_power[i - 1] - bulk_status["tx%dpower" % i] = tx_power[i - 1] + bulk_status["rx%dpower" % i] = self.mw_to_dbm(rx_power[i - 1]) if rx_power[i - 1] != 'N/A' else 'N/A' + bulk_status["tx%dpower" % i] = self.mw_to_dbm(tx_power[i - 1]) if tx_power[i - 1] != 'N/A' else 'N/A' return bulk_status diff --git a/tests/sonic_xcvr/test_sff8436.py b/tests/sonic_xcvr/test_sff8436.py index 1c192e7d0..7b132df13 100644 --- a/tests/sonic_xcvr/test_sff8436.py +++ b/tests/sonic_xcvr/test_sff8436.py @@ -201,3 +201,50 @@ def test_get_transceiver_status(self, mock_response, expected): self.api.get_tx_disable.return_value = mock_response[3] result = self.api.get_transceiver_status() assert result == expected + + @pytest.mark.parametrize("mock_response, expected",[ + ( + [ + 50, + 3.3, + [70, 70, 70, 70], + [0.1, 0.1, 0.1, 0.1], + [0.1, 0.1, 0.1, 0.1], + True, True, True, True, True, True + ], + { + 'temperature': 50, + 'voltage': 3.3, + 'tx1power': -10.0, 'tx2power': -10.0, 'tx3power': -10.0, 'tx4power': -10.0, + 'rx1power': -10.0, 'rx2power': -10.0, 'rx3power': -10.0, 'rx4power': -10.0, + 'tx1bias': 70, 'tx2bias': 70, 'tx3bias': 70, 'tx4bias': 70, + } + ) + ]) + def test_get_transceiver_bulk_status(self, mock_response, expected): + self.api.get_module_temperature = MagicMock() + self.api.get_module_temperature.return_value = mock_response[0] + self.api.get_voltage = MagicMock() + self.api.get_voltage.return_value = mock_response[1] + self.api.get_tx_bias = MagicMock() + self.api.get_tx_bias.return_value = mock_response[2] + self.api.get_rx_power = MagicMock() + self.api.get_rx_power.return_value = mock_response[3] + self.api.get_tx_power = MagicMock() + self.api.get_tx_power.return_value = mock_response[4] + self.api.get_rx_los_support = MagicMock() + self.api.get_rx_los_support.return_value = mock_response[5] + self.api.get_tx_fault_support = MagicMock() + self.api.get_tx_fault_support.return_value = mock_response[6] + self.api.get_tx_disable_support = MagicMock() + self.api.get_tx_disable_support.return_value = mock_response[7] + self.api.get_tx_bias_support = MagicMock() + self.api.get_tx_bias_support.return_value = mock_response[8] + self.api.get_tx_power_support = MagicMock() + self.api.get_tx_power_support.return_value = mock_response[9] + self.api.get_rx_power_support = MagicMock() + self.api.get_rx_power_support.return_value = mock_response[10] + result = self.api.get_transceiver_bulk_status() + assert result == expected + + diff --git a/tests/sonic_xcvr/test_sff8472.py b/tests/sonic_xcvr/test_sff8472.py index 7a10c6cf6..a1d8c1a2c 100644 --- a/tests/sonic_xcvr/test_sff8472.py +++ b/tests/sonic_xcvr/test_sff8472.py @@ -243,3 +243,49 @@ def test_get_transceiver_status(self, mock_response, expected): self.api.get_tx_disable.return_value = mock_response[3] result = self.api.get_transceiver_status() assert result == expected + + @pytest.mark.parametrize("mock_response, expected",[ + ( + [ + 50, + 3.3, + [70], + [0.1], + [0.1], + True, True, True, True, True, True + ], + { + 'temperature': 50, + 'voltage': 3.3, + 'tx1power': -10.0, 'tx2power': 'N/A', 'tx3power': 'N/A', 'tx4power': 'N/A', + 'rx1power': -10.0, 'rx2power': 'N/A', 'rx3power': 'N/A', 'rx4power': 'N/A', + 'tx1bias': 70, 'tx2bias': 'N/A', 'tx3bias': 'N/A', 'tx4bias': 'N/A', + } + ) + ]) + def test_get_transceiver_bulk_status(self, mock_response, expected): + self.api.get_module_temperature = MagicMock() + self.api.get_module_temperature.return_value = mock_response[0] + self.api.get_voltage = MagicMock() + self.api.get_voltage.return_value = mock_response[1] + self.api.get_tx_bias = MagicMock() + self.api.get_tx_bias.return_value = mock_response[2] + self.api.get_rx_power = MagicMock() + self.api.get_rx_power.return_value = mock_response[3] + self.api.get_tx_power = MagicMock() + self.api.get_tx_power.return_value = mock_response[4] + self.api.get_rx_los_support = MagicMock() + self.api.get_rx_los_support.return_value = mock_response[5] + self.api.get_tx_fault_support = MagicMock() + self.api.get_tx_fault_support.return_value = mock_response[6] + self.api.get_tx_disable_support = MagicMock() + self.api.get_tx_disable_support.return_value = mock_response[7] + self.api.get_tx_bias_support = MagicMock() + self.api.get_tx_bias_support.return_value = mock_response[8] + self.api.get_tx_power_support = MagicMock() + self.api.get_tx_power_support.return_value = mock_response[9] + self.api.get_rx_power_support = MagicMock() + self.api.get_rx_power_support.return_value = mock_response[10] + result = self.api.get_transceiver_bulk_status() + assert result == expected + diff --git a/tests/sonic_xcvr/test_sff8636.py b/tests/sonic_xcvr/test_sff8636.py index 7b337b9ae..7a566bc2d 100644 --- a/tests/sonic_xcvr/test_sff8636.py +++ b/tests/sonic_xcvr/test_sff8636.py @@ -201,3 +201,49 @@ def test_get_transceiver_status(self, mock_response, expected): self.api.get_tx_disable.return_value = mock_response[3] result = self.api.get_transceiver_status() assert result == expected + + @pytest.mark.parametrize("mock_response, expected",[ + ( + [ + 50, + 3.3, + [70, 70, 70, 70], + [0.1, 0.1, 0.1, 0.1], + [0.1, 0.1, 0.1, 0.1], + True, True, True, True, True, True + ], + { + 'temperature': 50, + 'voltage': 3.3, + 'tx1power': -10.0, 'tx2power': -10.0, 'tx3power': -10.0, 'tx4power': -10.0, + 'rx1power': -10.0, 'rx2power': -10.0, 'rx3power': -10.0, 'rx4power': -10.0, + 'tx1bias': 70, 'tx2bias': 70, 'tx3bias': 70, 'tx4bias': 70, + } + ) + ]) + def test_get_transceiver_bulk_status(self, mock_response, expected): + self.api.get_module_temperature = MagicMock() + self.api.get_module_temperature.return_value = mock_response[0] + self.api.get_voltage = MagicMock() + self.api.get_voltage.return_value = mock_response[1] + self.api.get_tx_bias = MagicMock() + self.api.get_tx_bias.return_value = mock_response[2] + self.api.get_rx_power = MagicMock() + self.api.get_rx_power.return_value = mock_response[3] + self.api.get_tx_power = MagicMock() + self.api.get_tx_power.return_value = mock_response[4] + self.api.get_rx_los_support = MagicMock() + self.api.get_rx_los_support.return_value = mock_response[5] + self.api.get_tx_fault_support = MagicMock() + self.api.get_tx_fault_support.return_value = mock_response[6] + self.api.get_tx_disable_support = MagicMock() + self.api.get_tx_disable_support.return_value = mock_response[7] + self.api.get_tx_bias_support = MagicMock() + self.api.get_tx_bias_support.return_value = mock_response[8] + self.api.get_tx_power_support = MagicMock() + self.api.get_tx_power_support.return_value = mock_response[9] + self.api.get_rx_power_support = MagicMock() + self.api.get_rx_power_support.return_value = mock_response[10] + result = self.api.get_transceiver_bulk_status() + assert result == expected + From 086a88c48756465a68f33b4a73d7051f63e746f6 Mon Sep 17 00:00:00 2001 From: Mridul Bajpai <30709399+bmridul@users.noreply.github.com> Date: Thu, 7 Sep 2023 15:25:44 -0700 Subject: [PATCH 23/24] Sensor Monitor support (#393) New base class is introduced for defining voltage and current sensors. Chassis and Module base classes are enhanced with APIs to retrieve voltage and current sensor data from platform. --- sonic_platform_base/__init__.py | 1 + sonic_platform_base/chassis_base.py | 91 +++++++++++++++ sonic_platform_base/module_base.py | 91 +++++++++++++++ sonic_platform_base/sensor_base.py | 173 ++++++++++++++++++++++++++++ tests/chassis_base_test.py | 17 ++- tests/module_base_test.py | 19 +++ tests/sensor_base_test.py | 51 ++++++++ 7 files changed, 442 insertions(+), 1 deletion(-) create mode 100644 sonic_platform_base/sensor_base.py create mode 100644 tests/module_base_test.py create mode 100644 tests/sensor_base_test.py diff --git a/sonic_platform_base/__init__.py b/sonic_platform_base/__init__.py index 3d7f64b24..45ece3cfe 100644 --- a/sonic_platform_base/__init__.py +++ b/sonic_platform_base/__init__.py @@ -8,4 +8,5 @@ from . import psu_base from . import sfp_base from . import thermal_base +from . import sensor_base from . import watchdog_base diff --git a/sonic_platform_base/chassis_base.py b/sonic_platform_base/chassis_base.py index 877d9c849..6cd4403c6 100644 --- a/sonic_platform_base/chassis_base.py +++ b/sonic_platform_base/chassis_base.py @@ -54,6 +54,8 @@ def __init__(self): # List of ThermalBase-derived objects representing all thermals # available on the chassis self._thermal_list = [] + self._voltage_sensor_list = [] + self._current_sensor_list = [] # List of SfpBase-derived objects representing all sfps # available on the chassis @@ -451,6 +453,95 @@ def get_thermal_manager(self): """ raise NotImplementedError + ############################################## + # Voltage Sensor Methods + ############################################## + + def get_num_voltage_sensors(self): + """ + Retrieves the number of voltage sensors available on this chassis + + Returns: + An integer, the number of voltage sensors available on this chassis + """ + return len(self._voltage_sensor_list) + + def get_all_voltage_sensors(self): + """ + Retrieves all voltage sensors available on this chassis + + Returns: + A list of objects derived from VoltageSensorBase representing all voltage + sensors available on this chassis + """ + return self._voltage_sensor_list + + def get_voltage_sensor(self, index): + """ + Retrieves voltage sensor unit represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the voltage sensor to + retrieve + + Returns: + An object derived from VoltageSensorBase representing the specified voltage sensor + """ + voltage_sensor = None + + try: + voltage_sensor = self._voltage_sensor_list[index] + except IndexError: + sys.stderr.write("Voltage sensor index {} out of range (0-{})\n".format( + index, len(self._voltage_sensor_list)-1)) + + return voltage_sensor + + ############################################## + # Current Sensor Methods + ############################################## + + def get_num_current_sensors(self): + """ + Retrieves the number of current sensors available on this chassis + + Returns: + An integer, the number of current sensors available on this chassis + """ + return len(self._current_sensor_list) + + def get_all_current_sensors(self): + """ + Retrieves all current sensors available on this chassis + + Returns: + A list of objects derived from CurrentSensorBase representing all current + sensors available on this chassis + """ + return self._current_sensor_list + + def get_current_sensor(self, index): + """ + Retrieves current sensor object represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the current sensor to + retrieve + + Returns: + An object derived from CurrentSensorBase representing the specified current + sensor + """ + current_sensor = None + + try: + current_sensor = self._current_sensor_list[index] + except IndexError: + sys.stderr.write("Current sensor index {} out of range (0-{})\n".format( + index, len(self._current_sensor_list)-1)) + + return current_sensor + ############################################## # SFP methods ############################################## diff --git a/sonic_platform_base/module_base.py b/sonic_platform_base/module_base.py index 3e96f8b24..ccd507b44 100644 --- a/sonic_platform_base/module_base.py +++ b/sonic_platform_base/module_base.py @@ -66,6 +66,8 @@ def __init__(self): # List of ThermalBase-derived objects representing all thermals # available on the module self._thermal_list = [] + self._voltage_sensor_list = [] + self._current_sensor_list = [] # List of SfpBase-derived objects representing all sfps # available on the module @@ -372,6 +374,95 @@ def get_thermal(self, index): return thermal + ############################################## + # Voltage Sensor methods + ############################################## + + def get_num_voltage_sensors(self): + """ + Retrieves the number of voltage sensors available on this module + + Returns: + An integer, the number of voltage sensors available on this module + """ + return len(self._voltage_sensor_list) + + def get_all_voltage_sensors(self): + """ + Retrieves all voltage sensors available on this module + + Returns: + A list of objects derived from VoltageSensorBase representing all voltage + sensors available on this module + """ + return self._voltage_sensor_list + + def get_voltage_sensor(self, index): + """ + Retrieves voltage sensor unit represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the voltage sensor to + retrieve + + Returns: + An object derived from VoltageSensorBase representing the specified voltage + sensor + """ + voltage_sensor = None + + try: + voltage_sensor = self._voltage_sensor_list[index] + except IndexError: + sys.stderr.write("Voltage sensor index {} out of range (0-{})\n".format( + index, len(self._voltage_sensor_list)-1)) + + return voltage_sensor + + ############################################## + # Current sensor methods + ############################################## + + def get_num_current_sensors(self): + """ + Retrieves the number of current sensors available on this module + + Returns: + An integer, the number of current sensors available on this module + """ + return len(self._current_sensor_list) + + def get_all_current_sensors(self): + """ + Retrieves all current sensors available on this module + + Returns: + A list of objects derived from CurrentSensorBase representing all current + sensors available on this module + """ + return self._current_sensor_list + + def get_current_sensor(self, index): + """ + Retrieves current sensor object represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the current sensor to + retrieve + + Returns: + An object derived from CurrentSensorBase representing the specified current_sensor + """ + current_sensor = None + + try: + current_sensor = self._current_sensor_list[index] + except IndexError: + sys.stderr.write("Current sensor index {} out of range (0-{})\n".format( + index, len(self._current_sensor_list)-1)) + + return current_sensor + ############################################## # SFP methods ############################################## diff --git a/sonic_platform_base/sensor_base.py b/sonic_platform_base/sensor_base.py new file mode 100644 index 000000000..9e2c20a71 --- /dev/null +++ b/sonic_platform_base/sensor_base.py @@ -0,0 +1,173 @@ +""" + sensor_base.py + + Abstract base class for implementing a platform-specific class with which + to interact with a sensor module in SONiC +""" + +from . import device_base + +class SensorBase(device_base.DeviceBase): + """ + Abstract base class for interfacing with a sensor module + """ + + @classmethod + def get_type(cls): + """ + Specifies the type of the sensor such as current/voltage etc. + + Returns: + Sensor type + """ + raise NotImplementedError + + def get_value(self): + """ + Retrieves measurement reported by sensor + + Returns: + Sensor measurement + """ + raise NotImplementedError + + @classmethod + def get_unit(cls): + """ + Retrieves unit of measurement reported by sensor + + Returns: + Sensor measurement unit + """ + raise NotImplementedError + + def get_high_threshold(self): + """ + Retrieves the high threshold of sensor + + Returns: + High threshold + """ + raise NotImplementedError + + def get_low_threshold(self): + """ + Retrieves the low threshold + + Returns: + Low threshold + """ + raise NotImplementedError + + def set_high_threshold(self, value): + """ + Sets the high threshold value of sensor + + Args: + value: High threshold value to set + + Returns: + A boolean, True if threshold is set successfully, False if not + """ + raise NotImplementedError + + def set_low_threshold(self, value): + """ + Sets the low threshold value of sensor + + Args: + value: Value + + Returns: + A boolean, True if threshold is set successfully, False if not + """ + raise NotImplementedError + + def get_high_critical_threshold(self): + """ + Retrieves the high critical threshold value of sensor + + Returns: + The high critical threshold value of sensor + """ + raise NotImplementedError + + def get_low_critical_threshold(self): + """ + Retrieves the low critical threshold value of sensor + + Returns: + The low critical threshold value of sensor + """ + raise NotImplementedError + + def set_high_critical_threshold(self, value): + """ + Sets the critical high threshold value of sensor + + Args: + value: Critical high threshold Value + + Returns: + A boolean, True if threshold is set successfully, False if not + """ + raise NotImplementedError + + def set_low_critical_threshold(self, value): + """ + Sets the critical low threshold value of sensor + + Args: + value: Critial low threshold Value + + Returns: + A boolean, True if threshold is set successfully, False if not + """ + raise NotImplementedError + + def get_minimum_recorded(self): + """ + Retrieves the minimum recorded value of sensor + + Returns: + The minimum recorded value of sensor + """ + raise NotImplementedError + + def get_maximum_recorded(self): + """ + Retrieves the maximum recorded value of sensor + + Returns: + The maximum recorded value of sensor + """ + raise NotImplementedError + + + +class VoltageSensorBase(SensorBase): + """ + Abstract base class for interfacing with a voltage sensor module + """ + + @classmethod + def get_type(cls): + return "SENSOR_TYPE_VOLTAGE" + + @classmethod + def get_unit(cls): + return "mV" + + +class CurrentSensorBase(SensorBase): + """ + Abstract base class for interfacing with a current sensor module + """ + + @classmethod + def get_type(cls): + return "SENSOR_TYPE_CURRENT" + + @classmethod + def get_unit(cls): + return "mA" diff --git a/tests/chassis_base_test.py b/tests/chassis_base_test.py index d410ff4ae..e550ce5f8 100644 --- a/tests/chassis_base_test.py +++ b/tests/chassis_base_test.py @@ -33,4 +33,19 @@ def test_chassis_base(self): except NotImplementedError: exception_raised = True - assert exception_raised \ No newline at end of file + assert exception_raised + + def test_sensors(self): + chassis = ChassisBase() + assert(chassis.get_num_voltage_sensors() == 0) + assert(chassis.get_all_voltage_sensors() == []) + assert(chassis.get_voltage_sensor(0) == None) + chassis._voltage_sensor_list = ["s1"] + assert(chassis.get_all_voltage_sensors() == ["s1"]) + assert(chassis.get_voltage_sensor(0) == "s1") + assert(chassis.get_num_current_sensors() == 0) + assert(chassis.get_all_current_sensors() == []) + assert(chassis.get_current_sensor(0) == None) + chassis._current_sensor_list = ["s1"] + assert(chassis.get_all_current_sensors() == ["s1"]) + assert(chassis.get_current_sensor(0) == "s1") diff --git a/tests/module_base_test.py b/tests/module_base_test.py new file mode 100644 index 000000000..20d0ef05a --- /dev/null +++ b/tests/module_base_test.py @@ -0,0 +1,19 @@ +from sonic_platform_base.module_base import ModuleBase + +class TestModuleBase: + + def test_sensors(self): + module = ModuleBase() + assert(module.get_num_voltage_sensors() == 0) + assert(module.get_all_voltage_sensors() == []) + assert(module.get_voltage_sensor(0) == None) + module._voltage_sensor_list = ["s1"] + assert(module.get_all_voltage_sensors() == ["s1"]) + assert(module.get_voltage_sensor(0) == "s1") + assert(module.get_num_current_sensors() == 0) + assert(module.get_all_current_sensors() == []) + assert(module.get_current_sensor(0) == None) + module._current_sensor_list = ["s1"] + assert(module.get_all_current_sensors() == ["s1"]) + assert(module.get_current_sensor(0) == "s1") + diff --git a/tests/sensor_base_test.py b/tests/sensor_base_test.py new file mode 100644 index 000000000..bba0d4fd4 --- /dev/null +++ b/tests/sensor_base_test.py @@ -0,0 +1,51 @@ +''' +Test sensor module base classes +''' + +from unittest import mock +from sonic_platform_base.sensor_base import SensorBase +from sonic_platform_base.sensor_base import VoltageSensorBase +from sonic_platform_base.sensor_base import CurrentSensorBase + +class TestSensorBase: + ''' + Collection of SensorBase test methods + ''' + @staticmethod + def test_sensor_base(): + ''' + Verify unimplemented methods + ''' + sensor = SensorBase() + not_implemented_methods = [ + (sensor.get_value,), + (sensor.get_high_threshold,), + (sensor.get_low_threshold,), + (sensor.set_high_threshold,0), + (sensor.set_low_threshold,0), + (sensor.get_high_critical_threshold,), + (sensor.set_high_critical_threshold,0), + (sensor.get_low_critical_threshold,), + (sensor.set_low_critical_threshold,0), + (sensor.get_minimum_recorded,), + (sensor.get_maximum_recorded,)] + + for method in not_implemented_methods: + expected_exception = False + try: + func = method[0] + args = method[1:] + func(*args) + except Exception as exc: + expected_exception = isinstance(exc, NotImplementedError) + assert expected_exception + + @staticmethod + def test_voltage_sensor_base(): + assert(VoltageSensorBase.get_type() == "SENSOR_TYPE_VOLTAGE") + assert(VoltageSensorBase.get_unit() == "mV") + + @staticmethod + def test_current_sensor_base(): + assert(CurrentSensorBase.get_type() == "SENSOR_TYPE_CURRENT") + assert(CurrentSensorBase.get_unit() == "mA") From 0dafb55befe81734f5532592a29e90da59c14838 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Wed, 13 Sep 2023 11:13:45 -0700 Subject: [PATCH 24/24] Remove command to install libhiredis deb file (#398) libhiredis is now used as-is from the slave container, since we're not making any changes to this package. See also sonic-net/sonic-buildimage#15633. Signed-off-by: Saikrishna Arcot --- azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b62671c07..637849b3d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -41,13 +41,12 @@ steps: - script: | set -xe - sudo apt-get -y purge libhiredis-dev libnl-3-dev libnl-route-3-dev || true + sudo apt-get -y purge libnl-3-dev libnl-route-3-dev || true sudo dpkg -i libyang_1.0.73_amd64.deb \ libnl-3-200_*.deb \ libnl-genl-3-200_*.deb \ libnl-route-3-200_*.deb \ libnl-nf-3-200_*.deb \ - libhiredis0.14_*.deb \ libswsscommon_1.0.0_amd64.deb \ python3-swsscommon_1.0.0_amd64.deb workingDirectory: $(Build.ArtifactStagingDirectory)/target/debs/bullseye/