[Mellanox] optimize new platform api (#3289)
optimize SFP module operations and fix issues. - split initialization of variant categories of devices and initialize each category of devices only when needed, so that unnecessary dependencies can be avoided. - update watchdog logic, only initializing watchdog when referenced. - support platform.py and enable to initialize variant devices on a host/docker basis - update init so that sonic_platform can be imported as a whole.
This commit is contained in:
parent
4c36866a26
commit
5c2d71138b
@ -0,0 +1,2 @@
|
|||||||
|
__all__ = ["platform", "chassis"]
|
||||||
|
from sonic_platform import *
|
@ -10,15 +10,7 @@
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from sonic_platform_base.chassis_base import ChassisBase
|
from sonic_platform_base.chassis_base import ChassisBase
|
||||||
from sonic_platform.psu import Psu
|
|
||||||
from sonic_platform.fan import Fan
|
|
||||||
from sonic_platform.fan import FAN_PATH
|
|
||||||
from sonic_platform.sfp import SFP
|
|
||||||
from sonic_platform.thermal import Thermal, initialize_thermals
|
|
||||||
from sonic_platform.watchdog import get_watchdog
|
|
||||||
from sonic_daemon_base.daemon_base import Logger
|
from sonic_daemon_base.daemon_base import Logger
|
||||||
from eeprom import Eeprom
|
|
||||||
from sfp_event import sfp_event
|
|
||||||
from os import listdir
|
from os import listdir
|
||||||
from os.path import isfile, join
|
from os.path import isfile, join
|
||||||
import sys
|
import sys
|
||||||
@ -43,11 +35,6 @@ HWMGMT_SYSTEM_ROOT = '/var/run/hw-management/system/'
|
|||||||
#reboot cause related definitions
|
#reboot cause related definitions
|
||||||
REBOOT_CAUSE_ROOT = HWMGMT_SYSTEM_ROOT
|
REBOOT_CAUSE_ROOT = HWMGMT_SYSTEM_ROOT
|
||||||
|
|
||||||
REBOOT_CAUSE_POWER_LOSS_FILE = 'reset_main_pwr_fail'
|
|
||||||
REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC_FILE = 'reset_asic_thermal'
|
|
||||||
REBOOT_CAUSE_WATCHDOG_FILE = 'reset_hotswap_or_wd'
|
|
||||||
REBOOT_CAUSE_MLNX_FIRMWARE_RESET = 'reset_fw_reset'
|
|
||||||
|
|
||||||
REBOOT_CAUSE_FILE_LENGTH = 1
|
REBOOT_CAUSE_FILE_LENGTH = 1
|
||||||
|
|
||||||
#version retrieving related definitions
|
#version retrieving related definitions
|
||||||
@ -84,14 +71,30 @@ class Chassis(ChassisBase):
|
|||||||
# Initialize SKU name
|
# Initialize SKU name
|
||||||
self.sku_name = self._get_sku_name()
|
self.sku_name = self._get_sku_name()
|
||||||
|
|
||||||
|
# move the initialization of each components to their dedicated initializer
|
||||||
|
# which will be called from platform
|
||||||
|
self.sfp_module_initialized = False
|
||||||
|
self.sfp_event_initialized = False
|
||||||
|
self.reboot_cause_initialized = False
|
||||||
|
logger.log_info("Chassis loaded successfully")
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.sfp_event_initialized:
|
||||||
|
self.sfp_event.deinitialize()
|
||||||
|
|
||||||
|
def initialize_psu(self):
|
||||||
|
from sonic_platform.psu import Psu
|
||||||
# Initialize PSU list
|
# Initialize PSU list
|
||||||
|
self.psu_module = Psu
|
||||||
for index in range(MLNX_NUM_PSU):
|
for index in range(MLNX_NUM_PSU):
|
||||||
psu = Psu(index, self.sku_name)
|
psu = Psu(index, self.sku_name)
|
||||||
self._psu_list.append(psu)
|
self._psu_list.append(psu)
|
||||||
|
|
||||||
# Initialize watchdog
|
def initialize_fan(self):
|
||||||
self._watchdog = get_watchdog()
|
from sonic_platform.fan import Fan
|
||||||
|
from sonic_platform.fan import FAN_PATH
|
||||||
|
self.fan_module = Fan
|
||||||
|
self.fan_path = FAN_PATH
|
||||||
# Initialize FAN list
|
# Initialize FAN list
|
||||||
multi_rotor_in_drawer = False
|
multi_rotor_in_drawer = False
|
||||||
num_of_fan, num_of_drawer = self._extract_num_of_fans_and_fan_drawers()
|
num_of_fan, num_of_drawer = self._extract_num_of_fans_and_fan_drawers()
|
||||||
@ -104,6 +107,11 @@ class Chassis(ChassisBase):
|
|||||||
fan = Fan(index, index)
|
fan = Fan(index, index)
|
||||||
self._fan_list.append(fan)
|
self._fan_list.append(fan)
|
||||||
|
|
||||||
|
def initialize_sfp(self):
|
||||||
|
from sonic_platform.sfp import SFP
|
||||||
|
|
||||||
|
self.sfp_module = SFP
|
||||||
|
|
||||||
# Initialize SFP list
|
# Initialize SFP list
|
||||||
port_position_tuple = self._get_port_position_tuple_by_sku_name()
|
port_position_tuple = self._get_port_position_tuple_by_sku_name()
|
||||||
self.PORT_START = port_position_tuple[0]
|
self.PORT_START = port_position_tuple[0]
|
||||||
@ -118,31 +126,82 @@ class Chassis(ChassisBase):
|
|||||||
sfp_module = SFP(index, 'SFP')
|
sfp_module = SFP(index, 'SFP')
|
||||||
self._sfp_list.append(sfp_module)
|
self._sfp_list.append(sfp_module)
|
||||||
|
|
||||||
|
self.sfp_module_initialized = True
|
||||||
|
|
||||||
|
def initialize_thermals(self):
|
||||||
|
from sonic_platform.thermal import initialize_thermals
|
||||||
# Initialize thermals
|
# Initialize thermals
|
||||||
initialize_thermals(self.sku_name, self._thermal_list, self._psu_list)
|
initialize_thermals(self.sku_name, self._thermal_list, self._psu_list)
|
||||||
|
|
||||||
|
def initialize_eeprom(self):
|
||||||
|
from eeprom import Eeprom
|
||||||
# Initialize EEPROM
|
# Initialize EEPROM
|
||||||
self.eeprom = Eeprom()
|
self._eeprom = Eeprom()
|
||||||
|
|
||||||
|
def initialize_components_list(self):
|
||||||
# Initialize component list
|
# Initialize component list
|
||||||
self._component_name_list.append(COMPONENT_BIOS)
|
self._component_name_list.append(COMPONENT_BIOS)
|
||||||
self._component_name_list.append(COMPONENT_FIRMWARE)
|
self._component_name_list.append(COMPONENT_FIRMWARE)
|
||||||
self._component_name_list.append(COMPONENT_CPLD1)
|
self._component_name_list.append(COMPONENT_CPLD1)
|
||||||
self._component_name_list.append(COMPONENT_CPLD2)
|
self._component_name_list.append(COMPONENT_CPLD2)
|
||||||
|
|
||||||
# Initialize sfp-change-listening stuff
|
##############################################
|
||||||
self._init_sfp_change_event()
|
# SFP methods
|
||||||
|
##############################################
|
||||||
|
def get_num_sfps(self):
|
||||||
|
"""
|
||||||
|
Retrieves the number of sfps available on this chassis
|
||||||
|
|
||||||
def _init_sfp_change_event(self):
|
Returns:
|
||||||
self.sfp_event = sfp_event()
|
An integer, the number of sfps available on this chassis
|
||||||
self.sfp_event.initialize()
|
"""
|
||||||
self.MAX_SELECT_EVENT_RETURNED = self.PORT_END
|
if not self.sfp_module_initialized:
|
||||||
|
self.initialize_sfp()
|
||||||
|
return len(self._sfp_list)
|
||||||
|
|
||||||
|
def get_all_sfps(self):
|
||||||
|
"""
|
||||||
|
Retrieves all sfps available on this chassis
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of objects derived from SfpBase representing all sfps
|
||||||
|
available on this chassis
|
||||||
|
"""
|
||||||
|
if not self.sfp_module_initialized:
|
||||||
|
self.initialize_sfp()
|
||||||
|
return self._sfp_list
|
||||||
|
|
||||||
|
def get_sfp(self, index):
|
||||||
|
"""
|
||||||
|
Retrieves sfp represented by (0-based) index <index>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index: An integer, the index (0-based) of the sfp to retrieve.
|
||||||
|
The index should be the sequence of a physical port in a chassis,
|
||||||
|
starting from 0.
|
||||||
|
For example, 0 for Ethernet0, 1 for Ethernet4 and so on.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An object dervied from SfpBase representing the specified sfp
|
||||||
|
"""
|
||||||
|
if not self.sfp_module_initialized:
|
||||||
|
self.initialize_sfp()
|
||||||
|
|
||||||
|
sfp = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
sfp = self._sfp_list[index]
|
||||||
|
except IndexError:
|
||||||
|
sys.stderr.write("SFP index {} out of range (0-{})\n".format(
|
||||||
|
index, len(self._sfp_list)-1))
|
||||||
|
|
||||||
|
return sfp
|
||||||
|
|
||||||
def _extract_num_of_fans_and_fan_drawers(self):
|
def _extract_num_of_fans_and_fan_drawers(self):
|
||||||
num_of_fan = 0
|
num_of_fan = 0
|
||||||
num_of_drawer = 0
|
num_of_drawer = 0
|
||||||
for f in listdir(FAN_PATH):
|
for f in listdir(self.fan_path):
|
||||||
if isfile(join(FAN_PATH, f)):
|
if isfile(join(self.fan_path, f)):
|
||||||
match_obj = re.match('fan(\d+)_speed_get', f)
|
match_obj = re.match('fan(\d+)_speed_get', f)
|
||||||
if match_obj != None:
|
if match_obj != None:
|
||||||
if int(match_obj.group(1)) > num_of_fan:
|
if int(match_obj.group(1)) > num_of_fan:
|
||||||
@ -163,6 +222,30 @@ class Chassis(ChassisBase):
|
|||||||
position_tuple = port_position_tuple_list[hwsku_dict_port[self.sku_name]]
|
position_tuple = port_position_tuple_list[hwsku_dict_port[self.sku_name]]
|
||||||
return position_tuple
|
return position_tuple
|
||||||
|
|
||||||
|
def get_watchdog(self):
|
||||||
|
"""
|
||||||
|
Retrieves hardware watchdog device on this chassis
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An object derived from WatchdogBase representing the hardware
|
||||||
|
watchdog device
|
||||||
|
|
||||||
|
Note:
|
||||||
|
We overload this method to ensure that watchdog is only initialized
|
||||||
|
when it is referenced. Currently, only one daemon can open the watchdog.
|
||||||
|
To initialize watchdog in the constructor causes multiple daemon
|
||||||
|
try opening watchdog when loading and constructing a chassis object
|
||||||
|
and fail. By doing so we can eliminate that risk.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if self._watchdog is None:
|
||||||
|
from sonic_platform.watchdog import get_watchdog
|
||||||
|
self._watchdog = get_watchdog()
|
||||||
|
except Exception as e:
|
||||||
|
logger.log_info("Fail to load watchdog due to {}".format(repr(e)))
|
||||||
|
|
||||||
|
return self._watchdog
|
||||||
|
|
||||||
def get_base_mac(self):
|
def get_base_mac(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the base MAC address for the chassis
|
Retrieves the base MAC address for the chassis
|
||||||
@ -171,7 +254,7 @@ class Chassis(ChassisBase):
|
|||||||
A string containing the MAC address in the format
|
A string containing the MAC address in the format
|
||||||
'XX:XX:XX:XX:XX:XX'
|
'XX:XX:XX:XX:XX:XX'
|
||||||
"""
|
"""
|
||||||
return self.eeprom.get_base_mac()
|
return self._eeprom.get_base_mac()
|
||||||
|
|
||||||
def get_serial_number(self):
|
def get_serial_number(self):
|
||||||
"""
|
"""
|
||||||
@ -180,7 +263,7 @@ class Chassis(ChassisBase):
|
|||||||
Returns:
|
Returns:
|
||||||
A string containing the hardware serial number for this chassis.
|
A string containing the hardware serial number for this chassis.
|
||||||
"""
|
"""
|
||||||
return self.eeprom.get_serial_number()
|
return self._eeprom.get_serial_number()
|
||||||
|
|
||||||
def get_system_eeprom_info(self):
|
def get_system_eeprom_info(self):
|
||||||
"""
|
"""
|
||||||
@ -191,7 +274,7 @@ class Chassis(ChassisBase):
|
|||||||
OCP ONIE TlvInfo EEPROM format and values are their corresponding
|
OCP ONIE TlvInfo EEPROM format and values are their corresponding
|
||||||
values.
|
values.
|
||||||
"""
|
"""
|
||||||
return self.eeprom.get_system_eeprom_info()
|
return self._eeprom.get_system_eeprom_info()
|
||||||
|
|
||||||
def _read_generic_file(self, filename, len):
|
def _read_generic_file(self, filename, len):
|
||||||
"""
|
"""
|
||||||
@ -205,7 +288,7 @@ class Chassis(ChassisBase):
|
|||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.log_info("Fail to read file {} due to {}".format(filename, repr(e)))
|
logger.log_info("Fail to read file {} due to {}".format(filename, repr(e)))
|
||||||
return ''
|
return '0'
|
||||||
|
|
||||||
def _verify_reboot_cause(self, filename):
|
def _verify_reboot_cause(self, filename):
|
||||||
'''
|
'''
|
||||||
@ -215,6 +298,31 @@ class Chassis(ChassisBase):
|
|||||||
'''
|
'''
|
||||||
return bool(int(self._read_generic_file(join(REBOOT_CAUSE_ROOT, filename), REBOOT_CAUSE_FILE_LENGTH).rstrip('\n')))
|
return bool(int(self._read_generic_file(join(REBOOT_CAUSE_ROOT, filename), REBOOT_CAUSE_FILE_LENGTH).rstrip('\n')))
|
||||||
|
|
||||||
|
def initialize_reboot_cause(self):
|
||||||
|
self.reboot_major_cause_dict = {
|
||||||
|
'reset_main_pwr_fail' : self.REBOOT_CAUSE_POWER_LOSS,
|
||||||
|
'reset_aux_pwr_or_ref' : self.REBOOT_CAUSE_POWER_LOSS,
|
||||||
|
'reset_asic_thermal' : self.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC,
|
||||||
|
'reset_hotswap_or_wd' : self.REBOOT_CAUSE_WATCHDOG,
|
||||||
|
'reset_swb_wd' : self.REBOOT_CAUSE_WATCHDOG,
|
||||||
|
'reset_sff_wd' : self.REBOOT_CAUSE_WATCHDOG
|
||||||
|
}
|
||||||
|
self.reboot_minor_cause_dict = {
|
||||||
|
'reset_fw_reset' : "Reset by ASIC firmware",
|
||||||
|
'reset_long_pb' : "Reset by long press on power button",
|
||||||
|
'reset_short_pb' : "Reset by short press on power button",
|
||||||
|
'reset_comex_thermal' : "ComEx thermal shutdown",
|
||||||
|
'reset_comex_pwr_fail' : "ComEx power fail",
|
||||||
|
'reset_comex_wd' : "Reset requested from ComEx",
|
||||||
|
'reset_from_asic' : "Reset requested from ASIC",
|
||||||
|
'reset_reload_bios' : "Reset caused by BIOS reload",
|
||||||
|
'reset_sw_reset' : "Software reset",
|
||||||
|
'reset_hotswap_or_halt' : "Reset caused by hotswap or halt",
|
||||||
|
'reset_from_comex' : "Reset from ComEx",
|
||||||
|
'reset_voltmon_upgrade_fail': "Reset due to voltage monitor devices upgrade failure"
|
||||||
|
}
|
||||||
|
self.reboot_cause_initialized = True
|
||||||
|
|
||||||
def get_reboot_cause(self):
|
def get_reboot_cause(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the cause of the previous reboot
|
Retrieves the cause of the previous reboot
|
||||||
@ -227,21 +335,18 @@ class Chassis(ChassisBase):
|
|||||||
to pass a description of the reboot cause.
|
to pass a description of the reboot cause.
|
||||||
"""
|
"""
|
||||||
#read reboot causes files in the following order
|
#read reboot causes files in the following order
|
||||||
minor_cause = ''
|
if not self.reboot_cause_initialized:
|
||||||
if self._verify_reboot_cause(REBOOT_CAUSE_POWER_LOSS_FILE):
|
self.initialize_reboot_cause()
|
||||||
major_cause = self.REBOOT_CAUSE_POWER_LOSS
|
|
||||||
elif self._verify_reboot_cause(REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC_FILE):
|
|
||||||
major_cause = self.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC
|
|
||||||
elif self._verify_reboot_cause(REBOOT_CAUSE_WATCHDOG_FILE):
|
|
||||||
major_cause = self.REBOOT_CAUSE_WATCHDOG
|
|
||||||
else:
|
|
||||||
major_cause = self.REBOOT_CAUSE_HARDWARE_OTHER
|
|
||||||
if self._verify_reboot_cause(REBOOT_CAUSE_MLNX_FIRMWARE_RESET):
|
|
||||||
minor_cause = "Reset by ASIC firmware"
|
|
||||||
else:
|
|
||||||
major_cause = self.REBOOT_CAUSE_NON_HARDWARE
|
|
||||||
|
|
||||||
return major_cause, minor_cause
|
for reset_file, reset_cause in self.reboot_major_cause_dict.iteritems():
|
||||||
|
if self._verify_reboot_cause(reset_file):
|
||||||
|
return reset_cause, ''
|
||||||
|
|
||||||
|
for reset_file, reset_cause in self.reboot_minor_cause_dict.iteritems():
|
||||||
|
if self._verify_reboot_cause(reset_file):
|
||||||
|
return self.REBOOT_CAUSE_HARDWARE_OTHER, reset_cause
|
||||||
|
|
||||||
|
return self.REBOOT_CAUSE_NON_HARDWARE, ''
|
||||||
|
|
||||||
def _get_cpld_version(self, version_file):
|
def _get_cpld_version(self, version_file):
|
||||||
cpld_version = self._read_generic_file(join(CPLD_VERSION_ROOT, version_file), CPLD_VERSION_MAX_LENGTH)
|
cpld_version = self._read_generic_file(join(CPLD_VERSION_ROOT, version_file), CPLD_VERSION_MAX_LENGTH)
|
||||||
@ -383,6 +488,14 @@ class Chassis(ChassisBase):
|
|||||||
indicates that fan 0 has been removed, fan 2
|
indicates that fan 0 has been removed, fan 2
|
||||||
has been inserted and sfp 11 has been removed.
|
has been inserted and sfp 11 has been removed.
|
||||||
"""
|
"""
|
||||||
|
# Initialize SFP event first
|
||||||
|
if not self.sfp_event_initialized:
|
||||||
|
from sonic_platform.sfp_event import sfp_event
|
||||||
|
self.sfp_event = sfp_event()
|
||||||
|
self.sfp_event.initialize()
|
||||||
|
self.MAX_SELECT_EVENT_RETURNED = self.PORT_END
|
||||||
|
self.sfp_event_initialized = True
|
||||||
|
|
||||||
wait_for_ever = (timeout == 0)
|
wait_for_ever = (timeout == 0)
|
||||||
port_dict = {}
|
port_dict = {}
|
||||||
if wait_for_ever:
|
if wait_for_ever:
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# Mellanox
|
||||||
|
#
|
||||||
|
# implementation of new platform api
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
from sonic_platform_base.platform_base import PlatformBase
|
||||||
|
from sonic_platform.chassis import Chassis
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
class Platform(PlatformBase):
|
||||||
|
def __init__(self):
|
||||||
|
PlatformBase.__init__(self)
|
||||||
|
if self._is_host():
|
||||||
|
self._chassis = Chassis()
|
||||||
|
else:
|
||||||
|
self._chassis = Chassis()
|
||||||
|
self._chassis.initialize_psu()
|
||||||
|
self._chassis.initialize_fan()
|
||||||
|
self._chassis.initialize_eeprom()
|
||||||
|
self._chassis.initialize_components_list()
|
||||||
|
|
||||||
|
def _is_host(self):
|
||||||
|
"""
|
||||||
|
Test whether current process is running on the host or an docker
|
||||||
|
return True for host and False for docker
|
||||||
|
"""
|
||||||
|
is_host = False
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen("docker --version 2>/dev/null", stdout=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
stdout = proc.communicate()[0]
|
||||||
|
proc.wait()
|
||||||
|
result = stdout.rstrip('\n')
|
||||||
|
if result != '':
|
||||||
|
is_host = True
|
||||||
|
|
||||||
|
except OSError, e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return is_host
|
@ -63,6 +63,24 @@ XCVR_VENDOR_DATE_OFFSET = 84
|
|||||||
XCVR_VENDOR_DATE_WIDTH = 8
|
XCVR_VENDOR_DATE_WIDTH = 8
|
||||||
XCVR_DOM_CAPABILITY_OFFSET = 92
|
XCVR_DOM_CAPABILITY_OFFSET = 92
|
||||||
XCVR_DOM_CAPABILITY_WIDTH = 2
|
XCVR_DOM_CAPABILITY_WIDTH = 2
|
||||||
|
# to improve performance we retrieve all eeprom data via a single ethtool command
|
||||||
|
# in function get_transceiver_info and get_transceiver_bulk_status
|
||||||
|
# XCVR_INTERFACE_DATA_SIZE stands for the max size to be read
|
||||||
|
# this variable is only used by get_transceiver_info.
|
||||||
|
# please be noted that each time some new value added to the function
|
||||||
|
# we should make sure that it falls into the area
|
||||||
|
# [XCVR_INTERFACE_DATA_START, XCVR_INTERFACE_DATA_SIZE] or
|
||||||
|
# adjust XCVR_INTERFACE_MAX_SIZE to contain the new data
|
||||||
|
# It's same for [QSFP_DOM_BULK_DATA_START, QSFP_DOM_BULK_DATA_SIZE] and
|
||||||
|
# [SFP_DOM_BULK_DATA_START, SFP_DOM_BULK_DATA_SIZE] which are used by
|
||||||
|
# get_transceiver_bulk_status
|
||||||
|
XCVR_INTERFACE_DATA_START = 0
|
||||||
|
XCVR_INTERFACE_DATA_SIZE = 92
|
||||||
|
|
||||||
|
QSFP_DOM_BULK_DATA_START = 22
|
||||||
|
QSFP_DOM_BULK_DATA_SIZE = 36
|
||||||
|
SFP_DOM_BULK_DATA_START = 96
|
||||||
|
SFP_DOM_BULK_DATA_SIZE = 10
|
||||||
|
|
||||||
# definitions of the offset for values in OSFP info eeprom
|
# definitions of the offset for values in OSFP info eeprom
|
||||||
OSFP_TYPE_OFFSET = 0
|
OSFP_TYPE_OFFSET = 0
|
||||||
@ -229,7 +247,7 @@ class SFP(SfpBase):
|
|||||||
bool: True if device is present, False if not
|
bool: True if device is present, False if not
|
||||||
"""
|
"""
|
||||||
presence = False
|
presence = False
|
||||||
ethtool_cmd = "ethtool -m sfp{} 2>/dev/null".format(self.index)
|
ethtool_cmd = "ethtool -m sfp{} hex on offset 0 length 4 2>/dev/null".format(self.index)
|
||||||
try:
|
try:
|
||||||
proc = subprocess.Popen(ethtool_cmd, stdout=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT)
|
proc = subprocess.Popen(ethtool_cmd, stdout=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT)
|
||||||
stdout = proc.communicate()[0]
|
stdout = proc.communicate()[0]
|
||||||
@ -261,6 +279,15 @@ class SFP(SfpBase):
|
|||||||
return eeprom_raw
|
return eeprom_raw
|
||||||
|
|
||||||
def _dom_capability_detect(self):
|
def _dom_capability_detect(self):
|
||||||
|
if not self.get_presence():
|
||||||
|
self.dom_supported = False
|
||||||
|
self.dom_temp_supported = False
|
||||||
|
self.dom_volt_supported = False
|
||||||
|
self.dom_rx_power_supported = False
|
||||||
|
self.dom_tx_power_supported = False
|
||||||
|
self.calibration = 0
|
||||||
|
return
|
||||||
|
|
||||||
if self.sfp_type == "QSFP":
|
if self.sfp_type == "QSFP":
|
||||||
self.calibration = 1
|
self.calibration = 1
|
||||||
sfpi_obj = sff8436InterfaceId()
|
sfpi_obj = sff8436InterfaceId()
|
||||||
@ -466,47 +493,37 @@ class SFP(SfpBase):
|
|||||||
print("Error: sfp_object open failed")
|
print("Error: sfp_object open failed")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sfp_interface_bulk_raw = self._read_eeprom_specific_bytes((offset + XCVR_INTFACE_BULK_OFFSET), interface_info_bulk_width)
|
sfp_interface_bulk_raw = self._read_eeprom_specific_bytes(offset + XCVR_INTERFACE_DATA_START, XCVR_INTERFACE_DATA_SIZE)
|
||||||
if sfp_interface_bulk_raw is not None:
|
if sfp_interface_bulk_raw is None:
|
||||||
sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk(sfp_interface_bulk_raw, 0)
|
|
||||||
else:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sfp_vendor_name_raw = self._read_eeprom_specific_bytes((offset + XCVR_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH)
|
start = XCVR_INTFACE_BULK_OFFSET - XCVR_INTERFACE_DATA_START
|
||||||
if sfp_vendor_name_raw is not None:
|
end = start + interface_info_bulk_width
|
||||||
sfp_vendor_name_data = sfpi_obj.parse_vendor_name(sfp_vendor_name_raw, 0)
|
sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk(sfp_interface_bulk_raw[start : end], 0)
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
sfp_vendor_pn_raw = self._read_eeprom_specific_bytes((offset + XCVR_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH)
|
start = XCVR_VENDOR_NAME_OFFSET - XCVR_INTERFACE_DATA_START
|
||||||
if sfp_vendor_pn_raw is not None:
|
end = start + XCVR_VENDOR_NAME_WIDTH
|
||||||
sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn(sfp_vendor_pn_raw, 0)
|
sfp_vendor_name_data = sfpi_obj.parse_vendor_name(sfp_interface_bulk_raw[start : end], 0)
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
sfp_vendor_rev_raw = self._read_eeprom_specific_bytes((offset + XCVR_HW_REV_OFFSET), vendor_rev_width)
|
start = XCVR_VENDOR_PN_OFFSET - XCVR_INTERFACE_DATA_START
|
||||||
if sfp_vendor_rev_raw is not None:
|
end = start + XCVR_VENDOR_PN_WIDTH
|
||||||
sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev(sfp_vendor_rev_raw, 0)
|
sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn(sfp_interface_bulk_raw[start : end], 0)
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
sfp_vendor_sn_raw = self._read_eeprom_specific_bytes((offset + XCVR_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH)
|
start = XCVR_HW_REV_OFFSET - XCVR_INTERFACE_DATA_START
|
||||||
if sfp_vendor_sn_raw is not None:
|
end = start + vendor_rev_width
|
||||||
sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn(sfp_vendor_sn_raw, 0)
|
sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev(sfp_interface_bulk_raw[start : end], 0)
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
sfp_vendor_oui_raw = self._read_eeprom_specific_bytes((offset + XCVR_VENDOR_OUI_OFFSET), XCVR_VENDOR_OUI_WIDTH)
|
start = XCVR_VENDOR_SN_OFFSET - XCVR_INTERFACE_DATA_START
|
||||||
if sfp_vendor_oui_raw is not None:
|
end = start + XCVR_VENDOR_SN_WIDTH
|
||||||
sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui(sfp_vendor_oui_raw, 0)
|
sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn(sfp_interface_bulk_raw[start : end], 0)
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
sfp_vendor_date_raw = self._read_eeprom_specific_bytes((offset + XCVR_VENDOR_DATE_OFFSET), XCVR_VENDOR_DATE_WIDTH)
|
start = XCVR_VENDOR_OUI_OFFSET - XCVR_INTERFACE_DATA_START
|
||||||
if sfp_vendor_date_raw is not None:
|
end = start + XCVR_VENDOR_OUI_WIDTH
|
||||||
sfp_vendor_date_data = sfpi_obj.parse_vendor_date(sfp_vendor_date_raw, 0)
|
sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui(sfp_interface_bulk_raw[start : end], 0)
|
||||||
else:
|
|
||||||
return None
|
start = XCVR_VENDOR_DATE_OFFSET - XCVR_INTERFACE_DATA_START
|
||||||
|
end = start + XCVR_VENDOR_DATE_WIDTH
|
||||||
|
sfp_vendor_date_data = sfpi_obj.parse_vendor_date(sfp_interface_bulk_raw[start : end], 0)
|
||||||
|
|
||||||
transceiver_info_dict['type'] = sfp_interface_bulk_data['data']['type']['value']
|
transceiver_info_dict['type'] = sfp_interface_bulk_data['data']['type']['value']
|
||||||
transceiver_info_dict['manufacturename'] = sfp_vendor_name_data['data']['Vendor Name']['value']
|
transceiver_info_dict['manufacturename'] = sfp_vendor_name_data['data']['Vendor Name']['value']
|
||||||
@ -578,84 +595,63 @@ class SFP(SfpBase):
|
|||||||
"""
|
"""
|
||||||
transceiver_dom_info_dict = {}
|
transceiver_dom_info_dict = {}
|
||||||
|
|
||||||
|
dom_info_dict_keys = ['temperature', 'voltage',
|
||||||
|
'rx1power', 'rx2power',
|
||||||
|
'rx3power', 'rx4power',
|
||||||
|
'tx1bias', 'tx2bias',
|
||||||
|
'tx3bias', 'tx4bias',
|
||||||
|
'tx1power', 'tx2power',
|
||||||
|
'tx3power', 'tx4power'
|
||||||
|
]
|
||||||
|
transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A')
|
||||||
|
|
||||||
if self.sfp_type == OSFP_TYPE:
|
if self.sfp_type == OSFP_TYPE:
|
||||||
transceiver_dom_info_dict['temperature'] = 'N/A'
|
pass
|
||||||
transceiver_dom_info_dict['voltage'] = 'N/A'
|
|
||||||
transceiver_dom_info_dict['rx1power'] = 'N/A'
|
|
||||||
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'] = 'N/A'
|
|
||||||
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'] = 'N/A'
|
|
||||||
transceiver_dom_info_dict['tx2power'] = 'N/A'
|
|
||||||
transceiver_dom_info_dict['tx3power'] = 'N/A'
|
|
||||||
transceiver_dom_info_dict['tx4power'] = 'N/A'
|
|
||||||
|
|
||||||
elif self.sfp_type == QSFP_TYPE:
|
elif self.sfp_type == QSFP_TYPE:
|
||||||
if not self.dom_supported:
|
if not self.dom_supported:
|
||||||
return None
|
return transceiver_dom_info_dict
|
||||||
|
|
||||||
offset = 0
|
offset = 0
|
||||||
sfpd_obj = sff8436Dom()
|
sfpd_obj = sff8436Dom()
|
||||||
if sfpd_obj is None:
|
if sfpd_obj is None:
|
||||||
return None
|
return transceiver_dom_info_dict
|
||||||
|
|
||||||
|
dom_data_raw = self._read_eeprom_specific_bytes((offset + QSFP_DOM_BULK_DATA_START), QSFP_DOM_BULK_DATA_SIZE)
|
||||||
|
if dom_data_raw is None:
|
||||||
|
return transceiver_dom_info_dict
|
||||||
|
|
||||||
if self.dom_temp_supported:
|
if self.dom_temp_supported:
|
||||||
dom_temperature_raw = self._read_eeprom_specific_bytes((offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH)
|
start = QSFP_TEMPE_OFFSET - QSFP_DOM_BULK_DATA_START
|
||||||
if dom_temperature_raw is not None:
|
end = start + QSFP_TEMPE_WIDTH
|
||||||
dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0)
|
dom_temperature_data = sfpd_obj.parse_temperature(dom_data_raw[start : end], 0)
|
||||||
temp = self._convert_string_to_num(dom_temperature_data['data']['Temperature']['value'])
|
temp = self._convert_string_to_num(dom_temperature_data['data']['Temperature']['value'])
|
||||||
if temp is not None:
|
if temp is not None:
|
||||||
transceiver_dom_info_dict['temperature'] = temp
|
transceiver_dom_info_dict['temperature'] = temp
|
||||||
else:
|
|
||||||
transceiver_dom_info_dict['temperature'] = 'N/A'
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
transceiver_dom_info_dict['temperature'] = 'N/A'
|
|
||||||
|
|
||||||
if self.dom_volt_supported:
|
if self.dom_volt_supported:
|
||||||
dom_voltage_raw = self._read_eeprom_specific_bytes((offset + QSFP_VOLT_OFFSET), QSFP_VOLT_WIDTH)
|
start = QSFP_VOLT_OFFSET - QSFP_DOM_BULK_DATA_START
|
||||||
if dom_voltage_raw is not None:
|
end = start + QSFP_VOLT_WIDTH
|
||||||
dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0)
|
dom_voltage_data = sfpd_obj.parse_voltage(dom_data_raw[start : end], 0)
|
||||||
volt = self._convert_string_to_num(dom_voltage_data['data']['Vcc']['value'])
|
volt = self._convert_string_to_num(dom_voltage_data['data']['Vcc']['value'])
|
||||||
if volt is not None:
|
if volt is not None:
|
||||||
transceiver_dom_info_dict['voltage'] = volt
|
transceiver_dom_info_dict['voltage'] = volt
|
||||||
else:
|
|
||||||
transceiver_dom_info_dict['voltage'] = 'N/A'
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
transceiver_dom_info_dict['voltage'] = 'N/A'
|
|
||||||
|
|
||||||
dom_channel_monitor_raw = self._read_eeprom_specific_bytes((offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH)
|
start = QSFP_CHANNL_MON_OFFSET - QSFP_DOM_BULK_DATA_START
|
||||||
if dom_channel_monitor_raw is not None:
|
end = start + QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH
|
||||||
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_channel_monitor_raw, 0)
|
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_data_raw[start : end], 0)
|
||||||
|
|
||||||
if self.dom_tx_power_supported:
|
if self.dom_tx_power_supported:
|
||||||
transceiver_dom_info_dict['tx1power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TX1Power']['value'])
|
transceiver_dom_info_dict['tx1power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TX1Power']['value'])
|
||||||
transceiver_dom_info_dict['tx2power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TX2Power']['value'])
|
transceiver_dom_info_dict['tx2power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TX2Power']['value'])
|
||||||
transceiver_dom_info_dict['tx3power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TX3Power']['value'])
|
transceiver_dom_info_dict['tx3power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TX3Power']['value'])
|
||||||
transceiver_dom_info_dict['tx4power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TX4Power']['value'])
|
transceiver_dom_info_dict['tx4power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TX4Power']['value'])
|
||||||
else:
|
|
||||||
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'
|
|
||||||
|
|
||||||
if self.dom_rx_power_supported:
|
if self.dom_rx_power_supported:
|
||||||
transceiver_dom_info_dict['rx1power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RX1Power']['value'])
|
transceiver_dom_info_dict['rx1power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RX1Power']['value'])
|
||||||
transceiver_dom_info_dict['rx2power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RX2Power']['value'])
|
transceiver_dom_info_dict['rx2power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RX2Power']['value'])
|
||||||
transceiver_dom_info_dict['rx3power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RX3Power']['value'])
|
transceiver_dom_info_dict['rx3power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RX3Power']['value'])
|
||||||
transceiver_dom_info_dict['rx4power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RX4Power']['value'])
|
transceiver_dom_info_dict['rx4power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RX4Power']['value'])
|
||||||
else:
|
|
||||||
transceiver_dom_info_dict['rx1power'] = 'N/A'
|
|
||||||
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']['TX1Bias']['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['tx2bias'] = dom_channel_monitor_data['data']['TX2Bias']['value']
|
||||||
@ -664,46 +660,33 @@ class SFP(SfpBase):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
if not self.dom_supported:
|
if not self.dom_supported:
|
||||||
return None
|
return transceiver_dom_info_dict
|
||||||
|
|
||||||
offset = 256
|
offset = 256
|
||||||
sfpd_obj = sff8472Dom()
|
sfpd_obj = sff8472Dom()
|
||||||
if sfpd_obj is None:
|
if sfpd_obj is None:
|
||||||
return None
|
return transceiver_dom_info_dict
|
||||||
sfpd_obj._calibration_type = self.calibration
|
sfpd_obj._calibration_type = self.calibration
|
||||||
|
|
||||||
dom_temperature_raw = self._read_eeprom_specific_bytes((offset + SFP_TEMPE_OFFSET), SFP_TEMPE_WIDTH)
|
dom_data_raw = self._read_eeprom_specific_bytes((offset + SFP_DOM_BULK_DATA_START), SFP_DOM_BULK_DATA_SIZE)
|
||||||
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((offset + SFP_VOLT_OFFSET), SFP_VOLT_WIDTH)
|
start = SFP_TEMPE_OFFSET - SFP_DOM_BULK_DATA_START
|
||||||
if dom_voltage_raw is not None:
|
end = start + SFP_TEMPE_WIDTH
|
||||||
dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0)
|
dom_temperature_data = sfpd_obj.parse_temperature(dom_data_raw[start: end], 0)
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
dom_channel_monitor_raw = self._read_eeprom_specific_bytes((offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH)
|
start = SFP_VOLT_OFFSET - SFP_DOM_BULK_DATA_START
|
||||||
if dom_channel_monitor_raw is not None:
|
end = start + SFP_VOLT_WIDTH
|
||||||
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0)
|
dom_voltage_data = sfpd_obj.parse_voltage(dom_data_raw[start: end], 0)
|
||||||
else:
|
|
||||||
return None
|
start = SFP_CHANNL_MON_OFFSET - SFP_DOM_BULK_DATA_START
|
||||||
|
end = start + SFP_CHANNL_MON_WIDTH
|
||||||
|
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_data_raw[start: end], 0)
|
||||||
|
|
||||||
transceiver_dom_info_dict['temperature'] = self._convert_string_to_num(dom_temperature_data['data']['Temperature']['value'])
|
transceiver_dom_info_dict['temperature'] = self._convert_string_to_num(dom_temperature_data['data']['Temperature']['value'])
|
||||||
transceiver_dom_info_dict['voltage'] = self._convert_string_to_num(dom_voltage_data['data']['Vcc']['value'])
|
transceiver_dom_info_dict['voltage'] = self._convert_string_to_num(dom_voltage_data['data']['Vcc']['value'])
|
||||||
transceiver_dom_info_dict['rx1power'] = self._convert_string_to_num(dom_channel_monitor_data['data']['RXPower']['value'])
|
transceiver_dom_info_dict['rx1power'] = self._convert_string_to_num(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'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TXBias']['value'])
|
transceiver_dom_info_dict['tx1bias'] = self._convert_string_to_num(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'] = self._convert_string_to_num(dom_channel_monitor_data['data']['TXPower']['value'])
|
transceiver_dom_info_dict['tx1power'] = self._convert_string_to_num(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
|
return transceiver_dom_info_dict
|
||||||
|
|
||||||
@ -1036,7 +1019,7 @@ class SFP(SfpBase):
|
|||||||
sfpd_obj = sff8472Dom()
|
sfpd_obj = sff8472Dom()
|
||||||
if sfpd_obj is None:
|
if sfpd_obj is None:
|
||||||
return None
|
return None
|
||||||
sfpd_obj._calibration_type = 1
|
sfpd_obj._calibration_type = self.calibration
|
||||||
|
|
||||||
if self.dom_supported:
|
if self.dom_supported:
|
||||||
dom_channel_monitor_raw = self._read_eeprom_specific_bytes((offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH)
|
dom_channel_monitor_raw = self._read_eeprom_specific_bytes((offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH)
|
||||||
@ -1145,7 +1128,7 @@ class SFP(SfpBase):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if self.dom_supported:
|
if self.dom_supported:
|
||||||
sfpd_obj._calibration_type = 1
|
sfpd_obj._calibration_type = self.calibration
|
||||||
|
|
||||||
dom_channel_monitor_raw = self._read_eeprom_specific_bytes((offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH)
|
dom_channel_monitor_raw = self._read_eeprom_specific_bytes((offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH)
|
||||||
if dom_channel_monitor_raw is not None:
|
if dom_channel_monitor_raw is not None:
|
||||||
|
Reference in New Issue
Block a user