[Mellanox] get LED capability from capability file (#14584)

- Why I did it
Currently, LED sysfs path is hardcoded. We will need change LED code if new LED color is supported for new platforms. This PR is aimed to improve this. By this PR, LED sysfs path is deduced from LED capability file.

- How I did it
Improve LED management on Nvidia platform:
get LED capability from capability file and deduce sysfs name according to the capability

- How to verify it
Unit test
Manual test
This commit is contained in:
Junchao-Mellanox 2023-05-11 01:53:50 +08:00 committed by GitHub
parent fa02411750
commit 9deca05f9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 165 additions and 148 deletions

View File

@ -184,6 +184,12 @@ class DeviceDataManager:
from sonic_py_common import device_info
return device_info.get_platform()
@classmethod
@utils.read_only_cache()
def is_simx_platform(cls):
platform_name = cls.get_platform_name()
return platform_name and 'simx' in platform_name
@classmethod
@utils.read_only_cache()
def get_fan_drawer_count(cls):

View File

@ -19,105 +19,130 @@ import time
from sonic_py_common.logger import Logger
from . import utils
from . import device_data
logger = Logger()
class Led(object):
STATUS_LED_COLOR_GREEN = 'green'
STATUS_LED_COLOR_GREEN_BLINK = 'green_blink'
STATUS_LED_COLOR_RED = 'red'
STATUS_LED_COLOR_RED_BLINK = 'red_blink'
STATUS_LED_COLOR_ORANGE = 'orange'
STATUS_LED_COLOR_ORANGE_BLINK = 'orange_blink'
STATUS_LED_COLOR_OFF = 'off'
STATUS_LED_COLOR_GREEN_BLINK = 'green_blink'
STATUS_LED_COLOR_RED_BLINK = 'red_blink'
STATUS_LED_COLOR_ORANGE_BLINK = 'orange_blink'
LED_ON = '255'
LED_OFF = '0'
LED_BLINK = '50'
SIMILAR_COLORS = {
'red': ('amber', 'orange'),
'amber': ('red', 'orange'),
'orange': ('red', 'amber')
}
PRIMARY_COLORS = {
'red': 'red',
'amber': 'red',
'orange': 'red',
'green': 'green'
}
LED_PATH = "/var/run/hw-management/led/"
def __init__(self):
self.supported_colors = set()
self.supported_blinks = set()
def _get_actual_color(self, color):
# Different platform has different LED capability, this capability should be
# transparent for upper layer. So, here is the logic to help find actual color
# if the given color is not supported.
if color not in self.supported_colors:
return self._get_similar_color(color)
return color
def _get_similar_color(self, color):
# If a given color is not supported, we try to find a similar color from
# canditates
similar_colors = self.SIMILAR_COLORS.get(color)
if similar_colors:
for actual_color in similar_colors:
if actual_color in self.supported_colors:
return actual_color
return None
def _get_primary_color(self, color):
# For backward compatible, we don't return the actual color here.
# We always return "green"(indicate a good status) or "red"(indicate a bad status)
# which are the "primary" colors.
return self.PRIMARY_COLORS.get(color, color)
def _get_actual_blink_color(self, blink_color):
# Different platform has different LED capability, this capability should be
# transparent for upper layer. So, here is the logic to help find actual blink color
# if the given blink color is not supported.
if blink_color not in self.supported_blinks:
return self._get_similar_blink_color(blink_color)
return blink_color
def _get_similar_blink_color(self, color):
# If a given blink color is not supported, we try to find a similar blink color from
# canditates
similar_colors = self.SIMILAR_COLORS.get(color)
if similar_colors:
for actual_color in similar_colors:
if actual_color in self.supported_blinks:
return actual_color
return None
def set_status(self, color):
led_cap_list = self.get_capability()
if led_cap_list is None:
self.get_capability()
if not self.supported_colors:
if not device_data.DeviceDataManager.is_simx_platform():
logger.log_error(f'Failed to get LED capability for {self._led_id} LED')
return False
status = False
try:
self._stop_blink(led_cap_list)
blink_pos = color.find('blink')
self._stop_blink()
blink_pos = color.find('_blink')
if blink_pos != -1:
return self._set_status_blink(color, led_cap_list)
return self._set_status_blink(color[0:blink_pos])
if color == Led.STATUS_LED_COLOR_GREEN:
utils.write_file(self.get_green_led_path(), Led.LED_ON)
status = True
elif color == Led.STATUS_LED_COLOR_RED:
# Some led don't support red led but support orange led, in this case we set led to orange
if Led.STATUS_LED_COLOR_RED in led_cap_list:
led_path = self.get_red_led_path()
elif Led.STATUS_LED_COLOR_ORANGE in led_cap_list:
led_path = self.get_orange_led_path()
else:
if color != Led.STATUS_LED_COLOR_OFF:
actual_color = self._get_actual_color(color)
if not actual_color:
logger.log_error(f'Set LED to color {color} is not supported')
return False
utils.write_file(led_path, Led.LED_ON)
status = True
elif color == Led.STATUS_LED_COLOR_ORANGE:
if Led.STATUS_LED_COLOR_ORANGE in led_cap_list:
led_path = self.get_orange_led_path()
elif Led.STATUS_LED_COLOR_RED in led_cap_list:
led_path = self.get_red_led_path()
else:
return False
utils.write_file(led_path, Led.LED_ON)
status = True
elif color == Led.STATUS_LED_COLOR_OFF:
if Led.STATUS_LED_COLOR_GREEN in led_cap_list:
utils.write_file(self.get_green_led_path(), Led.LED_OFF)
if Led.STATUS_LED_COLOR_RED in led_cap_list:
utils.write_file(self.get_red_led_path(), Led.LED_OFF)
if Led.STATUS_LED_COLOR_ORANGE in led_cap_list:
utils.write_file(self.get_orange_led_path(), Led.LED_OFF)
utils.write_file(self.get_led_path(actual_color), Led.LED_ON)
status = True
else:
status = False
for color in self.supported_colors:
utils.write_file(self.get_led_path(color), Led.LED_OFF)
status = True
except (ValueError, IOError):
status = False
return status
def _set_status_blink(self, color, led_cap_list):
if color not in led_cap_list:
if color == Led.STATUS_LED_COLOR_RED_BLINK and Led.STATUS_LED_COLOR_ORANGE_BLINK in led_cap_list:
color = Led.STATUS_LED_COLOR_ORANGE_BLINK
elif color == Led.STATUS_LED_COLOR_ORANGE_BLINK and Led.STATUS_LED_COLOR_RED_BLINK in led_cap_list:
color = Led.STATUS_LED_COLOR_RED_BLINK
else:
return False
if Led.STATUS_LED_COLOR_GREEN_BLINK == color:
self._trigger_blink(self.get_green_led_trigger())
return self._set_led_blink_status(self.get_green_led_delay_on_path(), self.get_green_led_delay_off_path(), Led.LED_BLINK)
elif Led.STATUS_LED_COLOR_RED_BLINK == color:
self._trigger_blink(self.get_red_led_trigger())
return self._set_led_blink_status(self.get_red_led_delay_on_path(), self.get_red_led_delay_off_path(), Led.LED_BLINK)
elif Led.STATUS_LED_COLOR_ORANGE_BLINK == color:
self._trigger_blink(self.get_orange_led_trigger())
return self._set_led_blink_status(self.get_orange_led_delay_on_path(), self.get_orange_led_delay_off_path(), Led.LED_BLINK)
else:
def _set_status_blink(self, color):
actual_color = self._get_actual_blink_color(color)
if not actual_color:
logger.log_error(f'Set LED to color {color}_blink is not supported')
return False
def _stop_blink(self, led_cap_list):
self._trigger_blink(self.get_led_trigger(actual_color))
return self._set_led_blink_status(actual_color)
def _stop_blink(self):
try:
if Led.STATUS_LED_COLOR_GREEN_BLINK in led_cap_list:
self._untrigger_blink(self.get_green_led_trigger())
if Led.STATUS_LED_COLOR_RED_BLINK in led_cap_list:
self._untrigger_blink(self.get_red_led_trigger())
if Led.STATUS_LED_COLOR_ORANGE_BLINK in led_cap_list:
self._untrigger_blink(self.get_orange_led_trigger())
for color in self.supported_colors:
self._untrigger_blink(self.get_led_trigger(color))
except Exception as e:
return
@ -127,12 +152,14 @@ class Led(object):
def _untrigger_blink(self, blink_trigger_file):
utils.write_file(blink_trigger_file, 'none')
def _set_led_blink_status(self, delay_on_file, delay_off_file, value):
def _set_led_blink_status(self, actual_color):
delay_on_file = self.get_led_delay_on_path(actual_color)
delay_off_file = self.get_led_delay_off_path(actual_color)
if not self._wait_files_ready((delay_on_file, delay_off_file)):
return False
utils.write_file(delay_on_file, value)
utils.write_file(delay_off_file, value)
utils.write_file(delay_on_file, Led.LED_BLINK)
utils.write_file(delay_off_file, Led.LED_BLINK)
return True
def _wait_files_ready(self, file_list):
@ -155,97 +182,76 @@ class Led(object):
return False
def get_status(self):
led_cap_list = self.get_capability()
if led_cap_list is None:
self.get_capability()
if not self.supported_colors:
if not device_data.DeviceDataManager.is_simx_platform():
logger.log_error(f'Failed to get LED capability for {self._led_id} LED')
return Led.STATUS_LED_COLOR_OFF
try:
blink_status = self._get_blink_status(led_cap_list)
blink_status = self._get_blink_status()
if blink_status is not None:
return blink_status
if utils.read_str_from_file(self.get_green_led_path()) != Led.LED_OFF:
return Led.STATUS_LED_COLOR_GREEN
actual_color = None
for color in self.supported_colors:
if utils.read_str_from_file(self.get_led_path(color)) != Led.LED_OFF:
actual_color = color
break
if Led.STATUS_LED_COLOR_RED in led_cap_list:
if utils.read_str_from_file(self.get_red_led_path()) != Led.LED_OFF:
return Led.STATUS_LED_COLOR_RED
if Led.STATUS_LED_COLOR_ORANGE in led_cap_list:
if utils.read_str_from_file(self.get_orange_led_path()) != Led.LED_OFF:
return Led.STATUS_LED_COLOR_RED
if actual_color is not None:
return self._get_primary_color(actual_color)
except (ValueError, IOError) as e:
raise RuntimeError("Failed to read led status due to {}".format(repr(e)))
return Led.STATUS_LED_COLOR_OFF
def _get_blink_status(self, led_cap_list):
def _get_blink_status(self):
try:
if Led.STATUS_LED_COLOR_GREEN_BLINK in led_cap_list:
if self._is_led_blinking(self.get_green_led_delay_on_path(), self.get_green_led_delay_off_path()):
return Led.STATUS_LED_COLOR_GREEN_BLINK
if Led.STATUS_LED_COLOR_RED_BLINK in led_cap_list:
if self._is_led_blinking(self.get_red_led_delay_on_path(), self.get_red_led_delay_off_path()):
return Led.STATUS_LED_COLOR_RED_BLINK
if Led.STATUS_LED_COLOR_ORANGE_BLINK in led_cap_list:
if self._is_led_blinking(self.get_orange_led_delay_on_path(), self.get_orange_led_delay_off_path()):
return Led.STATUS_LED_COLOR_ORANGE_BLINK
for color in self.supported_colors:
if self._is_led_blinking(color):
return f'{color}_blink'
except Exception as e:
return None
return None
def _is_led_blinking(self, delay_on_file, delay_off_file):
delay_on = utils.read_str_from_file(delay_on_file, default=Led.LED_OFF, log_func=None)
delay_off = utils.read_str_from_file(delay_off_file, default=Led.LED_OFF, log_func=None)
def _is_led_blinking(self, color):
delay_on = utils.read_str_from_file(self.get_led_delay_on_path(color), default=Led.LED_OFF, log_func=None)
delay_off = utils.read_str_from_file(self.get_led_delay_off_path(color), default=Led.LED_OFF, log_func=None)
return delay_on != Led.LED_OFF and delay_off != Led.LED_OFF
def get_capability(self):
caps = utils.read_str_from_file(self.get_led_cap_path())
return set(caps.split())
for capability in caps.split():
if capability == 'none':
continue
def get_green_led_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_green'.format(self._led_id))
pos = capability.find('_blink')
if pos != -1:
self.supported_blinks.add(capability[0:pos])
else:
self.supported_colors.add(capability)
def get_green_led_delay_off_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_green_delay_off'.format(self._led_id))
def get_led_path(self, color):
return os.path.join(Led.LED_PATH, f'led_{self._led_id}_{color}')
def get_green_led_delay_on_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_green_delay_on'.format(self._led_id))
def get_led_trigger(self, color):
return os.path.join(Led.LED_PATH, f'led_{self._led_id}_{color}_trigger')
def get_green_led_trigger(self):
return os.path.join(Led.LED_PATH, 'led_{}_green_trigger'.format(self._led_id))
def get_led_delay_off_path(self, color):
return os.path.join(Led.LED_PATH, f'led_{self._led_id}_{color}_delay_off')
def get_red_led_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_red'.format(self._led_id))
def get_red_led_delay_off_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_red_delay_off'.format(self._led_id))
def get_red_led_delay_on_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_red_delay_on'.format(self._led_id))
def get_red_led_trigger(self):
return os.path.join(Led.LED_PATH, 'led_{}_red_trigger'.format(self._led_id))
def get_orange_led_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_orange'.format(self._led_id))
def get_orange_led_delay_off_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_orange_delay_off'.format(self._led_id))
def get_orange_led_delay_on_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_orange_delay_on'.format(self._led_id))
def get_orange_led_trigger(self):
return os.path.join(Led.LED_PATH, 'led_{}_orange_trigger'.format(self._led_id))
def get_led_delay_on_path(self, color):
return os.path.join(Led.LED_PATH, f'led_{self._led_id}_{color}_delay_on')
def get_led_cap_path(self):
return os.path.join(Led.LED_PATH, 'led_{}_capability'.format(self._led_id))
return os.path.join(Led.LED_PATH, f'led_{self._led_id}_capability')
class FanLed(Led):
def __init__(self, index):
super().__init__()
if index is not None:
self._led_id = 'fan{}'.format(index)
else:
@ -254,6 +260,7 @@ class FanLed(Led):
class PsuLed(Led):
def __init__(self, index):
super().__init__()
if index is not None:
self._led_id = 'psu{}'.format(index)
else:
@ -262,10 +269,13 @@ class PsuLed(Led):
class SystemLed(Led):
def __init__(self):
super().__init__()
self._led_id = 'status'
class SharedLed(object):
# for shared LED, blink is not supported for now. Currently, only PSU and fan LED
# might be shared LED, and there is no requirement to set PSU/fan LED to blink status.
LED_PRIORITY = {
Led.STATUS_LED_COLOR_RED: 0,
Led.STATUS_LED_COLOR_GREEN: 1

View File

@ -57,45 +57,45 @@ class TestLed:
utils.write_file = mock_write_file
assert obj.get_status_led() == Led.STATUS_LED_COLOR_GREEN
mock_file_content[physical_led.get_green_led_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_path('green')] = Led.LED_OFF
assert obj.set_status_led(Led.STATUS_LED_COLOR_RED) is True
assert obj.get_status_led() == Led.STATUS_LED_COLOR_RED
mock_file_content[physical_led.get_red_led_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_path('red')] = Led.LED_OFF
assert obj.set_status_led(Led.STATUS_LED_COLOR_GREEN) is True
assert obj.get_status_led() == Led.STATUS_LED_COLOR_GREEN
mock_file_content[physical_led.get_green_led_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_path('green')] = Led.LED_OFF
assert obj.set_status_led(Led.STATUS_LED_COLOR_ORANGE) is True
assert obj.get_status_led() == Led.STATUS_LED_COLOR_RED
mock_file_content[physical_led.get_orange_led_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_path('orange')] = Led.LED_OFF
assert obj.set_status_led(Led.STATUS_LED_COLOR_RED_BLINK)
assert obj.get_status_led() == Led.STATUS_LED_COLOR_RED_BLINK
mock_file_content[physical_led.get_red_led_delay_off_path()] = Led.LED_OFF
mock_file_content[physical_led.get_red_led_delay_on_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_delay_off_path('red')] = Led.LED_OFF
mock_file_content[physical_led.get_led_delay_on_path('red')] = Led.LED_OFF
assert obj.set_status_led(Led.STATUS_LED_COLOR_GREEN_BLINK)
assert obj.get_status_led() == Led.STATUS_LED_COLOR_GREEN_BLINK
mock_file_content[physical_led.get_green_led_delay_off_path()] = Led.LED_OFF
mock_file_content[physical_led.get_green_led_delay_on_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_delay_off_path('green')] = Led.LED_OFF
mock_file_content[physical_led.get_led_delay_on_path('green')] = Led.LED_OFF
assert obj.set_status_led(Led.STATUS_LED_COLOR_ORANGE_BLINK)
assert obj.get_status_led() == Led.STATUS_LED_COLOR_RED_BLINK
mock_file_content[physical_led.get_green_led_delay_off_path()] = Led.LED_OFF
mock_file_content[physical_led.get_green_led_delay_on_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_delay_off_path('green')] = Led.LED_OFF
mock_file_content[physical_led.get_led_delay_on_path('green')] = Led.LED_OFF
def _mock_led_file_content(self, led):
return {
led.get_green_led_path(): Led.LED_ON,
led.get_red_led_path(): Led.LED_OFF,
led.get_orange_led_path(): Led.LED_OFF,
led.get_led_path('green'): Led.LED_ON,
led.get_led_path('red'): Led.LED_OFF,
led.get_led_path('orange'): Led.LED_OFF,
led.get_led_cap_path(): 'none green green_blink red red_blink orange',
led.get_green_led_delay_off_path(): Led.LED_OFF,
led.get_green_led_delay_on_path(): Led.LED_OFF,
led.get_red_led_delay_off_path(): Led.LED_OFF,
led.get_red_led_delay_on_path(): Led.LED_OFF,
led.get_orange_led_delay_off_path(): Led.LED_OFF,
led.get_orange_led_delay_on_path(): Led.LED_OFF,
led.get_led_delay_off_path('green'): Led.LED_OFF,
led.get_led_delay_on_path('green'): Led.LED_OFF,
led.get_led_delay_off_path('red'): Led.LED_OFF,
led.get_led_delay_on_path('red'): Led.LED_OFF,
led.get_led_delay_off_path('orange'): Led.LED_OFF,
led.get_led_delay_on_path('orange'): Led.LED_OFF,
}
@mock.patch('sonic_platform.led.Led._wait_files_ready', mock.MagicMock(return_value=True))
@ -124,19 +124,20 @@ class TestLed:
utils.write_file = mock_write_file
assert obj1.set_status_led(Led.STATUS_LED_COLOR_GREEN)
assert obj2.get_status_led() == Led.STATUS_LED_COLOR_GREEN
mock_file_content[physical_led.get_green_led_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_path('green')] = Led.LED_OFF
assert obj2.set_status_led(Led.STATUS_LED_COLOR_RED)
assert obj2.get_status_led() == Led.STATUS_LED_COLOR_RED
assert obj1.set_status_led(Led.STATUS_LED_COLOR_RED)
assert obj2.get_status_led() == Led.STATUS_LED_COLOR_RED
mock_file_content[physical_led.get_red_led_path()] = Led.LED_OFF
mock_file_content[physical_led.get_led_path('red')] = Led.LED_OFF
assert obj1.set_status_led(Led.STATUS_LED_COLOR_GREEN)
assert obj1.get_status_led() == Led.STATUS_LED_COLOR_RED
assert obj2.get_status_led() == Led.STATUS_LED_COLOR_RED
mock_file_content[physical_led.get_led_path('red')] = Led.LED_OFF
assert obj2.set_status_led(Led.STATUS_LED_COLOR_GREEN)
assert obj1.get_status_led() == Led.STATUS_LED_COLOR_GREEN
assert obj1.get_status_led() == Led.STATUS_LED_COLOR_GREEN
assert obj2.get_status_led() == Led.STATUS_LED_COLOR_GREEN
@mock.patch('sonic_platform.led.Led._wait_files_ready', mock.MagicMock(return_value=True))
def test_psu_led(self):