Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mellanox] optimize new platform api #3289

Merged
merged 14 commits into from
Aug 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__all__ = ["platform", "chassis"]
from sonic_platform import *
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • [](start = 27, length = 1)

Don't import wildcard in production code.

199 changes: 156 additions & 43 deletions platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,7 @@

try:
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 eeprom import Eeprom
from sfp_event import sfp_event
from os import listdir
from os.path import isfile, join
import sys
Expand All @@ -43,11 +35,6 @@
#reboot cause related definitions
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

#version retrieving related definitions
Expand Down Expand Up @@ -84,14 +71,30 @@ def __init__(self):
# Initialize 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
self.psu_module = Psu
for index in range(MLNX_NUM_PSU):
psu = Psu(index, self.sku_name)
self._psu_list.append(psu)

# Initialize watchdog
self._watchdog = get_watchdog()

def initialize_fan(self):
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
multi_rotor_in_drawer = False
num_of_fan, num_of_drawer = self._extract_num_of_fans_and_fan_drawers()
Expand All @@ -104,6 +107,11 @@ def __init__(self):
fan = Fan(index, index)
self._fan_list.append(fan)

def initialize_sfp(self):
from sonic_platform.sfp import SFP

self.sfp_module = SFP

# Initialize SFP list
port_position_tuple = self._get_port_position_tuple_by_sku_name()
self.PORT_START = port_position_tuple[0]
Expand All @@ -118,31 +126,82 @@ def __init__(self):
sfp_module = SFP(index, 'SFP')
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(self.sku_name, self._thermal_list, self._psu_list)

def initialize_eeprom(self):
from eeprom import Eeprom
# Initialize EEPROM
self.eeprom = Eeprom()
self._eeprom = Eeprom()

def initialize_components_list(self):
# Initialize component list
self._component_name_list.append(COMPONENT_BIOS)
self._component_name_list.append(COMPONENT_FIRMWARE)
self._component_name_list.append(COMPONENT_CPLD1)
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

Returns:
An integer, the number of sfps available on this chassis
"""
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))

def _init_sfp_change_event(self):
self.sfp_event = sfp_event()
self.sfp_event.initialize()
self.MAX_SELECT_EVENT_RETURNED = self.PORT_END
return sfp

def _extract_num_of_fans_and_fan_drawers(self):
num_of_fan = 0
num_of_drawer = 0
for f in listdir(FAN_PATH):
if isfile(join(FAN_PATH, f)):
for f in listdir(self.fan_path):
if isfile(join(self.fan_path, f)):
match_obj = re.match('fan(\d+)_speed_get', f)
if match_obj != None:
if int(match_obj.group(1)) > num_of_fan:
Expand All @@ -163,6 +222,30 @@ def _get_port_position_tuple_by_sku_name(self):
position_tuple = port_position_tuple_list[hwsku_dict_port[self.sku_name]]
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):
"""
Retrieves the base MAC address for the chassis
Expand All @@ -171,7 +254,7 @@ def get_base_mac(self):
A string containing the MAC address in the format
'XX:XX:XX:XX:XX:XX'
"""
return self.eeprom.get_base_mac()
return self._eeprom.get_base_mac()

def get_serial_number(self):
"""
Expand All @@ -180,7 +263,7 @@ def get_serial_number(self):
Returns:
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):
"""
Expand All @@ -191,7 +274,7 @@ def get_system_eeprom_info(self):
OCP ONIE TlvInfo EEPROM format and values are their corresponding
values.
"""
return self.eeprom.get_system_eeprom_info()
return self._eeprom.get_system_eeprom_info()

def _read_generic_file(self, filename, len):
"""
Expand All @@ -205,7 +288,7 @@ def _read_generic_file(self, filename, len):
return result
except Exception as e:
logger.log_info("Fail to read file {} due to {}".format(filename, repr(e)))
return ''
return '0'

def _verify_reboot_cause(self, filename):
'''
Expand All @@ -215,6 +298,31 @@ def _verify_reboot_cause(self, filename):
'''
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):
"""
Retrieves the cause of the previous reboot
Expand All @@ -227,21 +335,18 @@ def get_reboot_cause(self):
to pass a description of the reboot cause.
"""
#read reboot causes files in the following order
minor_cause = ''
if self._verify_reboot_cause(REBOOT_CAUSE_POWER_LOSS_FILE):
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
if not self.reboot_cause_initialized:
self.initialize_reboot_cause()

for reset_file, reset_cause in self.reboot_major_cause_dict.iteritems():
if self._verify_reboot_cause(reset_file):
return reset_cause, ''

return major_cause, minor_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):
cpld_version = self._read_generic_file(join(CPLD_VERSION_ROOT, version_file), CPLD_VERSION_MAX_LENGTH)
Expand Down Expand Up @@ -383,6 +488,14 @@ def get_change_event(self, timeout=0):
indicates that fan 0 has been removed, fan 2
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)
port_dict = {}
if wait_for_ever:
Expand Down
45 changes: 45 additions & 0 deletions platform/mellanox/mlnx-platform-api/sonic_platform/platform.py
Original file line number Diff line number Diff line change
@@ -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
Loading