From d98600e8ed8df0280cb07487132e20adc48d7f6e Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Tue, 31 Jul 2018 02:06:00 +0800 Subject: [PATCH] [sfputilbase]: enhance the sfputilbase for transceiver monitoring (#9) * [sfputilbase]: enhance the sfputilbase [List of changes] * add ability to only read and parse specific bytes from eeprom * add two API to read the transceiver info and dom info which intrested by SNMP * add a new API definiton: get_transceiver_change_event * fix a bug: wrong 'VendorOUI' offset for sff8472 modified: sff8436.py modified: sff8472.py modified: sfputilbase.py Signed-off-by Liu Kebo kebol@mellanox.com * fix for missing N/A * fix review comments * fix indent * fix file close bug * add tx_power support * fix typo * fix typo --- sonic_sfp/sff8436.py | 237 +++++++++++++++++++++++++++++- sonic_sfp/sff8472.py | 103 ++++++++++++- sonic_sfp/sfputilbase.py | 307 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 631 insertions(+), 16 deletions(-) diff --git a/sonic_sfp/sff8436.py b/sonic_sfp/sff8436.py index f707c3925b16..63ca3a4ace93 100644 --- a/sonic_sfp/sff8436.py +++ b/sonic_sfp/sff8436.py @@ -364,6 +364,61 @@ class sff8436InterfaceId(sffbase): 'type' : 'bitmap', 'decode': {}}} + sfp_type = { + 'type': + {'offset': 0, + 'size': 1, + 'type': 'enum', + 'decode': type_of_transceiver} + } + + vendor_name = { + 'Vendor Name': + {'offset': 0, + 'size': 16, + 'type': 'str'} + } + + vendor_pn = { + 'Vendor PN': + {'offset': 0, + 'size': 16, + 'type': 'str'} + } + + vendor_rev = { + 'Vendor Rev': + {'offset': 0, + 'size': 2, + 'type': 'str'} + } + + vendor_sn = { + 'Vendor SN': + {'offset': 0, + 'size': 16, + 'type': 'str'} + } + + qsfp_dom_capability = { + 'Tx_power_support': + {'offset': 0, + 'bit': 2, + 'type': 'bitvalue'}, + 'Rx_power_support': + {'offset': 0, + 'bit': 3, + 'type': 'bitvalue'}, + 'Voltage_support': + {'offset': 0, + 'bit': 4, + 'type': 'bitvalue'}, + 'Temp_support': + {'offset': 0, + 'bit': 5, + 'type': 'bitvalue'} + } + def __init__(self, eeprom_raw_data=None): self.interface_data = None @@ -378,6 +433,24 @@ def __init__(self, eeprom_raw_data=None): def parse(self, eeprom_raw_data, start_pos): return sffbase.parse(self, self.interface_id, eeprom_raw_data, start_pos) + def parse_sfp_type(self, type_raw_data, start_pos): + return sffbase.parse(self, self.sfp_type, type_raw_data, start_pos) + + def parse_vendor_name(self, name_raw_data, start_pos): + return sffbase.parse(self, self.vendor_name, name_raw_data, start_pos) + + def parse_vendor_rev(self, rev_raw_data, start_pos): + return sffbase.parse(self, self.vendor_rev, rev_raw_data, start_pos) + + def parse_vendor_pn(self, pn_raw_data, start_pos): + return sffbase.parse(self, self.vendor_pn, pn_raw_data, start_pos) + + def parse_vendor_sn(self, sn_raw_data, start_pos): + return sffbase.parse(self, self.vendor_sn, sn_raw_data, start_pos) + + def parse_qsfp_dom_capability(self, sn_raw_data, start_pos): + return sffbase.parse(self, self.qsfp_dom_capability, sn_raw_data, start_pos) + def dump_pretty(self): if self.interface_data == None: print('Object not initialized, nothing to print') @@ -640,7 +713,6 @@ def calc_rx_power(self, eeprom_data, offset, size): return retval - dom_status_indicator = {'DataNotReady': {'offset': 2, 'bit': 0, @@ -857,7 +929,6 @@ def calc_rx_power(self, eeprom_data, offset, size): 'bit': 0, 'type': 'bitvalue'}} - dom_module_monitor_values = {'Temperature': {'offset':22, 'size':2, @@ -923,6 +994,148 @@ def calc_rx_power(self, eeprom_data, offset, size): 'type': 'nested', 'decode': dom_channel_monitor_values}} +# new added parser for some specific values interested by SNMP +# TO DO: find a way to reuse the definitions in above code, need refactor + revision_compliance = { + '00': 'Revision not specified', + '01': 'SFF-8436 Rev 4.8', + '02': 'SFF-8436 Rev 4.8 with extra bytes support', + '03': 'SFF-8636 Rev 1.3', + '04': 'SFF-8636 Rev 1.4', + '05': 'SFF-8636 Rev 1.5', + '06': 'SFF-8636 Rev 2.0', + '07': 'SFF-8636 Rev 2.5' + } + + sfp_dom_rev = { + 'dom_rev': + {'offset': 0, + 'size': 1, + 'type': 'enum', + 'decode': revision_compliance} + } + + dom_module_temperature = { + 'Temperature': + {'offset': 0, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_temperature}} + } + + dom_module_voltage = { + 'Vcc': + {'offset': 0, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_voltage}} + } + + dom_channel_monitor_params = { + 'RX1Power': + {'offset': 0, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}}, + 'RX2Power': + {'offset': 2, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}}, + 'RX3Power': + {'offset': 4, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}}, + 'RX4Power': + {'offset': 6, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}}, + 'TX1Bias': + {'offset': 8, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}}, + 'TX2Bias': + {'offset': 10, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}}, + 'TX3Bias': + {'offset': 12, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}}, + 'TX4Bias': + {'offset': 14, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}} + } + + dom_channel_monitor_params_with_tx_power = { + 'RX1Power': + {'offset': 0, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}}, + 'RX2Power': + {'offset': 2, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}}, + 'RX3Power': + {'offset': 4, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}}, + 'RX4Power': + {'offset': 6, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}}, + 'TX1Bias': + {'offset': 8, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}}, + 'TX2Bias': + {'offset': 10, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}}, + 'TX3Bias': + {'offset': 12, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}}, + 'TX4Bias': + {'offset': 14, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}}, + 'TX1Power': + {'offset': 0, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_tx_power}}, + 'TX2Power': + {'offset': 2, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_tx_power}}, + 'TX3Power': + {'offset': 4, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_tx_power}}, + 'TX4Power': + {'offset': 6, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_tx_power}} + } def __init__(self, eeprom_raw_data=None, calibration_type=1): self._calibration_type = calibration_type @@ -936,6 +1149,26 @@ def parse(self, eeprom_raw_data, start_pos): return sffbase.parse(self, self.dom_map, eeprom_raw_data, start_pos) +# Parser functions for specific values interested by SNMP + def parse_sfp_dom_rev(self, type_raw_data, start_pos): + return sffbase.parse(self, self.sfp_dom_rev, type_raw_data, start_pos) + + def parse_temperature(self, eeprom_raw_data, start_pos): + return sffbase.parse(self, self.dom_module_temperature, eeprom_raw_data, + start_pos) + + def parse_voltage(self, eeprom_raw_data, start_pos): + return sffbase.parse(self, self.dom_module_voltage, eeprom_raw_data, + start_pos) + + def parse_channel_monitor_params(self, eeprom_raw_data, start_pos): + return sffbase.parse(self, self.dom_channel_monitor_params, eeprom_raw_data, + start_pos) + + def parse_channel_monitor_params_with_tx_power(self, eeprom_raw_data, start_pos): + return sffbase.parse(self, self.dom_channel_monitor_params_with_tx_power, eeprom_raw_data, + start_pos) + def dump_pretty(self): if self.dom_data == None: print('Object not initialized, nothing to print') diff --git a/sonic_sfp/sff8472.py b/sonic_sfp/sff8472.py index b503e9ccf50b..0831dc82fe9c 100644 --- a/sonic_sfp/sff8472.py +++ b/sonic_sfp/sff8472.py @@ -345,7 +345,7 @@ class sff8472InterfaceId(sffbase): 'size' : 16, 'type' : 'str'}, 'VendorOUI': - {'offset':20, + {'offset':37, 'size':3, 'type' : 'str'}, 'VendorPN': @@ -425,6 +425,43 @@ class sff8472InterfaceId(sffbase): 'size':8, 'type': 'date'}} +# Parser for specific values that interested by SNMP + sfp_type = { + 'type': + {'offset': 0, + 'size': 1, + 'type': 'enum', + 'decode': type_of_transceiver} + } + + vendor_name = { + 'Vendor Name': + {'offset': 0, + 'size': 16, + 'type': 'str'} + } + + vendor_pn = { + 'Vendor PN': + {'offset': 0, + 'size': 16, + 'type': 'str'} + } + + vendor_rev = { + 'Vendor Rev': + {'offset': 0, + 'size': 4, + 'type': 'str'} + } + + vendor_sn = { + 'Vendor SN': + {'offset': 0, + 'size': 16, + 'type': 'str'} + } + # Returns calibration type def _get_calibration_type(self, eeprom_data): try: @@ -452,6 +489,22 @@ def __init__(self, eeprom_raw_data=None): def parse(self, eeprom_raw_data, start_pos): return sffbase.parse(self, self.interface_id, eeprom_raw_data, start_pos) +#new parser functions for specific values that interested by SNMP + def parse_sfp_type(self, type_raw_data, start_pos): + return sffbase.parse(self, self.sfp_type, type_raw_data, start_pos) + + def parse_vendor_name(self, name_raw_data, start_pos): + return sffbase.parse(self, self.vendor_name, name_raw_data, start_pos) + + def parse_vendor_rev(self, rev_raw_data, start_pos): + return sffbase.parse(self, self.vendor_rev, rev_raw_data, start_pos) + + def parse_vendor_pn(self, pn_raw_data, start_pos): + return sffbase.parse(self, self.vendor_pn, pn_raw_data, start_pos) + + def parse_vendor_sn(self, sn_raw_data, start_pos): + return sffbase.parse(self, self.vendor_sn, sn_raw_data, start_pos) + def dump_pretty(self): if self.interface_data == None: print('Object not initialized, nothing to print') @@ -467,7 +520,6 @@ def get_data(self): def get_data_pretty(self): return sffbase.get_data_pretty(self, self.interface_data) - class sff8472Dom(sffbase): """Parser and interpretor for Diagnostics data fields at address A2h""" @@ -684,7 +736,6 @@ def calc_tx_power(self, eeprom_data, offset, size): return retval - def calc_rx_power(self, eeprom_data, offset, size): try: cal_type = self.get_calibration_type() @@ -1025,6 +1076,40 @@ def calc_rx_power(self, eeprom_data, offset, size): 'type' : 'nested', 'decode':dom_warning_flags}} +# parsers for specific values that interested by SNMP + dom_module_temperature = { + 'Temperature': + {'offset': 0, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_temperature}} + } + + dom_module_voltage = { + 'Vcc': + {'offset': 0, + 'size': 2, + 'type': 'func', + 'decode': {'func':calc_voltage}} + } + + dom_channel_monitor_params = { + 'TXBias': + {'offset': 0, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_bias}}, + 'TXPower': + {'offset': 2, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_tx_power}}, + 'RXPower': + {'offset': 4, + 'size': 2, + 'type': 'func', + 'decode': {'func': calc_rx_power}} + } def __init__(self, eeprom_raw_data=None, calibration_type=0): self._calibration_type = calibration_type @@ -1037,6 +1122,18 @@ def __init__(self, eeprom_raw_data=None, calibration_type=0): def parse(self, eeprom_raw_data, start_pos): return sffbase.parse(self, self.dom_map, eeprom_raw_data, start_pos) +# parser functions for specific values interested by SNMP + def parse_temperature(self, eeprom_raw_data, start_pos): + return sffbase.parse(self, self.dom_module_temperature, eeprom_raw_data, + start_pos) + + def parse_voltage(self, eeprom_raw_data, start_pos): + return sffbase.parse(self, self.dom_module_voltage, eeprom_raw_data, + start_pos) + + def parse_channel_monitor_params(self, eeprom_raw_data, start_pos): + return sffbase.parse(self, self.dom_channel_monitor_params, eeprom_raw_data, + start_pos) def dump_pretty(self): if self.dom_data == None: diff --git a/sonic_sfp/sfputilbase.py b/sonic_sfp/sfputilbase.py index cea924906e0b..701b345389ff 100644 --- a/sonic_sfp/sfputilbase.py +++ b/sonic_sfp/sfputilbase.py @@ -19,6 +19,38 @@ except ImportError as e: raise ImportError("%s - required module not found" % str(e)) +# definitions of the offset and width for values in XCVR info eeprom +XCVR_TYPE_OFFSET = 0 +XCVR_TYPE_WIDTH = 1 +XCVR_VENDOR_NAME_OFFSET = 20 +XCVR_VENDOR_NAME_WIDTH = 16 +XCVR_VENDOR_PN_OFFSET = 40 +XCVR_VENDOR_PN_WIDTH = 16 +XCVR_HW_REV_OFFSET = 56 +XCVR_HW_REV_WIDTH_QSFP = 2 +XCVR_HW_REV_WIDTH_SFP = 4 +XCVR_VENDOR_SN_OFFSET = 68 +XCVR_VENDOR_SN_WIDTH = 16 +XCVR_DOM_CAPABILITY_OFFSET = 92 +XCVR_DOM_CAPABILITY_WIDTH = 1 + +#definitions of the offset and width for values in DOM info eeprom +QSFP_DOM_REV_OFFSET = 1 +QSFP_DOM_REV_WIDTH = 1 +QSFP_TEMPE_OFFSET = 22 +QSFP_TEMPE_WIDTH = 2 +QSFP_VLOT_OFFSET = 26 +QSFP_VOLT_WIDTH = 2 +QSFP_CHANNL_MON_OFFSET = 34 +QSFP_CHANNL_MON_WIDTH = 16 +QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH = 24 + +SFP_TEMPE_OFFSET = 96 +SFP_TEMPE_WIDTH = 2 +SFP_VLOT_OFFSET = 98 +SFP_VOLT_WIDTH = 2 +SFP_CHANNL_MON_OFFSET = 100 +SFP_CHANNL_MON_WIDTH = 6 class SfpUtilError(Exception): """Base class for exceptions in this module.""" @@ -176,14 +208,8 @@ def _sfp_eeprom_present(self, sysfs_sfp_i2c_client_eeprompath, offset): else: return True - # Read eeprom - def _read_eeprom_devid(self, port_num, devid, offset): + def _get_port_eeprom_path(self, port_num, devid): sysfs_i2c_adapter_base_path = "/sys/class/i2c-adapter" - eeprom_raw = [] - num_bytes = 256 - - for i in range(0, num_bytes): - eeprom_raw.append("0x00") if port_num in self.port_to_eeprom_mapping.keys(): sysfs_sfp_i2c_client_eeprom_path = self.port_to_eeprom_mapping[port_num] @@ -218,11 +244,15 @@ def _read_eeprom_devid(self, port_num, devid, offset): sysfs_sfp_i2c_client_eeprom_path = "%s/eeprom" % sysfs_sfp_i2c_client_path - if not self._sfp_eeprom_present(sysfs_sfp_i2c_client_eeprom_path, offset): - return None + return sysfs_sfp_i2c_client_eeprom_path + + # Read out any bytes from any offset + def _read_eeprom_specific_bytes(self, sysfsfile_eeprom, offset, num_bytes): + eeprom_raw = [] + for i in range(0, num_bytes): + eeprom_raw.append("0x00") try: - sysfsfile_eeprom = open(sysfs_sfp_i2c_client_eeprom_path, "rb") sysfsfile_eeprom.seek(offset) raw = sysfsfile_eeprom.read(num_bytes) except IOError: @@ -235,10 +265,27 @@ def _read_eeprom_devid(self, port_num, devid, offset): except: return None + return eeprom_raw + + # Read eeprom + def _read_eeprom_devid(self, port_num, devid, offset, num_bytes = 256): + sysfs_sfp_i2c_client_eeprom_path = self._get_port_eeprom_path(port_num, devid) + + if not self._sfp_eeprom_present(sysfs_sfp_i2c_client_eeprom_path, offset): + return None + + try: + sysfsfile_eeprom = open(sysfs_sfp_i2c_client_eeprom_path, "rb") + except IOError: + print("Error: reading sysfs file %s" % sysfs_sfp_i2c_client_eeprom_path) + return None + + eeprom_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, offset, num_bytes) + try: sysfsfile_eeprom.close() except: - return 0 + return None return eeprom_raw @@ -582,6 +629,234 @@ def get_eeprom_dict(self, port_num): return sfp_data + # Read out SFP type, vendor name, PN, REV, SN from eeprom. + def get_transceiver_info_dict(self, port_num): + transceiver_info_dict = {} + + if port_num in self.qsfp_ports: + offset = 128 + vendor_rev_width = XCVR_HW_REV_WIDTH_QSFP + else: + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_SFP + + file_path = self._get_port_eeprom_path(port_num, self.IDENTITY_EEPROM_ADDR) + if not self._sfp_eeprom_present(file_path, 0): + print("Error, file not exist %s" % file_path) + return None + + try: + sysfsfile_eeprom = open(file_path, "rb") + except IOError: + print("Error: reading sysfs file %s" % file_path) + return None + + sfpi_obj = sff8436InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return None + + sfp_type_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + XCVR_TYPE_OFFSET), XCVR_TYPE_WIDTH) + if sfp_type_raw is not None: + sfp_type_data = sfpi_obj.parse_sfp_type(sfp_type_raw, 0) + else: + return None + + sfp_vendor_name_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + XCVR_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + if sfp_vendor_name_raw is not None: + sfp_vendor_name_data = sfpi_obj.parse_vendor_name(sfp_vendor_name_raw, 0) + else: + return None + + sfp_vendor_pn_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + XCVR_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + if sfp_vendor_pn_raw is not None: + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn(sfp_vendor_pn_raw, 0) + else: + return None + + sfp_vendor_rev_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + XCVR_HW_REV_OFFSET), vendor_rev_width) + if sfp_vendor_rev_raw is not None: + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev(sfp_vendor_rev_raw, 0) + else: + return None + + sfp_vendor_sn_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + XCVR_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + if sfp_vendor_sn_raw is not None: + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn(sfp_vendor_sn_raw, 0) + else: + return None + + try: + sysfsfile_eeprom.close() + except IOError: + print("Error: closing sysfs file %s" % file_path) + return None + + transceiver_info_dict['type'] = sfp_type_data['data']['type']['value'] + transceiver_info_dict['manufacturename'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['modelname'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardwarerev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serialnum'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + + return transceiver_info_dict + + def get_transceiver_dom_info_dict(self, port_num): + transceiver_dom_info_dict = {} + + if port_num in self.qsfp_ports: + offset = 0 + offset_xcvr = 128 + file_path = self._get_port_eeprom_path(port_num, self.IDENTITY_EEPROM_ADDR) + if not self._sfp_eeprom_present(file_path, 0): + return None + + try: + sysfsfile_eeprom = open(file_path, "rb") + except IOError: + print("Error: reading sysfs file %s" % file_path) + return None + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + + sfpi_obj = sff8436InterfaceId() + if sfpi_obj is None: + return None + + # QSFP capability byte parse, through this byte can know whether it support tx_power or not. + # TODO: in the future when decided to migrate to support SFF-8636 instead of SFF-8436, + # need to add more code for determining the capability and version compliance + # in SFF-8636 dom capability definitions evolving with the versions. + qsfp_dom_capability_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset_xcvr + XCVR_DOM_CAPABILITY_OFFSET), XCVR_DOM_CAPABILITY_WIDTH) + if qsfp_dom_capability_raw is not None: + qspf_dom_capability_data = sfpi_obj.parse_qsfp_dom_capability(qsfp_dom_capability_raw, 0) + else: + return None + + dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0) + else: + return None + + dom_voltage_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_VLOT_OFFSET), QSFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + else: + return None + + qsfp_dom_rev_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_DOM_REV_OFFSET), QSFP_DOM_REV_WIDTH) + if qsfp_dom_rev_raw is not None: + qsfp_dom_rev_data = sfpd_obj.parse_sfp_dom_rev(qsfp_dom_rev_raw, 0) + else: + return None + + transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + + # The tx_power monitoring is only available on QSFP which compliant with SFF-8636 + # and claimed that it support tx_power with one indicator bit. + dom_channel_monitor_data = {} + qsfp_dom_rev = qsfp_dom_rev_data['data']['dom_rev']['value'] + qsfp_tx_power_support = qspf_dom_capability_data['data']['Tx_power_support']['value'] + if (qsfp_dom_rev[0:8] != 'SFF-8636' or (qsfp_dom_rev[0:8] == 'SFF-8636' and qsfp_tx_power_support != 'on')): + dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0) + else: + return None + + transceiver_dom_info_dict['tx1power'] = 'N/A' + transceiver_dom_info_dict['tx2power'] = 'N/A' + transceiver_dom_info_dict['tx3power'] = 'N/A' + transceiver_dom_info_dict['tx4power'] = 'N/A' + else: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_channel_monitor_raw, 0) + else: + return None + + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TX1Power']['value'] + transceiver_dom_info_dict['tx2power'] = dom_channel_monitor_data['data']['TX2Power']['value'] + transceiver_dom_info_dict['tx3power'] = dom_channel_monitor_data['data']['TX3Power']['value'] + transceiver_dom_info_dict['tx4power'] = dom_channel_monitor_data['data']['TX4Power']['value'] + + try: + sysfsfile_eeprom.close() + except IOError: + print("Error: closing sysfs file %s" % file_path) + return None + + transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RX1Power']['value'] + transceiver_dom_info_dict['rx2power'] = dom_channel_monitor_data['data']['RX2Power']['value'] + transceiver_dom_info_dict['rx3power'] = dom_channel_monitor_data['data']['RX3Power']['value'] + transceiver_dom_info_dict['rx4power'] = dom_channel_monitor_data['data']['RX4Power']['value'] + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TX1Bias']['value'] + transceiver_dom_info_dict['tx2bias'] = dom_channel_monitor_data['data']['TX2Bias']['value'] + transceiver_dom_info_dict['tx3bias'] = dom_channel_monitor_data['data']['TX3Bias']['value'] + transceiver_dom_info_dict['tx4bias'] = dom_channel_monitor_data['data']['TX4Bias']['value'] + + else: + offset = 256 + file_path = self._get_port_eeprom_path(port_num, self.DOM_EEPROM_ADDR) + if not self._sfp_eeprom_present(file_path, 0): + return None + + try: + sysfsfile_eeprom = open(file_path, "rb") + except IOError: + print("Error: reading sysfs file %s" % file_path) + return None + + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return None + + dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_TEMPE_OFFSET), SFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0) + else: + return None + + dom_voltage_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_VLOT_OFFSET), SFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + else: + return None + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0) + else: + return None + + try: + sysfsfile_eeprom.close() + except IOError: + print("Error: closing sysfs file %s" % file_path) + return None + + transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value'] + transceiver_dom_info_dict['rx2power'] = 'N/A' + transceiver_dom_info_dict['rx3power'] = 'N/A' + transceiver_dom_info_dict['rx4power'] = 'N/A' + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value'] + transceiver_dom_info_dict['tx2bias'] = 'N/A' + transceiver_dom_info_dict['tx3bias'] = 'N/A' + transceiver_dom_info_dict['tx4bias'] = 'N/A' + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TXPower']['value'] + transceiver_dom_info_dict['tx2power'] = 'N/A' + transceiver_dom_info_dict['tx3power'] = 'N/A' + transceiver_dom_info_dict['tx4power'] = 'N/A' + + return transceiver_dom_info_dict + @abc.abstractmethod def get_presence(self, port_num): """ @@ -614,3 +889,13 @@ def reset(self, port_num): :returns: Boolean, True if reset successful, False if not """ return + + @abc.abstractmethod + def get_transceiver_change_event(self, timeout=0): + """ + :param timeout + :returns: Boolean, True if call successful, False if not; + dict for pysical interface number and the SFP status, + status='1' represent plug in, '0' represent plug out like {'0': '1', '31':'0'} + """ + return