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 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/ 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 e292cfcd3..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 ############################################## @@ -546,6 +637,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/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/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/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_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/sonic_platform_base/sonic_ssd/ssd_generic.py b/sonic_platform_base/sonic_ssd/ssd_generic.py index 95ed5c332..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 @@ -124,7 +129,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 +155,23 @@ 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 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/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py index 6929d2614..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 @@ -352,30 +349,24 @@ 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 - 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 @@ -496,77 +487,71 @@ 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] 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] @@ -644,7 +629,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 @@ -759,83 +744,77 @@ 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] - 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['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] + 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): diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 7d6f2e883..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 @@ -161,10 +167,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,21 +181,24 @@ 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() - 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 \ @@ -198,15 +207,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' @@ -501,8 +506,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)] @@ -706,6 +715,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 @@ -730,13 +757,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): ''' @@ -757,13 +789,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): ''' @@ -874,7 +911,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) @@ -1286,10 +1323,24 @@ 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 + 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 + 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: @@ -1300,12 +1351,12 @@ 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} - 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) @@ -1532,7 +1583,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() @@ -1559,7 +1610,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: @@ -1567,7 +1618,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 @@ -1620,6 +1671,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 @@ -1738,6 +1791,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): @@ -2015,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 @@ -2038,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/api/public/sff8436.py b/sonic_platform_base/sonic_xcvr/api/public/sff8436.py index 4cf2aacce..4336cf6f0 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,18 +106,14 @@ 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 } 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 3ccfcfb0e..36e458d3a 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,18 +98,14 @@ 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 } 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): @@ -247,20 +258,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/sonic_platform_base/sonic_xcvr/api/public/sff8636.py b/sonic_platform_base/sonic_xcvr/api/public/sff8636.py index e14c07da5..f59163a6c 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,18 +113,14 @@ 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 } 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/xcvr_api.py b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py index e759f794e..eaaed35da 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 @@ -79,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, @@ -135,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/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 diff --git a/sonic_platform_base/sonic_xcvr/fields/consts.py b/sonic_platform_base/sonic_xcvr/fields/consts.py index d5cf4f608..e9e250a32 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" @@ -297,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/fields/xcvr_field.py b/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py index 68febd72c..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 @@ -105,7 +107,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 <= 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) @@ -128,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): 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..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, ) @@ -126,6 +127,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, @@ -189,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=" 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, diff --git a/tests/chassis_base_test.py b/tests/chassis_base_test.py index c2f2e5f97..e550ce5f8 100644 --- a/tests/chassis_base_test.py +++ b/tests/chassis_base_test.py @@ -16,3 +16,36 @@ 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 + + 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/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") + 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 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") diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index 1f106197c..5b072deb8 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) ]) @@ -652,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), @@ -689,13 +727,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", [ @@ -735,13 +794,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", [ @@ -1082,6 +1162,10 @@ 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}), ]) def test_get_module_fw_info(self, mock_response, expected): self.api.cdb = MagicMock() @@ -1123,7 +1207,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, '') ), ( @@ -1133,12 +1217,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, '') ), ]) @@ -1155,7 +1239,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), ( [ { @@ -1183,7 +1267,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', @@ -1196,9 +1281,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', @@ -1247,14 +1332,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() @@ -1268,10 +1351,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], @@ -1296,11 +1375,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, @@ -1311,10 +1385,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, @@ -1327,10 +1397,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'], @@ -1348,50 +1414,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 @@ -1601,7 +1654,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', @@ -1633,6 +1687,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, @@ -1835,7 +1898,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', @@ -1894,6 +1958,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 @@ -2128,24 +2196,120 @@ def test_set_application(self): self.api.xcvr_eeprom.write = MagicMock() self.api.xcvr_eeprom.write.call_count = 0 - self.api.set_application(0x00, 1) - assert self.api.xcvr_eeprom.write.call_count == 1 + self.api.set_application(0x00, 1, 0) + assert self.api.xcvr_eeprom.write.call_count == 0 self.api.xcvr_eeprom.write.call_count = 0 - self.api.set_application(0x01, 1) - assert self.api.xcvr_eeprom.write.call_count == 1 + 1 + self.api.set_application(0x01, 1, 1) + assert self.api.xcvr_eeprom.write.call_count == 1 self.api.xcvr_eeprom.write.call_count = 0 - self.api.set_application(0x0f, 1) - assert self.api.xcvr_eeprom.write.call_count == 4 + 1 + self.api.set_application(0x0f, 1, 1) + assert self.api.xcvr_eeprom.write.call_count == 4 self.api.xcvr_eeprom.write.call_count = 0 - self.api.set_application(0xff, 1) - assert self.api.xcvr_eeprom.write.call_count == 8 + 1 + self.api.set_application(0xff, 1, 1) + assert self.api.xcvr_eeprom.write.call_count == 8 self.api.xcvr_eeprom.write.call_count = 0 - self.api.set_application(0x7fffffff, 1) - assert self.api.xcvr_eeprom.write.call_count == self.api.NUM_CHANNELS + 1 + self.api.set_application(0x7fffffff, 1, 1) + assert self.api.xcvr_eeprom.write.call_count == self.api.NUM_CHANNELS + + def test_set_module_si_eq_pre_settings(self): + optics_si_dict = { "OutputEqPreCursorTargetRx":{ + "OutputEqPreCursorTargetRx1":2, "OutputEqPreCursorTargetRx2":2, "OutputEqPreCursorTargetRx3":2, "OutputEqPreCursorTargetRx4":2, + "OutputEqPreCursorTargetRx5":2, "OutputEqPreCursorTargetRx6":2, "OutputEqPreCursorTargetRx7":2, "OutputEqPreCursorTargetRx8":2 } + } + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.write = MagicMock() + mock_resp = [0x1, 0x7] + self.api.xcvr_eeprom.read.side_effect = mock_resp + self.api.stage_custom_si_settings(0x01, optics_si_dict) + assert self.api.xcvr_eeprom.write.call_count == 1 + + def test_set_module_si_eq_en_settings(self): + optics_si_dict = { "AdaptiveInputEqEnableTx":{ + "AdaptiveInputEqEnableTx1": 2, "AdaptiveInputEqEnableTx2": 2, "AdaptiveInputEqEnableTx3": 2, "AdaptiveInputEqEnableTx4": 2, + "AdaptiveInputEqEnableTx5": 2, "AdaptiveInputEqEnableTx6": 2, "AdaptiveInputEqEnableTx7": 2, "AdaptiveInputEqEnableTx8": 2,} + } + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.write = MagicMock() + mock_resp = [0x1, 0x3] + self.api.xcvr_eeprom.read.side_effect = mock_resp + self.api.stage_custom_si_settings(0xff, optics_si_dict) + assert self.api.xcvr_eeprom.write.call_count == 8 + + def test_set_module_si_eq_recall_settings(self): + optics_si_dict = { "AdaptiveInputEqRecalledTx":{ + "AdaptiveInputEqRecalledTx1":1, "AdaptiveInputEqRecalledTx2":1, "AdaptiveInputEqRecalledTx3":1, "AdaptiveInputEqRecalledTx4":1, + "AdaptiveInputEqRecalledTx5":1, "AdaptiveInputEqRecalledTx6":1, "AdaptiveInputEqRecalledTx7":1, "AdaptiveInputEqRecalledTx8":1 } + } + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.write = MagicMock() + mock_resp = [0x1] + self.api.xcvr_eeprom.read.side_effect = mock_resp + self.api.stage_custom_si_settings(0x0f, optics_si_dict) + assert self.api.xcvr_eeprom.write.call_count == 4 + + def test_set_module_si_eq_post_settings(self): + optics_si_dict = { "OutputEqPostCursorTargetRx":{ + "OutputEqPostCursorTargetRx1":2, "OutputEqPostCursorTargetRx2":2, "OutputEqPostCursorTargetRx3":2, "OutputEqPostCursorTargetRx4":2, + "OutputEqPostCursorTargetRx5":2, "OutputEqPostCursorTargetRx6":2, "OutputEqPostCursorTargetRx7":2, "OutputEqPostCursorTargetRx8":2 } + } + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.write = MagicMock() + mock_resp = [0x1, 0x7] + self.api.xcvr_eeprom.read.side_effect = mock_resp + self.api.stage_custom_si_settings(0x01, optics_si_dict) + assert self.api.xcvr_eeprom.write.call_count == 1 + + def test_set_module_si_fixed_en_settings(self): + optics_si_dict = { "FixedInputEqTargetTx":{ + "FixedInputEqTargetTx1":1, "FixedInputEqTargetTx2":1, "FixedInputEqTargetTx3":1, "FixedInputEqTargetTx4":1, + "FixedInputEqTargetTx5":1, "FixedInputEqTargetTx6":1, "FixedInputEqTargetTx7":1, "FixedInputEqTargetTx8":1 } + } + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.write = MagicMock() + mock_resp = [0x1, 0x1] + self.api.xcvr_eeprom.read.side_effect = mock_resp + self.api.stage_custom_si_settings(0xff, optics_si_dict) + assert self.api.xcvr_eeprom.write.call_count == 8 + + def test_set_module_cdr_enable_tx_settings(self): + optics_si_dict = { "CDREnableTx":{ + "CDREnableTx1":1, "CDREnableTx2":1, "CDREnableTx3":1, "CDREnableTx4":1, + "CDREnableTx5":0, "CDREnableTx6":0, "CDREnableTx7":0, "CDREnableTx8":0 } + } + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.write = MagicMock() + mock_resp = [0x1] + self.api.xcvr_eeprom.read.side_effect = mock_resp + self.api.stage_custom_si_settings(0x0f, optics_si_dict) + assert self.api.xcvr_eeprom.write.call_count == 4 + + def test_set_module_cdr_enable_rx_settings(self): + optics_si_dict = { "CDREnableRx":{ + "CDREnableRx1":1, "CDREnableRx2":1, "CDREnableRx3":1, "CDREnableRx4":1, + "CDREnableRx5":0, "CDREnableRx6":0, "CDREnableRx7":0, "CDREnableRx8":0 } + } + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.write = MagicMock() + mock_resp = [0x1] + self.api.xcvr_eeprom.read.side_effect = mock_resp + self.api.stage_custom_si_settings(0xff, optics_si_dict) + assert self.api.xcvr_eeprom.write.call_count == 8 + + def test_set_module_OutputAmplitudeTargetRx_settings(self): + optics_si_dict = { "OutputAmplitudeTargetRx":{ + "OutputAmplitudeTargetRx1":1, "OutputAmplitudeTargetRx2":1, "OutputAmplitudeTargetRx3":1, "OutputAmplitudeTargetRx4":1, + "OutputAmplitudeTargetRx5":1, "OutputAmplitudeTargetRx6":1, "OutputAmplitudeTargetRx7":1, "OutputAmplitudeTargetRx8":1 } + } + self.api.xcvr_eeprom.read = MagicMock() + self.api.xcvr_eeprom.write = MagicMock() + mock_resp = [0x1, 0x7] + self.api.xcvr_eeprom.read.side_effect = mock_resp + self.api.stage_custom_si_settings(0x0f, optics_si_dict) + assert self.api.xcvr_eeprom.write.call_count == 4 def test_get_error_description(self): self.api.get_module_state = MagicMock() @@ -2196,3 +2360,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"] diff --git a/tests/sonic_xcvr/test_sff8436.py b/tests/sonic_xcvr/test_sff8436.py index 053175efd..7b132df13 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,95 @@ 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 + + @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 7831fcf06..a1d8c1a2c 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() @@ -40,8 +41,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() @@ -205,3 +206,86 @@ 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 + + @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 a347e8fa2..7a566bc2d 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,95 @@ 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 + + @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_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="