Skip to content
Closed
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
50 changes: 50 additions & 0 deletions platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,56 @@ def get_psu(self, index):
self.initialize_psu()
return super(Chassis, self).get_psu(index)

##############################################
# PDB methods
##############################################

def initialize_pdb(self):
if not self._pdb_list:
pdb_count = DeviceDataManager.get_pdb_count()
if pdb_count == 0:
return
Comment thread
yuazhe marked this conversation as resolved.
from .pdb import Pdb
for index in range(pdb_count):
self._pdb_list.append(Pdb(index))

def get_num_pdbs(self):
"""
Retrieves the number of power distribution boards available on this chassis

Returns:
An integer, the number of PDBs available on this chassis
"""
self.initialize_pdb()
return len(self._pdb_list)

def get_all_pdbs(self):
"""
Retrieves all power distribution boards available on this chassis

Returns:
A list of objects derived from PdbBase representing all PDBs
available on this chassis
"""
self.initialize_pdb()
return self._pdb_list

def get_pdb(self, index):
"""
Retrieves the PDB object at the specified (0-based) index

Args:
index: An integer, the index (0-based) of the PDB to retrieve

Returns:
An object derived from PdbBase representing the specified PDB
"""
self.initialize_pdb()
if index < 0 or index >= len(self._pdb_list):
logger.log_error(f"PDB index {index} is out of range")
return None
Comment thread
yuazhe marked this conversation as resolved.
return self._pdb_list[index]

##############################################
# Fan methods
##############################################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ def get_psu_count(cls):
def is_psu_hotswapable(cls):
return utils.read_int_from_file('/run/hw-management/config/hotplug_psus') > 0

@classmethod
@utils.read_only_cache()
def get_pdb_count(cls):
"""Return number of PDBs from /var/run/hw-management/config/hotplug_pdbs."""
return utils.read_int_from_file('/var/run/hw-management/config/hotplug_pdbs', default = 0, log_func=None)

@classmethod
@utils.read_only_cache()
def get_sfp_count(cls):
Expand Down
155 changes: 155 additions & 0 deletions platform/mellanox/mlnx-platform-api/sonic_platform/pdb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
Comment thread
yuazhe marked this conversation as resolved.
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#############################################################################
# Mellanox
#
# Module contains an implementation of SONiC Platform Base API and
# provides the PDBs status which are available in the platform
#
#############################################################################

import os
from sonic_platform_base.pdb_base import PdbBase
from sonic_py_common.logger import Logger
from . import utils
from .thermal import Thermal

logger = Logger()
PDB_PATH = '/var/run/hw-management'
LED_POWER_STATE_FILE = os.path.join(PDB_PATH, 'led', 'led_power')
DEFAULT_TEMP_SCALE = 1000
LED_DISPLAY_NA = 'N/A'


class Pdb(PdbBase):
"""Mellanox PDB; sysfs under /var/run/hw-management (power, thermal, environment)."""

def __init__(self, pdb_index):
super(Pdb, self).__init__()
self.index = pdb_index + 1
self._name = "PDB {}".format(self.index)

# Status: system/pdb{N}_pwr_status
self._pwr_status = os.path.join(PDB_PATH, 'system', 'pdb{}_pwr_status'.format(self.index))

# Temperature: thermal/pdb_hotswap{N}_temp1_input, _temp1_max, _temp1_crit
self._thermal_list = []
temp_path = os.path.join(PDB_PATH, 'thermal', 'pdb_hotswap{}_temp1_input'.format(self.index))
high_path = os.path.join(PDB_PATH, 'thermal', 'pdb_hotswap{}_temp1_max'.format(self.index))
crit_path = os.path.join(PDB_PATH, 'thermal', 'pdb_hotswap{}_temp1_crit'.format(self.index))
if os.path.exists(temp_path):
Comment thread
yuazhe marked this conversation as resolved.
self._thermal_list.append(Thermal(
'PDB-{} Temp'.format(self.index), temp_path,
high_path if os.path.exists(high_path) else None,
crit_path if os.path.exists(crit_path) else None,
None, None, DEFAULT_TEMP_SCALE, 1))
else:
logger.log_error(f"PDB {self.index} temperature file {temp_path} does not exist")

# Environment: pdb_hotswap{N}_* for in/out voltage, curr1, power1, power1_max; pwr_conv{N}_* for curr2, power2
env_dir = os.path.join(PDB_PATH, 'environment')
self._in_voltage = os.path.join(env_dir, 'pdb_pwr_conv{}_in1_input'.format(self.index))
self._in_current = os.path.join(env_dir, 'pdb_pwr_conv{}_curr1_input'.format(self.index))
self._in_power = os.path.join(env_dir, 'pdb_pwr_conv{}_power1_input'.format(self.index))
self._power_max = os.path.join(env_dir, 'pdb_hotswap{}_power1_max'.format(self.index))
self._out_voltage = os.path.join(env_dir, 'pdb_pwr_conv{}_in2_input'.format(self.index))
self._out_current = os.path.join(env_dir, 'pdb_pwr_conv{}_curr2_input'.format(self.index))
self._out_power = os.path.join(env_dir, 'pdb_pwr_conv{}_power2_input'.format(self.index))

def get_name(self):
return self._name

def get_presence(self):
return True

def get_status(self):
if not self.get_presence():
return False
return utils.read_int_from_file(self._pwr_status, log_func=None) == 1

def get_powergood_status(self):
return self.get_status()

def get_model(self):
return 'N/A'

def get_serial(self):
return 'N/A'

def get_revision(self):
return 'N/A'

def is_replaceable(self):
return False
Comment thread
yuazhe marked this conversation as resolved.

def get_status_led(self):
"""
Power LED aggregate state from hw-management (matches ``cat .../led/led_power``).
Returns ``N/A`` when the file is missing/unreadable or content is empty/none.
"""
text = utils.read_str_from_file(LED_POWER_STATE_FILE, default='', log_func=None).strip()
if not text or text.lower() == 'none':
return LED_DISPLAY_NA
return text

def get_temperature(self):
if self._thermal_list:
return self._thermal_list[0].get_temperature()
return None

def get_output_current(self):
if os.path.exists(self._out_current):
val = utils.read_int_from_file(self._out_current, log_func=logger.log_info)
return float(val) / 1000 if val is not None else None
return None

def get_output_power(self):
if os.path.exists(self._out_power):
val = utils.read_int_from_file(self._out_power, log_func=logger.log_info)
return float(val) / 1000000 if val is not None else None
return None

def get_output_voltage(self):
if os.path.exists(self._out_voltage):
val = utils.read_int_from_file(self._out_voltage, log_func=logger.log_info)
return float(val) / 1000 if val is not None else None
return None

def get_input_current(self):
if os.path.exists(self._in_current):
val = utils.read_int_from_file(self._in_current, log_func=logger.log_info)
return float(val) / 1000 if val is not None else None
return None

def get_input_power(self):
if os.path.exists(self._in_power):
val = utils.read_int_from_file(self._in_power, log_func=logger.log_info)
return float(val) / 1000000 if val is not None else None
return None

def get_input_voltage(self):
if os.path.exists(self._in_voltage):
val = utils.read_int_from_file(self._in_voltage, log_func=logger.log_info)
return float(val) / 1000 if val is not None else None
return None

def get_maximum_supplied_power(self):
if os.path.exists(self._power_max):
val = utils.read_int_from_file(self._power_max, log_func=logger.log_info)
return float(val) / 1000000 if val is not None else None
return None
Loading